From 926938813ab5e8f2d922ec5c43c5072453d7c781 Mon Sep 17 00:00:00 2001 From: Alexander Soklakov Date: Mon, 15 Feb 2021 13:32:47 +0400 Subject: [PATCH] Fix for Bug#20391832, SETOBJECT() FOR TYPES.TIME RESULTS IN EXCEPTION WHEN VALUE HAS FRACTIONAL PART. --- CHANGES | 2 + .../java/com/mysql/cj/QueryBindings.java | 5 +- .../com/mysql/cj/protocol/InternalTime.java | 12 +- .../java/com/mysql/cj/util/TimeUtil.java | 294 +++---- .../com/mysql/cj/AbstractQueryBindings.java | 34 +- .../mysql/cj/ClientPreparedQueryBindings.java | 67 +- .../cj/ServerPreparedQueryBindValue.java | 59 +- .../mysql/cj/ServerPreparedQueryBindings.java | 29 +- .../mysql/cj/result/DurationValueFactory.java | 74 ++ .../cj/result/LocalDateTimeValueFactory.java | 5 +- .../cj/result/LocalTimeValueFactory.java | 5 +- .../cj/result/OffsetDateTimeValueFactory.java | 5 +- .../cj/result/OffsetTimeValueFactory.java | 5 +- .../mysql/cj/result/SqlTimeValueFactory.java | 5 +- .../cj/result/SqlTimestampValueFactory.java | 5 +- .../mysql/cj/result/StringValueFactory.java | 9 +- .../cj/result/UtilCalendarValueFactory.java | 5 +- .../cj/result/ZonedDateTimeValueFactory.java | 5 +- .../mysql/cj/jdbc/result/ResultSetImpl.java | 7 + .../result/LocalDateTimeValueFactoryTest.java | 6 +- .../cj/result/LocalTimeValueFactoryTest.java | 6 +- .../cj/result/SqlTimeValueFactoryTest.java | 6 +- .../result/SqlTimestampValueFactoryTest.java | 6 +- .../ZeroDateTimeToNullValueFactoryTest.java | 6 +- .../java/com/mysql/cj/util/TimeUtilTest.java | 270 ++++++- .../regression/DateTimeRegressionTest.java | 743 ++++++++++++++++++ .../java/testsuite/simple/DateTimeTest.java | 281 ++++++- 27 files changed, 1704 insertions(+), 252 deletions(-) create mode 100644 src/main/core-impl/java/com/mysql/cj/result/DurationValueFactory.java create mode 100644 src/test/java/testsuite/regression/DateTimeRegressionTest.java diff --git a/CHANGES b/CHANGES index 79a2246bf..af8b75449 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,8 @@ Version 8.0.24 + - Fix for Bug#20391832, SETOBJECT() FOR TYPES.TIME RESULTS IN EXCEPTION WHEN VALUE HAS FRACTIONAL PART. + - Fix for Bug#97730 (31699993), xdev api: ConcurrentModificationException at Session.close. - Fix for Bug#99708 (31510398), mysql-connector-java 8.0.20 ASSERTION FAILED: Unknown message type: 57 s.close. diff --git a/src/main/core-api/java/com/mysql/cj/QueryBindings.java b/src/main/core-api/java/com/mysql/cj/QueryBindings.java index 18f8e92cd..c7800f94c 100644 --- a/src/main/core-api/java/com/mysql/cj/QueryBindings.java +++ b/src/main/core-api/java/com/mysql/cj/QueryBindings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. + * Copyright (c) 2017, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -38,6 +38,7 @@ import java.sql.NClob; import java.sql.Time; import java.sql.Timestamp; +import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -233,4 +234,6 @@ public interface QueryBindings { void setLocalDateTime(int parameterIndex, LocalDateTime x, MysqlType targetMysqlType); + void setDuration(int parameterIndex, Duration x, MysqlType targetMysqlType); + } diff --git a/src/main/core-api/java/com/mysql/cj/protocol/InternalTime.java b/src/main/core-api/java/com/mysql/cj/protocol/InternalTime.java index 8511ea618..69f9bb03c 100644 --- a/src/main/core-api/java/com/mysql/cj/protocol/InternalTime.java +++ b/src/main/core-api/java/com/mysql/cj/protocol/InternalTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -29,6 +29,8 @@ package com.mysql.cj.protocol; +import com.mysql.cj.util.TimeUtil; + public class InternalTime { private int hours = 0; @@ -94,4 +96,12 @@ public int getScale() { public void setScale(int scale) { this.scale = scale; } + + @Override + public String toString() { + if (this.nanos > 0) { + return String.format("%02d:%02d:%02d.%s", this.hours, this.minutes, this.seconds, TimeUtil.formatNanos(this.nanos, this.scale, false)); + } + return String.format("%02d:%02d:%02d", this.hours, this.minutes, this.seconds); + } } diff --git a/src/main/core-api/java/com/mysql/cj/util/TimeUtil.java b/src/main/core-api/java/com/mysql/cj/util/TimeUtil.java index f6385fc72..af396d08c 100644 --- a/src/main/core-api/java/com/mysql/cj/util/TimeUtil.java +++ b/src/main/core-api/java/com/mysql/cj/util/TimeUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2020, Oracle and/or its affiliates. + * Copyright (c) 2002, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -30,22 +30,26 @@ package com.mysql.cj.util; import java.io.IOException; -import java.io.StringReader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.Time; import java.sql.Timestamp; import java.text.SimpleDateFormat; +import java.time.Duration; +import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; import java.util.Calendar; import java.util.Locale; import java.util.Properties; import java.util.TimeZone; +import java.util.regex.Pattern; import com.mysql.cj.Messages; +import com.mysql.cj.MysqlType; import com.mysql.cj.exceptions.ExceptionFactory; import com.mysql.cj.exceptions.ExceptionInterceptor; import com.mysql.cj.exceptions.InvalidConnectionAttributeException; @@ -68,6 +72,26 @@ public class TimeUtil { public static final DateTimeFormatter DATETIME_FORMATTER_NO_FRACT_WITH_OFFSET = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssXXX"); public static final DateTimeFormatter DATETIME_FORMATTER_WITH_NANOS_WITH_OFFSET = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSSSSXXX"); + public static final Pattern DATE_LITERAL_WITH_DELIMITERS = Pattern + .compile("(\\d{4}|\\d{2})[\\p{Punct}&&[^:]](([0])?[1-9]|[1][0-2])[\\p{Punct}&&[^:]](([0])?[1-9]|[1-2]\\d|[3][0-1])"); + public static final Pattern DATE_LITERAL_NO_DELIMITERS = Pattern.compile("(\\d{4}|\\d{2})([0][1-9]|[1][0-2])([0][1-9]|[1-2]\\d|[3][0-1])"); + + public static final Pattern TIME_LITERAL_WITH_DELIMITERS = Pattern.compile("(([0-1])?\\d|[2][0-3]):([0-5])?\\d(:([0-5])?\\d(\\.\\d{1,9})?)?"); + public static final Pattern TIME_LITERAL_SHORT6 = Pattern.compile("([0-1]\\d|[2][0-3])([0-5]\\d){2}(\\.\\d{1,9})?"); + public static final Pattern TIME_LITERAL_SHORT4 = Pattern.compile("([0-5]\\d){2}(\\.\\d{1,9})?"); + public static final Pattern TIME_LITERAL_SHORT2 = Pattern.compile("[0-5]\\d(\\.\\d{1,9})?"); + + public static final Pattern DATETIME_LITERAL_WITH_DELIMITERS = Pattern.compile( + "(\\d{4}|\\d{2})\\p{Punct}(([0])?[1-9]|[1][0-2])\\p{Punct}(([0])?[1-9]|[1-2]\\d|[3][0-1])[ T](([0-1])?\\d|[2][0-3])\\p{Punct}([0-5])?\\d(\\p{Punct}([0-5])?\\d(\\.\\d{1,9})?)?"); + public static final Pattern DATETIME_LITERAL_SHORT14 = Pattern + .compile("\\d{4}([0][1-9]|[1][0-2])([0][1-9]|[1-2]\\d|[3][0-1])([0-1]\\d|[2][0-3])([0-5]\\d){2}(\\.\\d{1,9}){0,1}"); + public static final Pattern DATETIME_LITERAL_SHORT12 = Pattern + .compile("\\d{2}([0][1-9]|[1][0-2])([0][1-9]|[1-2]\\d|[3][0-1])([0-1]\\d|[2][0-3])([0-5]\\d){2}(\\.\\d{1,9}){0,1}"); + + public static final Pattern DURATION_LITERAL_WITH_DAYS = Pattern + .compile("(-)?(([0-2])?\\d|[3][0-4]) (([0-1])?\\d|[2][0-3])(:([0-5])?\\d(:([0-5])?\\d(\\.\\d{1,9})?)?)?"); + public static final Pattern DURATION_LITERAL_NO_DAYS = Pattern.compile("(-)?\\d{1,3}:([0-5])?\\d(:([0-5])?\\d(\\.\\d{1,9})?)?"); + // Mappings from TimeZone identifications (prefixed by type: Windows, TZ name, MetaZone, TZ alias, ...), to standard TimeZone Ids private static final String TIME_ZONE_MAPPINGS_RESOURCE = "/com/mysql/cj/util/TimeZoneMapping.properties"; @@ -219,6 +243,21 @@ public static LocalTime adjustNanosPrecision(LocalTime x, int fsp, boolean serve return x.withNano(adjustedNano); } + public static Duration adjustNanosPrecision(Duration x, int fsp, boolean serverRoundFracSecs) { + if (fsp < 0 || fsp > 6) { + throw ExceptionFactory.createException(WrongArgumentException.class, "fsp value must be in 0 to 6 range."); + } + int originalNano = x.getNano(); + double tail = Math.pow(10, 9 - fsp); + + int adjustedNano = serverRoundFracSecs ? (int) Math.round(originalNano / tail) * (int) tail : (int) (originalNano / tail) * (int) tail; + if (adjustedNano > 999999999) { // if rounded up to the second then increment seconds + adjustedNano %= 1000000000; + x = x.plusSeconds(1); + } + return x.withNanos(adjustedNano); + } + /** * Return a string representation of a fractional seconds part. This method assumes that all Timestamp adjustments are already done before, * thus no rounding is needed, only a proper "0" padding to be done. @@ -357,191 +396,118 @@ public static SimpleDateFormat getSimpleDateFormat(String pattern, Calendar cal) return sdf; } - /** - * Used in prepared statements - * - * @param dt - * DateTime string - * @param toTime - * true if get Time pattern - * @return pattern - * @throws IOException - * if an error occurs - */ - public static final String getDateTimePattern(String dt, boolean toTime) throws IOException { - // - // Special case - // - int dtLength = (dt != null) ? dt.length() : 0; - - if ((dtLength >= 8) && (dtLength <= 10)) { - int dashCount = 0; - boolean isDateOnly = true; + public static Object parseToDateTimeObject(String s, MysqlType targetMysqlType) throws IOException { + if (DATE_LITERAL_WITH_DELIMITERS.matcher(s).matches()) { + return LocalDate.parse(getCanonicalDate(s), DateTimeFormatter.ISO_LOCAL_DATE); - for (int i = 0; i < dtLength; i++) { - char c = dt.charAt(i); + } else if (DATE_LITERAL_NO_DELIMITERS.matcher(s).matches() && !(targetMysqlType == MysqlType.TIME && TIME_LITERAL_SHORT6.matcher(s).matches())) { + return s.length() == 8 ? LocalDate.parse(s, DateTimeFormatter.BASIC_ISO_DATE) : LocalDate.parse(s, DateTimeFormatter.ofPattern("yyMMdd")); - if (!Character.isDigit(c) && (c != '-')) { - isDateOnly = false; + } else if (TIME_LITERAL_WITH_DELIMITERS.matcher(s).matches()) { + return LocalTime.parse(getCanonicalTime(s), + new DateTimeFormatterBuilder().appendPattern("HH:mm:ss").appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).toFormatter()); - break; - } + } else if (TIME_LITERAL_SHORT6.matcher(s).matches()) { + return LocalTime.parse(s, + new DateTimeFormatterBuilder().appendPattern("HHmmss").appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).toFormatter()); - if (c == '-') { - dashCount++; - } - } + } else if (TIME_LITERAL_SHORT4.matcher(s).matches()) { + return LocalTime.parse("00" + s, + new DateTimeFormatterBuilder().appendPattern("HHmmss").appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).toFormatter()); - if (isDateOnly && (dashCount == 2)) { - return "yyyy-MM-dd"; - } - } + } else if (TIME_LITERAL_SHORT2.matcher(s).matches()) { + return LocalTime.parse("0000" + s, + new DateTimeFormatterBuilder().appendPattern("HHmmss").appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).toFormatter()); - // - // Special case - time-only - // - boolean colonsOnly = true; + } else if (DATETIME_LITERAL_SHORT14.matcher(s).matches()) { + return LocalDateTime.parse(s, + new DateTimeFormatterBuilder().appendPattern("yyyyMMddHHmmss").appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).toFormatter()); - for (int i = 0; i < dtLength; i++) { - char c = dt.charAt(i); + } else if (DATETIME_LITERAL_SHORT12.matcher(s).matches()) { + return LocalDateTime.parse(s, + new DateTimeFormatterBuilder().appendPattern("yyMMddHHmmss").appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).toFormatter()); - if (!Character.isDigit(c) && (c != ':')) { - colonsOnly = false; + } else if (DATETIME_LITERAL_WITH_DELIMITERS.matcher(s).matches()) { + return LocalDateTime.parse(getCanonicalDateTime(s), + new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss").appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true).toFormatter()); - break; + } else if (DURATION_LITERAL_WITH_DAYS.matcher(s).matches() || DURATION_LITERAL_NO_DAYS.matcher(s).matches()) { + s = s.startsWith("-") ? s.replace("-", "-P") : "P" + s; + s = s.contains(" ") ? s.replace(" ", "DT") : s.replace("P", "PT"); + String[] ch = new String[] { "H", "M", "S" }; + int pos = 0; + while (s.contains(":")) { + s = s.replaceFirst(":", ch[pos++]); } + s = s + ch[pos]; + return Duration.parse(s); } + throw ExceptionFactory.createException(WrongArgumentException.class, "There is no known date-time pattern for '" + s + "' value"); + } - if (colonsOnly) { - return "HH:mm:ss"; + private static String getCanonicalDate(String s) { + String[] sa = s.split("\\p{Punct}"); + StringBuilder sb = new StringBuilder(); + if (sa[0].length() == 2) { + sb.append(Integer.valueOf(sa[0]) > 69 ? "19" : "20"); } - - int n; - int z; - int count; - int maxvecs; - char c; - char separator; - StringReader reader = new StringReader(dt + " "); - ArrayList vec = new ArrayList<>(); - ArrayList vecRemovelist = new ArrayList<>(); - Object[] nv = new Object[3]; - Object[] v; - nv[0] = Character.valueOf('y'); - nv[1] = new StringBuilder(); - nv[2] = Integer.valueOf(0); - vec.add(nv); - - if (toTime) { - nv = new Object[3]; - nv[0] = Character.valueOf('h'); - nv[1] = new StringBuilder(); - nv[2] = Integer.valueOf(0); - vec.add(nv); + sb.append(sa[0]); + sb.append("-"); + if (sa[1].length() == 1) { + sb.append("0"); } - - while ((z = reader.read()) != -1) { - separator = (char) z; - maxvecs = vec.size(); - - for (count = 0; count < maxvecs; count++) { - v = vec.get(count); - n = ((Integer) v[2]).intValue(); - c = getSuccessor(((Character) v[0]).charValue(), n); - - if (!Character.isLetterOrDigit(separator)) { - if ((c == ((Character) v[0]).charValue()) && (c != 'S')) { - vecRemovelist.add(v); - } else { - ((StringBuilder) v[1]).append(separator); - - if ((c == 'X') || (c == 'Y')) { - v[2] = Integer.valueOf(4); - } - } - } else { - if (c == 'X') { - c = 'y'; - nv = new Object[3]; - nv[1] = (new StringBuilder(((StringBuilder) v[1]).toString())).append('M'); - nv[0] = Character.valueOf('M'); - nv[2] = Integer.valueOf(1); - vec.add(nv); - } else if (c == 'Y') { - c = 'M'; - nv = new Object[3]; - nv[1] = (new StringBuilder(((StringBuilder) v[1]).toString())).append('d'); - nv[0] = Character.valueOf('d'); - nv[2] = Integer.valueOf(1); - vec.add(nv); - } - - ((StringBuilder) v[1]).append(c); - - if (c == ((Character) v[0]).charValue()) { - v[2] = Integer.valueOf(n + 1); - } else { - v[0] = Character.valueOf(c); - v[2] = Integer.valueOf(1); - } - } - } - - int size = vecRemovelist.size(); - - for (int i = 0; i < size; i++) { - v = vecRemovelist.get(i); - vec.remove(v); - } - - vecRemovelist.clear(); + sb.append(sa[1]); + sb.append("-"); + if (sa[2].length() == 1) { + sb.append("0"); } + sb.append(sa[2]); - int size = vec.size(); - - for (int i = 0; i < size; i++) { - v = vec.get(i); - c = ((Character) v[0]).charValue(); - n = ((Integer) v[2]).intValue(); + return sb.toString(); + } - boolean bk = getSuccessor(c, n) != c; - boolean atEnd = (((c == 's') || (c == 'm') || ((c == 'h') && toTime)) && bk); - boolean finishesAtDate = (bk && (c == 'd') && !toTime); - boolean containsEnd = (((StringBuilder) v[1]).toString().indexOf('W') != -1); + private static String getCanonicalTime(String s) { + String[] sa = s.split("\\p{Punct}"); + StringBuilder sb = new StringBuilder(); - if ((!atEnd && !finishesAtDate) || (containsEnd)) { - vecRemovelist.add(v); + for (int i = 0; i < sa.length; i++) { + if (i > 0) { + sb.append(i < 3 ? ":" : "."); } - } - - size = vecRemovelist.size(); + if (i < 3 && sa[i].length() == 1) { + sb.append("0"); + } + sb.append(sa[i]); - for (int i = 0; i < size; i++) { - vec.remove(vecRemovelist.get(i)); + } + if (sa.length < 3) { + sb.append(":00"); } - vecRemovelist.clear(); - v = vec.get(0); // might throw exception - - StringBuilder format = (StringBuilder) v[1]; - format.setLength(format.length() - 1); + return sb.toString(); + } - return format.toString(); + private static String getCanonicalDateTime(String s) { + String[] sa = s.split("[ T]"); + StringBuilder sb = new StringBuilder(); + sb.append(getCanonicalDate(sa[0])); + sb.append(" "); + sb.append(getCanonicalTime(sa[1])); + return sb.toString(); } - private static final char getSuccessor(char c, int n) { - return ((c == 'y') && (n == 2)) ? 'X' - : (((c == 'y') && (n < 4)) ? 'y' - : ((c == 'y') ? 'M' - : (((c == 'M') && (n == 2)) ? 'Y' - : (((c == 'M') && (n < 3)) ? 'M' - : ((c == 'M') ? 'd' - : (((c == 'd') && (n < 2)) ? 'd' - : ((c == 'd') ? 'H' - : (((c == 'H') && (n < 2)) ? 'H' - : ((c == 'H') ? 'm' - : (((c == 'm') && (n < 2)) ? 'm' - : ((c == 'm') ? 's' - : (((c == 's') && (n < 2)) ? 's' : 'W')))))))))))); + public static String getDurationString(Duration x) { + String s = (x.isNegative() ? "-" + x.abs().toString() : x.toString()).replace("PT", ""); + if (s.contains("M")) { + s = s.replace("H", ":"); + if (s.contains("S")) { + s = s.replace("M", ":").replace("S", ""); + } else { + s = s.replace("M", ":0"); + } + } else { + s = s.replace("H", ":0:0"); + } + return s; } } diff --git a/src/main/core-impl/java/com/mysql/cj/AbstractQueryBindings.java b/src/main/core-impl/java/com/mysql/cj/AbstractQueryBindings.java index 31f11f71b..6da3d890b 100644 --- a/src/main/core-impl/java/com/mysql/cj/AbstractQueryBindings.java +++ b/src/main/core-impl/java/com/mysql/cj/AbstractQueryBindings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. + * Copyright (c) 2017, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -37,7 +37,7 @@ import java.math.BigDecimal; import java.math.BigInteger; import java.sql.Timestamp; -import java.text.ParsePosition; +import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -47,7 +47,6 @@ import java.time.ZonedDateTime; import java.util.Calendar; import java.util.HashMap; -import java.util.Locale; import java.util.Map; import java.util.Optional; @@ -226,6 +225,7 @@ public final void hexEscapeBlock(byte[] buf, NativePacketPayload packet, int siz DEFAULT_MYSQL_TYPES.put(OffsetTime.class, MysqlType.TIME); // default JDBC mapping is TIME_WITH_TIMEZONE, see B-4 DEFAULT_MYSQL_TYPES.put(OffsetDateTime.class, MysqlType.TIMESTAMP); // default JDBC mapping is TIMESTAMP_WITH_TIMEZONE, see B-4 DEFAULT_MYSQL_TYPES.put(ZonedDateTime.class, MysqlType.TIMESTAMP); // no JDBC mapping is defined + DEFAULT_MYSQL_TYPES.put(Duration.class, MysqlType.TIME); DEFAULT_MYSQL_TYPES.put(java.sql.Blob.class, MysqlType.BLOB); DEFAULT_MYSQL_TYPES.put(java.sql.Clob.class, MysqlType.TEXT); DEFAULT_MYSQL_TYPES.put(BigInteger.class, MysqlType.BIGINT); @@ -531,6 +531,25 @@ public void setObject(int parameterIndex, Object parameterObj, MysqlType targetM this.session.getExceptionInterceptor()); } + } else if (parameterObj instanceof Duration) { + switch (targetMysqlType) { + case TIME: + setDuration(parameterIndex, (Duration) parameterObj, targetMysqlType); + break; + case CHAR: + case VARCHAR: + case TINYTEXT: + case TEXT: + case MEDIUMTEXT: + case LONGTEXT: + setString(parameterIndex, TimeUtil.getDurationString((Duration) parameterObj)); + break; + default: + throw ExceptionFactory.createException(WrongArgumentException.class, + Messages.getString("PreparedStatement.67", new Object[] { parameterObj.getClass().getName(), targetMysqlType.toString() }), + this.session.getExceptionInterceptor()); + } + } else if (parameterObj instanceof java.sql.Date) { switch (targetMysqlType) { case DATE: @@ -815,14 +834,9 @@ public void setObject(int parameterIndex, Object parameterObj, MysqlType targetM case DATE: case DATETIME: case TIMESTAMP: - case YEAR: - ParsePosition pp = new ParsePosition(0); - java.text.DateFormat sdf = new java.text.SimpleDateFormat(TimeUtil.getDateTimePattern((String) parameterObj, false), Locale.US); - setObject(parameterIndex, sdf.parse((String) parameterObj, pp), targetMysqlType, scaleOrLength); - break; case TIME: - sdf = new java.text.SimpleDateFormat(TimeUtil.getDateTimePattern((String) parameterObj, true), Locale.US); - setTime(parameterIndex, new java.sql.Time(sdf.parse((String) parameterObj).getTime())); + case YEAR: + setObject(parameterIndex, TimeUtil.parseToDateTimeObject((String) parameterObj, targetMysqlType), targetMysqlType); break; case UNKNOWN: setSerializableObject(parameterIndex, parameterObj); diff --git a/src/main/core-impl/java/com/mysql/cj/ClientPreparedQueryBindings.java b/src/main/core-impl/java/com/mysql/cj/ClientPreparedQueryBindings.java index b71ab8bd9..713ecb90a 100644 --- a/src/main/core-impl/java/com/mysql/cj/ClientPreparedQueryBindings.java +++ b/src/main/core-impl/java/com/mysql/cj/ClientPreparedQueryBindings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. + * Copyright (c) 2017, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -47,9 +47,13 @@ import java.sql.Time; import java.sql.Timestamp; import java.text.SimpleDateFormat; +import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; import java.util.Calendar; import com.mysql.cj.conf.PropertyKey; @@ -463,14 +467,13 @@ public void setLocalTime(int parameterIndex, LocalTime x, MysqlType targetMysqlT x = TimeUtil.adjustNanosPrecision(x, fractLen, !this.session.getServerSession().isServerTruncatesFracSecs()); } + DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendPattern("HH:mm:ss").appendFraction(ChronoField.NANO_OF_SECOND, 0, 6, true) + .toFormatter(); + switch (targetMysqlType) { case TIME: StringBuilder sb = new StringBuilder("'"); - sb.append(x.toString()); - if (sb.length() < 7) { - // LocalTime.toString() omits zero parts, so we need to append zero minutes to be consistent with SSPS - sb.append(":00"); - } + sb.append(x.format(formatter)); sb.append("'"); setValue(parameterIndex, sb.toString(), targetMysqlType); break; @@ -479,11 +482,35 @@ public void setLocalTime(int parameterIndex, LocalTime x, MysqlType targetMysqlT sb = new StringBuilder("'"); sb.append(DEFAULT_DATE); sb.append(" "); - sb.append(x.toString()); - if (sb.length() < 18) { - // LocalTime.toString() omits zero parts, so we need to append zero minutes to be consistent with SSPS - sb.append(":00"); - } + sb.append(x.format(formatter)); + sb.append("'"); + setValue(parameterIndex, sb.toString(), targetMysqlType); + break; + default: + break; + } + } + + @Override + public void setDuration(int parameterIndex, Duration x, MysqlType targetMysqlType) { + if (!this.session.getServerSession().getCapabilities().serverSupportsFracSecs() || !this.sendFractionalSeconds.getValue()) { + if (x.getNano() > 0) { + x = x.isNegative() ? x.plusSeconds(1).withNanos(0) : x.withNanos(0); // truncate nanoseconds + } + } else { + int fractLen = 6; // max supported length (i.e. microsecond) + if (this.columnDefinition != null && parameterIndex <= this.columnDefinition.getFields().length && parameterIndex >= 0) { + // use the column definition if available + fractLen = this.columnDefinition.getFields()[parameterIndex].getDecimals(); + } + + x = TimeUtil.adjustNanosPrecision(x, fractLen, !this.session.getServerSession().isServerTruncatesFracSecs()); + } + + switch (targetMysqlType) { + case TIME: + StringBuilder sb = new StringBuilder("'"); + sb.append(TimeUtil.getDurationString(x)); sb.append("'"); setValue(parameterIndex, sb.toString(), targetMysqlType); break; @@ -528,25 +555,19 @@ public void setLocalDateTime(int parameterIndex, LocalDateTime x, MysqlType targ switch (targetMysqlType) { case TIME: + DateTimeFormatter formatter = new DateTimeFormatterBuilder().appendPattern("HH:mm:ss") + .appendFraction(ChronoField.NANO_OF_SECOND, 0, 6, true).toFormatter(); StringBuilder sb = new StringBuilder("'"); - sb.append(x.toLocalTime().toString()); - if (sb.length() < 7) { - // LocalTime.toString() omits zero parts, so we need to append zero minutes to be consistent with SSPS - sb.append(":00"); - } + sb.append(x.toLocalTime().format(formatter)); sb.append("'"); setValue(parameterIndex, sb.toString(), targetMysqlType); break; case DATETIME: case TIMESTAMP: + formatter = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss").appendFraction(ChronoField.NANO_OF_SECOND, 0, 6, true) + .toFormatter(); sb = new StringBuilder("'"); - sb.append(x.toLocalDate()); - sb.append(" "); - sb.append(x.toLocalTime().toString()); - if (sb.length() < 18) { - // LocalTime.toString() omits zero parts, so we need to append zero minutes to be consistent with SSPS - sb.append(":00"); - } + sb.append(x.format(formatter)); sb.append("'"); setValue(parameterIndex, sb.toString(), targetMysqlType); break; diff --git a/src/main/core-impl/java/com/mysql/cj/ServerPreparedQueryBindValue.java b/src/main/core-impl/java/com/mysql/cj/ServerPreparedQueryBindValue.java index f461fcfc0..313da18ef 100644 --- a/src/main/core-impl/java/com/mysql/cj/ServerPreparedQueryBindValue.java +++ b/src/main/core-impl/java/com/mysql/cj/ServerPreparedQueryBindValue.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. + * Copyright (c) 2017, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -29,9 +29,13 @@ package com.mysql.cj; +import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; import java.util.Calendar; import java.util.Locale; import java.util.TimeZone; @@ -46,6 +50,7 @@ import com.mysql.cj.protocol.a.NativeConstants.StringSelfDataType; import com.mysql.cj.protocol.a.NativePacketPayload; import com.mysql.cj.util.StringUtils; +import com.mysql.cj.util.TimeUtil; //TODO should not be protocol-specific @@ -138,6 +143,11 @@ public String toString(boolean quoteIfNeeded) { return "NULL"; } + DateTimeFormatter timeFmtWithOptMicros = new DateTimeFormatterBuilder().appendPattern("HH:mm:ss").appendFraction(ChronoField.NANO_OF_SECOND, 0, 6, true) + .toFormatter(); + DateTimeFormatter datetimeFmtWithOptMicros = new DateTimeFormatterBuilder().appendPattern("yyyy-MM-dd HH:mm:ss") + .appendFraction(ChronoField.NANO_OF_SECOND, 0, 6, true).toFormatter(); + switch (this.bufferType) { case MysqlType.FIELD_TYPE_TINY: case MysqlType.FIELD_TYPE_SHORT: @@ -149,9 +159,40 @@ public String toString(boolean quoteIfNeeded) { case MysqlType.FIELD_TYPE_DOUBLE: return String.valueOf(((Double) this.value).doubleValue()); case MysqlType.FIELD_TYPE_TIME: + String s; + if (this.value instanceof LocalDateTime) { + s = ((LocalDateTime) this.value).format(timeFmtWithOptMicros); + } else if (this.value instanceof LocalTime) { + s = ((LocalTime) this.value).format(timeFmtWithOptMicros); + } else if (this.value instanceof Duration) { + s = TimeUtil.getDurationString(((Duration) this.value)); + } else { + s = String.valueOf(this.value); + } + return "'" + s + "'"; case MysqlType.FIELD_TYPE_DATE: + if (this.value instanceof LocalDate) { + s = ((LocalDate) this.value).format(TimeUtil.DATE_FORMATTER); + } else if (this.value instanceof LocalTime) { + s = ((LocalTime) this.value).atDate(LocalDate.of(1970, 1, 1)).format(TimeUtil.DATE_FORMATTER); + } else if (this.value instanceof LocalDateTime) { + s = ((LocalDateTime) this.value).format(TimeUtil.DATE_FORMATTER); + } else { + s = String.valueOf(this.value); + } + return "'" + s + "'"; case MysqlType.FIELD_TYPE_DATETIME: case MysqlType.FIELD_TYPE_TIMESTAMP: + if (this.value instanceof LocalDate) { + s = ((LocalDate) this.value).format(datetimeFmtWithOptMicros); + } else if (this.value instanceof LocalTime) { + s = ((LocalTime) this.value).atDate(LocalDate.of(1970, 1, 1)).format(timeFmtWithOptMicros); + } else if (this.value instanceof LocalDateTime) { + s = ((LocalDateTime) this.value).format(datetimeFmtWithOptMicros); + } else { + s = String.valueOf(this.value); + } + return "'" + s + "'"; case MysqlType.FIELD_TYPE_VAR_STRING: case MysqlType.FIELD_TYPE_STRING: case MysqlType.FIELD_TYPE_VARCHAR: @@ -316,7 +357,7 @@ private void storeDate(NativePacketPayload intoPacket) { } private void storeTime(NativePacketPayload intoPacket) { - int hours, minutes, seconds, microseconds; + int neg = 0, days = 0, hours, minutes, seconds, microseconds; if (this.value instanceof LocalDateTime) { hours = ((LocalDateTime) this.value).getHour(); @@ -328,6 +369,16 @@ private void storeTime(NativePacketPayload intoPacket) { minutes = ((LocalTime) this.value).getMinute(); seconds = ((LocalTime) this.value).getSecond(); microseconds = ((LocalTime) this.value).getNano() / 1000; + } else if (this.value instanceof Duration) { + neg = ((Duration) this.value).isNegative() ? 1 : 0; + long fullSeconds = ((Duration) this.value).abs().getSeconds(); + seconds = (int) (fullSeconds % 60); + long fullMinutes = fullSeconds / 60; + minutes = (int) (fullMinutes % 60); + long fullHours = fullMinutes / 60; + hours = (int) (fullHours % 24); + days = (int) (fullHours / 24); + microseconds = ((Duration) this.value).abs().getNano() / 1000; } else { if (this.calendar == null) { this.calendar = Calendar.getInstance(this.defaultTimeZone, Locale.US); @@ -343,8 +394,8 @@ private void storeTime(NativePacketPayload intoPacket) { intoPacket.ensureCapacity(microseconds > 0 ? 13 : 9); intoPacket.writeInteger(IntegerDataType.INT1, microseconds > 0 ? 12 : 8); // length - intoPacket.writeInteger(IntegerDataType.INT1, 0); // neg flag - intoPacket.writeInteger(IntegerDataType.INT4, 0); // tm->day, not used + intoPacket.writeInteger(IntegerDataType.INT1, neg); + intoPacket.writeInteger(IntegerDataType.INT4, days); intoPacket.writeInteger(IntegerDataType.INT1, hours); intoPacket.writeInteger(IntegerDataType.INT1, minutes); intoPacket.writeInteger(IntegerDataType.INT1, seconds); diff --git a/src/main/core-impl/java/com/mysql/cj/ServerPreparedQueryBindings.java b/src/main/core-impl/java/com/mysql/cj/ServerPreparedQueryBindings.java index 6795aaa11..d239b0247 100644 --- a/src/main/core-impl/java/com/mysql/cj/ServerPreparedQueryBindings.java +++ b/src/main/core-impl/java/com/mysql/cj/ServerPreparedQueryBindings.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2020, Oracle and/or its affiliates. + * Copyright (c) 2017, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -39,6 +39,7 @@ import java.sql.NClob; import java.sql.Time; import java.sql.Timestamp; +import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -572,4 +573,30 @@ public void bindTimestamp(int parameterIndex, Timestamp x, Calendar targetCalend binding.parameterType = targetMysqlType; } + + @Override + public void setDuration(int parameterIndex, Duration x, MysqlType targetMysqlType) { + ServerPreparedQueryBindValue binding = getBinding(parameterIndex, false); + if (!this.session.getServerSession().getCapabilities().serverSupportsFracSecs() || !this.sendFractionalSeconds.getValue()) { + if (x.getNano() > 0) { + x = x.isNegative() ? x.plusSeconds(1).withNanos(0) : x.withNanos(0); // truncate nanoseconds + } + } else { + int fractLen = 6; // max supported length (i.e. microsecond) + if (this.columnDefinition != null && parameterIndex <= this.columnDefinition.getFields().length && parameterIndex >= 0) { + // use the column definition if available + fractLen = this.columnDefinition.getFields()[parameterIndex].getDecimals(); + } + x = TimeUtil.adjustNanosPrecision(x, fractLen, !this.session.getServerSession().isServerTruncatesFracSecs()); + } + + if (targetMysqlType == MysqlType.TIME) { + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_TIME, this.numberOfExecutions)); + } else { + // DATETIME or TIMESTAMP + this.sendTypesToServer.compareAndSet(false, binding.resetToType(MysqlType.FIELD_TYPE_DATETIME, this.numberOfExecutions)); + } + binding.parameterType = targetMysqlType; + binding.value = x; + } } diff --git a/src/main/core-impl/java/com/mysql/cj/result/DurationValueFactory.java b/src/main/core-impl/java/com/mysql/cj/result/DurationValueFactory.java new file mode 100644 index 000000000..77f92f74c --- /dev/null +++ b/src/main/core-impl/java/com/mysql/cj/result/DurationValueFactory.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 2.0, as published by the + * Free Software Foundation. + * + * This program is also distributed with certain software (including but not + * limited to OpenSSL) that is licensed under separate terms, as designated in a + * particular file or component or in included license documentation. The + * authors of MySQL hereby grant you an additional permission to link the + * program and your derivative works with the separately licensed software that + * they have included with MySQL. + * + * Without limiting anything contained in the foregoing, this file, which is + * part of MySQL Connector/J, is also subject to the Universal FOSS Exception, + * version 1.0, a copy of which can be found at + * http://oss.oracle.com/licenses/universal-foss-exception. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, + * for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package com.mysql.cj.result; + +import java.time.Duration; + +import com.mysql.cj.conf.PropertySet; +import com.mysql.cj.protocol.InternalDate; +import com.mysql.cj.protocol.InternalTime; +import com.mysql.cj.protocol.InternalTimestamp; + +/** + * A value factory to create {@link Duration} instances. + */ +public class DurationValueFactory extends AbstractDateTimeValueFactory { + + public DurationValueFactory(PropertySet pset) { + super(pset); + } + + @Override + Duration localCreateFromDate(InternalDate idate) { + return unsupported("DATE"); + } + + @Override + public Duration localCreateFromTime(InternalTime it) { + String ptn = (it.getHours() < 0 ? "-PT" : "PT") + (it.getHours() < 0 ? -it.getHours() : it.getHours()) + "H" + it.getMinutes() + "M" + it.getSeconds() + + "." + it.getNanos() + "S"; + return Duration.parse(ptn); + } + + @Override + public Duration localCreateFromTimestamp(InternalTimestamp its) { + return unsupported("TIMESTAMP"); + } + + @Override + public Duration localCreateFromDatetime(InternalTimestamp its) { + return unsupported("DATETIME"); + } + + public String getTargetTypeName() { + return Duration.class.getName(); + } + +} diff --git a/src/main/core-impl/java/com/mysql/cj/result/LocalDateTimeValueFactory.java b/src/main/core-impl/java/com/mysql/cj/result/LocalDateTimeValueFactory.java index 604672e32..2fe31276d 100644 --- a/src/main/core-impl/java/com/mysql/cj/result/LocalDateTimeValueFactory.java +++ b/src/main/core-impl/java/com/mysql/cj/result/LocalDateTimeValueFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. + * Copyright (c) 2016, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -65,8 +65,7 @@ public LocalDateTime localCreateFromDate(InternalDate idate) { @Override public LocalDateTime localCreateFromTime(InternalTime it) { if (it.getHours() < 0 || it.getHours() >= 24) { - throw new DataReadException( - Messages.getString("ResultSet.InvalidTimeValue", new Object[] { "" + it.getHours() + ":" + it.getMinutes() + ":" + it.getSeconds() })); + throw new DataReadException(Messages.getString("ResultSet.InvalidTimeValue", new Object[] { it.toString() })); } return createFromTimestamp(new InternalTimestamp(1970, 1, 1, it.getHours(), it.getMinutes(), it.getSeconds(), it.getNanos(), it.getScale())); } diff --git a/src/main/core-impl/java/com/mysql/cj/result/LocalTimeValueFactory.java b/src/main/core-impl/java/com/mysql/cj/result/LocalTimeValueFactory.java index c7317d774..2b7b80848 100644 --- a/src/main/core-impl/java/com/mysql/cj/result/LocalTimeValueFactory.java +++ b/src/main/core-impl/java/com/mysql/cj/result/LocalTimeValueFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2020, Oracle and/or its affiliates. + * Copyright (c) 2016, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -62,8 +62,7 @@ LocalTime localCreateFromDate(InternalDate idate) { @Override public LocalTime localCreateFromTime(InternalTime it) { if (it.getHours() < 0 || it.getHours() >= 24) { - throw new DataReadException( - Messages.getString("ResultSet.InvalidTimeValue", new Object[] { "" + it.getHours() + ":" + it.getMinutes() + ":" + it.getSeconds() })); + throw new DataReadException(Messages.getString("ResultSet.InvalidTimeValue", new Object[] { it.toString() })); } return LocalTime.of(it.getHours(), it.getMinutes(), it.getSeconds(), it.getNanos()); } diff --git a/src/main/core-impl/java/com/mysql/cj/result/OffsetDateTimeValueFactory.java b/src/main/core-impl/java/com/mysql/cj/result/OffsetDateTimeValueFactory.java index b749fc301..9fcff9afc 100644 --- a/src/main/core-impl/java/com/mysql/cj/result/OffsetDateTimeValueFactory.java +++ b/src/main/core-impl/java/com/mysql/cj/result/OffsetDateTimeValueFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -80,8 +80,7 @@ public OffsetDateTime localCreateFromDate(InternalDate idate) { @Override public OffsetDateTime localCreateFromTime(InternalTime it) { if (it.getHours() < 0 || it.getHours() >= 24) { - throw new DataReadException( - Messages.getString("ResultSet.InvalidTimeValue", new Object[] { "" + it.getHours() + ":" + it.getMinutes() + ":" + it.getSeconds() })); + throw new DataReadException(Messages.getString("ResultSet.InvalidTimeValue", new Object[] { it.toString() })); } return LocalDateTime.of(1970, 1, 1, it.getHours(), it.getMinutes(), it.getSeconds(), it.getNanos()).atZone(this.defaultTimeZone.toZoneId()) .toOffsetDateTime(); diff --git a/src/main/core-impl/java/com/mysql/cj/result/OffsetTimeValueFactory.java b/src/main/core-impl/java/com/mysql/cj/result/OffsetTimeValueFactory.java index 7f5568aac..65c705d9e 100644 --- a/src/main/core-impl/java/com/mysql/cj/result/OffsetTimeValueFactory.java +++ b/src/main/core-impl/java/com/mysql/cj/result/OffsetTimeValueFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -72,8 +72,7 @@ OffsetTime localCreateFromDate(InternalDate idate) { @Override public OffsetTime localCreateFromTime(InternalTime it) { if (it.getHours() < 0 || it.getHours() >= 24) { - throw new DataReadException( - Messages.getString("ResultSet.InvalidTimeValue", new Object[] { "" + it.getHours() + ":" + it.getMinutes() + ":" + it.getSeconds() })); + throw new DataReadException(Messages.getString("ResultSet.InvalidTimeValue", new Object[] { it.toString() })); } return LocalTime.of(it.getHours(), it.getMinutes(), it.getSeconds(), it.getNanos()).atOffset(ZoneOffset.ofTotalSeconds(this.tz.getRawOffset() / 1000)); } diff --git a/src/main/core-impl/java/com/mysql/cj/result/SqlTimeValueFactory.java b/src/main/core-impl/java/com/mysql/cj/result/SqlTimeValueFactory.java index bd5a45383..a4d37e99f 100644 --- a/src/main/core-impl/java/com/mysql/cj/result/SqlTimeValueFactory.java +++ b/src/main/core-impl/java/com/mysql/cj/result/SqlTimeValueFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. + * Copyright (c) 2015, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -84,8 +84,7 @@ Time localCreateFromDate(InternalDate idate) { @Override public Time localCreateFromTime(InternalTime it) { if (it.getHours() < 0 || it.getHours() >= 24) { - throw new DataReadException( - Messages.getString("ResultSet.InvalidTimeValue", new Object[] { "" + it.getHours() + ":" + it.getMinutes() + ":" + it.getSeconds() })); + throw new DataReadException(Messages.getString("ResultSet.InvalidTimeValue", new Object[] { it.toString() })); } synchronized (this.cal) { diff --git a/src/main/core-impl/java/com/mysql/cj/result/SqlTimestampValueFactory.java b/src/main/core-impl/java/com/mysql/cj/result/SqlTimestampValueFactory.java index c516a6b15..db1a29ae3 100644 --- a/src/main/core-impl/java/com/mysql/cj/result/SqlTimestampValueFactory.java +++ b/src/main/core-impl/java/com/mysql/cj/result/SqlTimestampValueFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. + * Copyright (c) 2015, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -112,8 +112,7 @@ public Timestamp localCreateFromDate(InternalDate idate) { @Override public Timestamp localCreateFromTime(InternalTime it) { if (it.getHours() < 0 || it.getHours() >= 24) { - throw new DataReadException( - Messages.getString("ResultSet.InvalidTimeValue", new Object[] { "" + it.getHours() + ":" + it.getMinutes() + ":" + it.getSeconds() })); + throw new DataReadException(Messages.getString("ResultSet.InvalidTimeValue", new Object[] { it.toString() })); } synchronized (this.defaultTimeZone) { diff --git a/src/main/core-impl/java/com/mysql/cj/result/StringValueFactory.java b/src/main/core-impl/java/com/mysql/cj/result/StringValueFactory.java index b1305fd8f..b6fe5daaf 100644 --- a/src/main/core-impl/java/com/mysql/cj/result/StringValueFactory.java +++ b/src/main/core-impl/java/com/mysql/cj/result/StringValueFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. + * Copyright (c) 2015, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -40,7 +40,6 @@ import com.mysql.cj.protocol.InternalTimestamp; import com.mysql.cj.util.DataTypeUtil; import com.mysql.cj.util.StringUtils; -import com.mysql.cj.util.TimeUtil; /** * A {@link com.mysql.cj.result.ValueFactory} implementation to create strings. @@ -77,11 +76,7 @@ public String createFromDate(InternalDate idate) { * @return string */ public String createFromTime(InternalTime it) { - if (it.getNanos() > 0) { - return String.format("%02d:%02d:%02d.%s", it.getHours(), it.getMinutes(), it.getSeconds(), - TimeUtil.formatNanos(it.getNanos(), it.getScale(), false)); - } - return String.format("%02d:%02d:%02d", it.getHours(), it.getMinutes(), it.getSeconds()); + return it.toString(); } /** diff --git a/src/main/core-impl/java/com/mysql/cj/result/UtilCalendarValueFactory.java b/src/main/core-impl/java/com/mysql/cj/result/UtilCalendarValueFactory.java index fe32e360a..a783959b2 100644 --- a/src/main/core-impl/java/com/mysql/cj/result/UtilCalendarValueFactory.java +++ b/src/main/core-impl/java/com/mysql/cj/result/UtilCalendarValueFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -94,8 +94,7 @@ public Calendar localCreateFromDate(InternalDate idate) { @Override public Calendar localCreateFromTime(InternalTime it) { if (it.getHours() < 0 || it.getHours() >= 24) { - throw new DataReadException( - Messages.getString("ResultSet.InvalidTimeValue", new Object[] { "" + it.getHours() + ":" + it.getMinutes() + ":" + it.getSeconds() })); + throw new DataReadException(Messages.getString("ResultSet.InvalidTimeValue", new Object[] { it.toString() })); } try { diff --git a/src/main/core-impl/java/com/mysql/cj/result/ZonedDateTimeValueFactory.java b/src/main/core-impl/java/com/mysql/cj/result/ZonedDateTimeValueFactory.java index f549aa590..8e37dff10 100644 --- a/src/main/core-impl/java/com/mysql/cj/result/ZonedDateTimeValueFactory.java +++ b/src/main/core-impl/java/com/mysql/cj/result/ZonedDateTimeValueFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -80,8 +80,7 @@ public ZonedDateTime localCreateFromDate(InternalDate idate) { @Override public ZonedDateTime localCreateFromTime(InternalTime it) { if (it.getHours() < 0 || it.getHours() >= 24) { - throw new DataReadException( - Messages.getString("ResultSet.InvalidTimeValue", new Object[] { "" + it.getHours() + ":" + it.getMinutes() + ":" + it.getSeconds() })); + throw new DataReadException(Messages.getString("ResultSet.InvalidTimeValue", new Object[] { it.toString() })); } return LocalDateTime.of(1970, 1, 1, it.getHours(), it.getMinutes(), it.getSeconds(), it.getNanos()).atZone(this.defaultTimeZone.toZoneId()); } diff --git a/src/main/user-impl/java/com/mysql/cj/jdbc/result/ResultSetImpl.java b/src/main/user-impl/java/com/mysql/cj/jdbc/result/ResultSetImpl.java index 507e3ffd0..10606d9cd 100644 --- a/src/main/user-impl/java/com/mysql/cj/jdbc/result/ResultSetImpl.java +++ b/src/main/user-impl/java/com/mysql/cj/jdbc/result/ResultSetImpl.java @@ -54,6 +54,7 @@ import java.sql.Struct; import java.sql.Time; import java.sql.Timestamp; +import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -99,6 +100,7 @@ import com.mysql.cj.result.BooleanValueFactory; import com.mysql.cj.result.ByteValueFactory; import com.mysql.cj.result.DoubleValueFactory; +import com.mysql.cj.result.DurationValueFactory; import com.mysql.cj.result.Field; import com.mysql.cj.result.FloatValueFactory; import com.mysql.cj.result.IntegerValueFactory; @@ -1396,6 +1398,11 @@ public T getObject(int columnIndex, Class type) throws SQLException { checkRowPos(); checkColumnBounds(columnIndex); return (T) this.thisRow.getValue(columnIndex - 1, this.defaultZonedDateTimeValueFactory); + + } else if (type.equals(Duration.class)) { + checkRowPos(); + checkColumnBounds(columnIndex); + return (T) this.thisRow.getValue(columnIndex - 1, new DurationValueFactory(this.session.getPropertySet())); } if (this.connection.getPropertySet().getBooleanProperty(PropertyKey.autoDeserialize).getValue()) { diff --git a/src/test/java/com/mysql/cj/result/LocalDateTimeValueFactoryTest.java b/src/test/java/com/mysql/cj/result/LocalDateTimeValueFactoryTest.java index ebfc5d26f..4d0c856da 100644 --- a/src/test/java/com/mysql/cj/result/LocalDateTimeValueFactoryTest.java +++ b/src/test/java/com/mysql/cj/result/LocalDateTimeValueFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -67,7 +67,7 @@ public void testCreateFromDate() { @Test public void testCreateFromTime() { assertThrows(DataReadException.class, - "The value '-1:0:0' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", + "The value '-1:00:00' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", new Callable() { @Override public Void call() throws Exception { @@ -77,7 +77,7 @@ public Void call() throws Exception { }); assertThrows(DataReadException.class, - "The value '44:0:0' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", + "The value '44:00:00' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", new Callable() { @Override public Void call() throws Exception { diff --git a/src/test/java/com/mysql/cj/result/LocalTimeValueFactoryTest.java b/src/test/java/com/mysql/cj/result/LocalTimeValueFactoryTest.java index ace6caabe..b09873273 100644 --- a/src/test/java/com/mysql/cj/result/LocalTimeValueFactoryTest.java +++ b/src/test/java/com/mysql/cj/result/LocalTimeValueFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -74,7 +74,7 @@ public void testCreateFromTime() { assertEquals(LocalTime.of(1, 1, 1, 1), this.vf.createFromTime(new InternalTime(1, 1, 1, 1, 9))); assertThrows(DataReadException.class, - "The value '-1:0:0' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", + "The value '-1:00:00' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", new Callable() { @Override public Void call() throws Exception { @@ -84,7 +84,7 @@ public Void call() throws Exception { }); assertThrows(DataReadException.class, - "The value '44:0:0' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", + "The value '44:00:00' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", new Callable() { @Override public Void call() throws Exception { diff --git a/src/test/java/com/mysql/cj/result/SqlTimeValueFactoryTest.java b/src/test/java/com/mysql/cj/result/SqlTimeValueFactoryTest.java index 4263a8fc1..d2595ddcb 100644 --- a/src/test/java/com/mysql/cj/result/SqlTimeValueFactoryTest.java +++ b/src/test/java/com/mysql/cj/result/SqlTimeValueFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Oracle and/or its affiliates. + * Copyright (c) 2019, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -76,7 +76,7 @@ public void testCreateFromTime() { assertEquals(Time.valueOf(LocalTime.of(1, 1, 1, 1)), this.vf.createFromTime(new InternalTime(1, 1, 1, 1, 9))); assertThrows(DataReadException.class, - "The value '-1:0:0' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", + "The value '-1:00:00' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", new Callable() { @Override public Void call() throws Exception { @@ -86,7 +86,7 @@ public Void call() throws Exception { }); assertThrows(DataReadException.class, - "The value '44:0:0' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", + "The value '44:00:00' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", new Callable() { @Override public Void call() throws Exception { diff --git a/src/test/java/com/mysql/cj/result/SqlTimestampValueFactoryTest.java b/src/test/java/com/mysql/cj/result/SqlTimestampValueFactoryTest.java index 6594a47d5..8055a21a8 100644 --- a/src/test/java/com/mysql/cj/result/SqlTimestampValueFactoryTest.java +++ b/src/test/java/com/mysql/cj/result/SqlTimestampValueFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. + * Copyright (c) 2015, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -81,7 +81,7 @@ public void testCreateFromTime() { assertEquals(Timestamp.valueOf(LocalDateTime.of(1970, 1, 1, 1, 1, 1, 1)), this.vf.createFromTime(new InternalTime(1, 1, 1, 1, 9))); assertThrows(DataReadException.class, - "The value '-1:0:0' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", + "The value '-1:00:00' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", new Callable() { @Override public Void call() throws Exception { @@ -91,7 +91,7 @@ public Void call() throws Exception { }); assertThrows(DataReadException.class, - "The value '44:0:0' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", + "The value '44:00:00' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", new Callable() { @Override public Void call() throws Exception { diff --git a/src/test/java/com/mysql/cj/result/ZeroDateTimeToNullValueFactoryTest.java b/src/test/java/com/mysql/cj/result/ZeroDateTimeToNullValueFactoryTest.java index 5cf6d0a10..dcdedc382 100644 --- a/src/test/java/com/mysql/cj/result/ZeroDateTimeToNullValueFactoryTest.java +++ b/src/test/java/com/mysql/cj/result/ZeroDateTimeToNullValueFactoryTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2020, Oracle and/or its affiliates. + * Copyright (c) 2018, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -65,7 +65,7 @@ public void testBasics() { assertEquals(LocalDateTime.ofEpochSecond(0, 0, ZoneOffset.UTC), vf.createFromTime(new InternalTime())); assertThrows(DataReadException.class, - "The value '-1:0:0' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", + "The value '-1:00:00' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", new Callable() { @Override public Void call() throws Exception { @@ -75,7 +75,7 @@ public Void call() throws Exception { }); assertThrows(DataReadException.class, - "The value '44:0:0' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", + "The value '44:00:00' is an invalid TIME value. JDBC Time objects represent a wall-clock time and not a duration as MySQL treats them. If you are treating this type as a duration, consider retrieving this value as a string and dealing with it according to your requirements.", new Callable() { @Override public Void call() throws Exception { diff --git a/src/test/java/com/mysql/cj/util/TimeUtilTest.java b/src/test/java/com/mysql/cj/util/TimeUtilTest.java index d50082f30..63cf254ce 100644 --- a/src/test/java/com/mysql/cj/util/TimeUtilTest.java +++ b/src/test/java/com/mysql/cj/util/TimeUtilTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -29,13 +29,22 @@ package com.mysql.cj.util; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.io.IOException; import java.sql.Timestamp; +import java.time.Duration; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.LocalTime; import org.junit.jupiter.api.Test; +import com.mysql.cj.MysqlType; +import com.mysql.cj.exceptions.WrongArgumentException; + /** * Tests for {@link TimeUtil}. */ @@ -59,4 +68,263 @@ public void testAdjustTimestampNanosPrecision() { assertTrue(LocalDateTime.of(2020, 02, 26, 14, 30, 10, 777000000) .equals(TimeUtil.adjustNanosPrecision(LocalDateTime.of(2020, 02, 26, 14, 30, 10, 777777777), 3, false))); } + + @Test + public void testParseToDateTimeObject() throws IOException { + + /* DATE literals */ + + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("2020-01-01", MysqlType.DATE)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("20-01-01", MysqlType.DATE)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("2020#01$01", MysqlType.DATE)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("20%01%01", MysqlType.DATE)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("2020-1-1", MysqlType.DATE)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("20-1-1", MysqlType.DATE)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("20200101", MysqlType.DATE)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("200101", MysqlType.DATE)); + assertEquals(LocalDate.of(1970, 1, 1), TimeUtil.parseToDateTimeObject("70-1-1", MysqlType.DATE)); + + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("2020-01-01", MysqlType.DATETIME)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("20-01-01", MysqlType.DATETIME)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("2020#01$01", MysqlType.DATETIME)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("20%01%01", MysqlType.DATETIME)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("2020-1-1", MysqlType.DATETIME)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("20-1-1", MysqlType.DATETIME)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("20200101", MysqlType.DATETIME)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("200101", MysqlType.DATETIME)); + assertEquals(LocalDate.of(1970, 1, 1), TimeUtil.parseToDateTimeObject("70-1-1", MysqlType.DATETIME)); + + assertEquals(LocalTime.of(20, 13, 32), TimeUtil.parseToDateTimeObject("201332", MysqlType.DATETIME)); + assertEquals(LocalTime.of(20, 00, 32), TimeUtil.parseToDateTimeObject("200032", MysqlType.DATETIME)); + + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("2020-01-01", MysqlType.TIME)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("20-01-01", MysqlType.TIME)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("2020#01$01", MysqlType.TIME)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("20%01%01", MysqlType.TIME)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("2020-1-1", MysqlType.TIME)); + assertEquals(LocalDate.of(2020, 2, 1), TimeUtil.parseToDateTimeObject("20-2-1", MysqlType.TIME)); + assertEquals(LocalDate.of(1970, 1, 1), TimeUtil.parseToDateTimeObject("70-1-1", MysqlType.TIME)); + assertEquals(LocalDate.of(2020, 1, 1), TimeUtil.parseToDateTimeObject("20200101", MysqlType.TIME)); + assertEquals(LocalTime.of(20, 1, 1), TimeUtil.parseToDateTimeObject("200101", MysqlType.TIME)); + + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("2020-00-01", MysqlType.DATE); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("2020-01-00", MysqlType.DATE); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("2020-13-01", MysqlType.DATE); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("2020-01-32", MysqlType.DATE); + }, "There is no known date-time pattern for.*"); + + /* TIME literals */ + + assertEquals(LocalTime.of(13, 4, 17), TimeUtil.parseToDateTimeObject("13:04:17", MysqlType.DATE)); + assertEquals(LocalTime.of(13, 4), TimeUtil.parseToDateTimeObject("13:04", MysqlType.DATE)); + assertEquals(LocalDate.of(2003, 11, 07), TimeUtil.parseToDateTimeObject("031107", MysqlType.DATE)); // it could be both DATE or TIME literal + assertEquals(LocalTime.of(0, 4, 17), TimeUtil.parseToDateTimeObject("0417", MysqlType.DATE)); + assertEquals(LocalTime.of(3, 14, 7, 12000000), TimeUtil.parseToDateTimeObject("03:14:07.012", MysqlType.DATE)); + assertEquals(LocalTime.of(3, 14, 7, 123000000), TimeUtil.parseToDateTimeObject("031407.123", MysqlType.DATE)); + + assertEquals(LocalTime.of(13, 4, 17), TimeUtil.parseToDateTimeObject("13:04:17", MysqlType.TIME)); + assertEquals(LocalTime.of(13, 4), TimeUtil.parseToDateTimeObject("13:04", MysqlType.TIME)); + assertEquals(LocalTime.of(3, 11, 07), TimeUtil.parseToDateTimeObject("031107", MysqlType.TIME)); // it could be both DATE or TIME literal + + assertEquals(LocalTime.of(0, 4, 17), TimeUtil.parseToDateTimeObject("0417", MysqlType.TIME)); + assertEquals(LocalTime.of(0, 4, 17, 123400000), TimeUtil.parseToDateTimeObject("0417.1234", MysqlType.TIME)); + assertEquals(LocalTime.of(0, 0, 17, 123456789), TimeUtil.parseToDateTimeObject("17.123456789", MysqlType.TIME)); + + assertEquals(LocalTime.of(3, 14, 7, 12000000), TimeUtil.parseToDateTimeObject("03:14:07.012", MysqlType.TIME)); + assertEquals(LocalTime.of(3, 14, 7, 123000000), TimeUtil.parseToDateTimeObject("031407.123", MysqlType.TIME)); + + assertEquals(LocalTime.of(13, 4, 17), TimeUtil.parseToDateTimeObject("13:04:17", MysqlType.DATETIME)); + assertEquals(LocalTime.of(13, 4), TimeUtil.parseToDateTimeObject("13:04", MysqlType.DATETIME)); + assertEquals(LocalDate.of(2003, 11, 07), TimeUtil.parseToDateTimeObject("031107", MysqlType.DATETIME)); // it could be both DATE or TIME literal + assertEquals(LocalTime.of(0, 4, 17), TimeUtil.parseToDateTimeObject("0417", MysqlType.DATETIME)); + assertEquals(LocalTime.of(3, 14, 7, 12000000), TimeUtil.parseToDateTimeObject("03:14:07.012", MysqlType.DATETIME)); + assertEquals(LocalTime.of(3, 14, 7, 123000000), TimeUtil.parseToDateTimeObject("031407.123", MysqlType.DATETIME)); + + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("23:64:07.12", MysqlType.DATE); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("23:64:07.12", MysqlType.TIME); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("23:64:07.12", MysqlType.DATETIME); + }, "There is no known date-time pattern for.*"); + + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("23:14:67.12", MysqlType.DATE); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("23:14:67.12", MysqlType.TIME); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("23:14:67.12", MysqlType.DATETIME); + }, "There is no known date-time pattern for.*"); + + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("031407#12", MysqlType.DATE); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("031407#12", MysqlType.TIME); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("031407#12", MysqlType.DATETIME); + }, "There is no known date-time pattern for.*"); + + /* Duration literals */ + + assertEquals(Duration.parse("P12DT1H"), TimeUtil.parseToDateTimeObject("12 1", MysqlType.DATE)); + assertEquals(Duration.parse("P12DT1H"), TimeUtil.parseToDateTimeObject("12 01", MysqlType.DATE)); + assertEquals(Duration.parse("P12DT12H"), TimeUtil.parseToDateTimeObject("12 12", MysqlType.DATE)); + assertEquals(Duration.parse("P12DT13H4M"), TimeUtil.parseToDateTimeObject("12 13:4", MysqlType.DATE)); + assertEquals(Duration.parse("P12DT13H4M"), TimeUtil.parseToDateTimeObject("12 13:04", MysqlType.DATE)); + assertEquals(Duration.parse("P12DT13H14M"), TimeUtil.parseToDateTimeObject("12 13:14", MysqlType.DATE)); + assertEquals(Duration.parse("P12DT13H4M7S"), TimeUtil.parseToDateTimeObject("12 13:04:7", MysqlType.DATE)); + assertEquals(Duration.parse("P1DT13H4M7S"), TimeUtil.parseToDateTimeObject("1 13:04:07", MysqlType.DATE)); + assertEquals(Duration.parse("P12DT13H4M17S"), TimeUtil.parseToDateTimeObject("12 13:04:17", MysqlType.DATE)); + assertEquals(Duration.parse("P12DT1H4M1S"), TimeUtil.parseToDateTimeObject("12 1:4:1", MysqlType.DATE)); + assertEquals(Duration.parse("P12DT1H4M21S"), TimeUtil.parseToDateTimeObject("12 1:4:21", MysqlType.DATE)); + assertEquals(Duration.parse("-P12DT1H4M21S"), TimeUtil.parseToDateTimeObject("-12 1:4:21", MysqlType.DATE)); + assertEquals(Duration.parse("-P12DT1H04M17S"), TimeUtil.parseToDateTimeObject("-12 1:04:17", MysqlType.DATE)); + assertEquals(Duration.parse("P12DT1H04M17.123456789S"), TimeUtil.parseToDateTimeObject("12 1:04:17.123456789", MysqlType.DATE)); + assertEquals(Duration.parse("-PT121H4M21S"), TimeUtil.parseToDateTimeObject("-121:4:21", MysqlType.DATE)); + assertEquals(Duration.parse("-PT121H4M21.12345S"), TimeUtil.parseToDateTimeObject("-121:4:21.12345", MysqlType.DATE)); + assertEquals(Duration.parse("PT25H14M7.12S"), TimeUtil.parseToDateTimeObject("25:14:07.12", MysqlType.DATE)); + + assertEquals(Duration.parse("P12DT1H"), TimeUtil.parseToDateTimeObject("12 1", MysqlType.TIME)); + assertEquals(Duration.parse("P12DT1H"), TimeUtil.parseToDateTimeObject("12 01", MysqlType.TIME)); + assertEquals(Duration.parse("P12DT12H"), TimeUtil.parseToDateTimeObject("12 12", MysqlType.TIME)); + assertEquals(Duration.parse("P12DT13H4M"), TimeUtil.parseToDateTimeObject("12 13:4", MysqlType.TIME)); + assertEquals(Duration.parse("P12DT13H4M"), TimeUtil.parseToDateTimeObject("12 13:04", MysqlType.TIME)); + assertEquals(Duration.parse("P12DT13H14M"), TimeUtil.parseToDateTimeObject("12 13:14", MysqlType.TIME)); + assertEquals(Duration.parse("P12DT13H4M7S"), TimeUtil.parseToDateTimeObject("12 13:04:7", MysqlType.TIME)); + assertEquals(Duration.parse("P1DT13H4M7S"), TimeUtil.parseToDateTimeObject("1 13:04:07", MysqlType.TIME)); + assertEquals(Duration.parse("P12DT13H4M17S"), TimeUtil.parseToDateTimeObject("12 13:04:17", MysqlType.TIME)); + assertEquals(Duration.parse("P12DT1H4M1S"), TimeUtil.parseToDateTimeObject("12 1:4:1", MysqlType.TIME)); + assertEquals(Duration.parse("P12DT1H4M21S"), TimeUtil.parseToDateTimeObject("12 1:4:21", MysqlType.TIME)); + assertEquals(Duration.parse("-P12DT1H4M21S"), TimeUtil.parseToDateTimeObject("-12 1:4:21", MysqlType.TIME)); + assertEquals(Duration.parse("-P12DT1H04M17S"), TimeUtil.parseToDateTimeObject("-12 1:04:17", MysqlType.TIME)); + assertEquals(Duration.parse("P12DT1H04M17.123456789S"), TimeUtil.parseToDateTimeObject("12 1:04:17.123456789", MysqlType.TIME)); + assertEquals(Duration.parse("-PT121H4M21S"), TimeUtil.parseToDateTimeObject("-121:4:21", MysqlType.TIME)); + assertEquals(Duration.parse("-PT121H4M21.12345S"), TimeUtil.parseToDateTimeObject("-121:4:21.12345", MysqlType.TIME)); + assertEquals(Duration.parse("PT25H14M7.12S"), TimeUtil.parseToDateTimeObject("25:14:07.12", MysqlType.TIME)); + + assertEquals(Duration.parse("P12DT1H"), TimeUtil.parseToDateTimeObject("12 1", MysqlType.DATETIME)); + assertEquals(Duration.parse("P12DT1H"), TimeUtil.parseToDateTimeObject("12 01", MysqlType.DATETIME)); + assertEquals(Duration.parse("P12DT12H"), TimeUtil.parseToDateTimeObject("12 12", MysqlType.DATETIME)); + assertEquals(Duration.parse("P12DT13H4M"), TimeUtil.parseToDateTimeObject("12 13:4", MysqlType.DATETIME)); + assertEquals(Duration.parse("P12DT13H4M"), TimeUtil.parseToDateTimeObject("12 13:04", MysqlType.DATETIME)); + assertEquals(Duration.parse("P12DT13H14M"), TimeUtil.parseToDateTimeObject("12 13:14", MysqlType.DATETIME)); + assertEquals(Duration.parse("P12DT13H4M7S"), TimeUtil.parseToDateTimeObject("12 13:04:7", MysqlType.DATETIME)); + assertEquals(Duration.parse("P1DT13H4M7S"), TimeUtil.parseToDateTimeObject("1 13:04:07", MysqlType.DATETIME)); + assertEquals(Duration.parse("P12DT13H4M17S"), TimeUtil.parseToDateTimeObject("12 13:04:17", MysqlType.DATETIME)); + assertEquals(Duration.parse("P12DT1H4M1S"), TimeUtil.parseToDateTimeObject("12 1:4:1", MysqlType.DATETIME)); + assertEquals(Duration.parse("P12DT1H4M21S"), TimeUtil.parseToDateTimeObject("12 1:4:21", MysqlType.DATETIME)); + assertEquals(Duration.parse("-P12DT1H4M21S"), TimeUtil.parseToDateTimeObject("-12 1:4:21", MysqlType.DATETIME)); + assertEquals(Duration.parse("-P12DT1H04M17S"), TimeUtil.parseToDateTimeObject("-12 1:04:17", MysqlType.DATETIME)); + assertEquals(Duration.parse("P12DT1H04M17.123456789S"), TimeUtil.parseToDateTimeObject("12 1:04:17.123456789", MysqlType.DATETIME)); + assertEquals(Duration.parse("-PT121H4M21S"), TimeUtil.parseToDateTimeObject("-121:4:21", MysqlType.DATETIME)); + assertEquals(Duration.parse("-PT121H4M21.12345S"), TimeUtil.parseToDateTimeObject("-121:4:21.12345", MysqlType.DATETIME)); + assertEquals(Duration.parse("PT25H14M7.12S"), TimeUtil.parseToDateTimeObject("25:14:07.12", MysqlType.DATETIME)); + + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("12 25:4:21", MysqlType.TIME); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("12 2:64:21", MysqlType.TIME); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("12 2:4:61", MysqlType.TIME); + }, "There is no known date-time pattern for.*"); + + /* DATETIME literals */ + + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("2013-04-17 03:14:07", MysqlType.DATE)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("13-04-17 03:14:07", MysqlType.DATE)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("2013#04$17 03@14%07", MysqlType.DATE)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("13#04$17 03@14%07", MysqlType.DATE)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("2013#04$17 03@14%07", MysqlType.DATE)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("13#04$17 03@14%07", MysqlType.DATE)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("2013#04$17T03@14%07", MysqlType.DATE)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("13#04$17T03@14%07", MysqlType.DATE)); + assertEquals(LocalDateTime.of(2013, 4, 7, 3, 4, 7), TimeUtil.parseToDateTimeObject("2013-4-7 3:4:7", MysqlType.DATE)); + assertEquals(LocalDateTime.of(2013, 4, 7, 3, 4, 7), TimeUtil.parseToDateTimeObject("13-4-7 3:4:7", MysqlType.DATE)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("20130417031407", MysqlType.DATE)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("130417031407", MysqlType.DATE)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7, 100000000), TimeUtil.parseToDateTimeObject("2013-04-17 03:14:07.1", MysqlType.DATE)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7, 12345678), TimeUtil.parseToDateTimeObject("2013-04-17 03:14:07.012345678", MysqlType.DATE)); + + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("2013-04-17 03:14:07", MysqlType.TIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("13-04-17 03:14:07", MysqlType.TIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("2013#04$17 03@14%07", MysqlType.TIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("13#04$17 03@14%07", MysqlType.TIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("2013#04$17 03@14%07", MysqlType.TIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("13#04$17 03@14%07", MysqlType.TIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("2013#04$17T03@14%07", MysqlType.TIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("13#04$17T03@14%07", MysqlType.TIME)); + assertEquals(LocalDateTime.of(2013, 4, 7, 3, 4, 7), TimeUtil.parseToDateTimeObject("2013-4-7 3:4:7", MysqlType.TIME)); + assertEquals(LocalDateTime.of(2013, 4, 7, 3, 4, 7), TimeUtil.parseToDateTimeObject("13-4-7 3:4:7", MysqlType.TIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("20130417031407", MysqlType.TIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("130417031407", MysqlType.TIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7, 100000000), TimeUtil.parseToDateTimeObject("2013-04-17 03:14:07.1", MysqlType.TIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7, 12345678), TimeUtil.parseToDateTimeObject("2013-04-17 03:14:07.012345678", MysqlType.TIME)); + + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("2013-04-17 03:14:07", MysqlType.DATETIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("13-04-17 03:14:07", MysqlType.DATETIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("2013#04$17 03@14%07", MysqlType.DATETIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("13#04$17 03@14%07", MysqlType.DATETIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("2013#04$17 03@14%07", MysqlType.DATETIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("13#04$17 03@14%07", MysqlType.DATETIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("2013#04$17T03@14%07", MysqlType.DATETIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("13#04$17T03@14%07", MysqlType.DATETIME)); + assertEquals(LocalDateTime.of(2013, 4, 7, 3, 4, 7), TimeUtil.parseToDateTimeObject("2013-4-7 3:4:7", MysqlType.DATETIME)); + assertEquals(LocalDateTime.of(2013, 4, 7, 3, 4, 7), TimeUtil.parseToDateTimeObject("13-4-7 3:4:7", MysqlType.DATETIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("20130417031407", MysqlType.DATETIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7), TimeUtil.parseToDateTimeObject("130417031407", MysqlType.DATETIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7, 100000000), TimeUtil.parseToDateTimeObject("2013-04-17 03:14:07.1", MysqlType.DATETIME)); + assertEquals(LocalDateTime.of(2013, 4, 17, 3, 14, 7, 12345678), TimeUtil.parseToDateTimeObject("2013-04-17 03:14:07.012345678", MysqlType.DATETIME)); + + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("2013-14-17 03:14:07.1", MysqlType.DATETIME); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("2013-04-37 03:14:07.1", MysqlType.DATETIME); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("2013-04-17 33:14:07.1", MysqlType.DATETIME); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("2013-04-17 03:64:07.1", MysqlType.DATETIME); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("2013-04-17 03:14:67.1", MysqlType.DATETIME); + }, "There is no known date-time pattern for.*"); + + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("2013-04-17 03:14:07#1", MysqlType.DATE); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("2013#04$17$03@14%07", MysqlType.DATE); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("2013-04-17 03:14:07#1", MysqlType.TIME); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("2013#04$17$03@14%07", MysqlType.TIME); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("2013-04-17 03:14:07#1", MysqlType.DATETIME); + }, "There is no known date-time pattern for.*"); + assertThrows(WrongArgumentException.class, () -> { + TimeUtil.parseToDateTimeObject("2013#04$17$03@14%07", MysqlType.DATETIME); + }, "There is no known date-time pattern for.*"); + + } + } diff --git a/src/test/java/testsuite/regression/DateTimeRegressionTest.java b/src/test/java/testsuite/regression/DateTimeRegressionTest.java new file mode 100644 index 000000000..0543d7f13 --- /dev/null +++ b/src/test/java/testsuite/regression/DateTimeRegressionTest.java @@ -0,0 +1,743 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 2.0, as published by the + * Free Software Foundation. + * + * This program is also distributed with certain software (including but not + * limited to OpenSSL) that is licensed under separate terms, as designated in a + * particular file or component or in included license documentation. The + * authors of MySQL hereby grant you an additional permission to link the + * program and your derivative works with the separately licensed software that + * they have included with MySQL. + * + * Without limiting anything contained in the foregoing, this file, which is + * part of MySQL Connector/J, is also subject to the Universal FOSS Exception, + * version 1.0, a copy of which can be found at + * http://oss.oracle.com/licenses/universal-foss-exception. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, + * for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package testsuite.regression; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Properties; + +import org.junit.jupiter.api.Test; + +import com.mysql.cj.MysqlType; +import com.mysql.cj.conf.PropertyKey; + +import testsuite.BaseTestCase; + +public class DateTimeRegressionTest extends BaseTestCase { + + /** + * Tests fix for Bug#20391832, SETOBJECT() FOR TYPES.TIME RESULTS IN EXCEPTION WHEN VALUE HAS FRACTIONAL PART. + * + * @throws Exception + */ + @Test + public void testBug20391832() throws Exception { + createTable("testBug20391832", "(v varchar(40))"); + + boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions + + Properties props = new Properties(); + props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), "LOCAL"); + for (boolean useSSPS : new boolean[] { false, true }) { + for (boolean sendFr : new boolean[] { false, true }) { + + System.out.println("useServerPrepStmts=" + useSSPS + "; sendFractSeconds=" + sendFr); + + props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS); + props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFr); + Connection testConn = getConnectionWithProps(timeZoneFreeDbUrl, props); + + this.pstmt = testConn.prepareStatement("insert into testBug20391832 values(?)"); + + for (MysqlType type : new MysqlType[] { MysqlType.DATETIME, MysqlType.TIMESTAMP }) { + subTestBug20391832(props, type, "2038-01-19", "2038-01-19 00:00:00"); + subTestBug20391832(props, type, "38-01-19", "2038-01-19 00:00:00"); + subTestBug20391832(props, type, "2038#01$19", "2038-01-19 00:00:00"); + subTestBug20391832(props, type, "38#01$19", "2038-01-19 00:00:00"); + subTestBug20391832(props, type, "20380119", "2038-01-19 00:00:00"); + subTestBug20391832(props, type, "380119", "2038-01-19 00:00:00"); + subTestBug20391832(props, type, "030417", "2003-04-17 00:00:00"); // resolved as a DATE literal + + assertThrows(SQLException.class, ".* Conversion from java.time.Duration to " + type + " is not supported.", () -> { + subTestBug20391832(props, type, "12 1", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.Duration to " + type + " is not supported.", () -> { + subTestBug20391832(props, type, "12 13:04", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.Duration to " + type + " is not supported.", () -> { + subTestBug20391832(props, type, "12 1:4:1", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.Duration to " + type + " is not supported.", () -> { + subTestBug20391832(props, type, "12 1:04:17.123456789", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.Duration to " + type + " is not supported.", () -> { + subTestBug20391832(props, type, "-838:59:59", null); + return null; + }); + + assertThrows(SQLException.class, ".* Conversion from java.time.LocalTime to " + type + " is not supported.", () -> { + subTestBug20391832(props, type, "13:04:17", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalTime to " + type + " is not supported.", () -> { + subTestBug20391832(props, type, "13:04", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalTime to " + type + " is not supported.", () -> { + subTestBug20391832(props, type, "0417", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalTime to " + type + " is not supported.", () -> { + subTestBug20391832(props, type, "03:14:07.012", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalTime to " + type + " is not supported.", () -> { + subTestBug20391832(props, type, "031407.123", null); + return null; + }); + + assertThrows(SQLException.class, ".* There is no known date-time pattern for.*", () -> { + subTestBug20391832(props, type, "031407#12", null); // wrong delimiter + return null; + }); + + subTestBug20391832(props, type, "2038-01-19 03:14:07", "2038-01-19 03:14:07"); + if (withFract) { + subTestBug20391832(props, type, "2038-01-19 03:14:07.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038-01-19 03:14:07.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038-01-19 03:14:07.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038-01-19 03:14:07.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038-01-19 03:14:07.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038-01-19 03:14:07.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038-01-19 03:14:07.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038-01-19 03:14:07.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038-01-19 03:14:07.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + } + + subTestBug20391832(props, type, "38-01-19 03:14:07", "2038-01-19 03:14:07"); + if (withFract) { + subTestBug20391832(props, type, "38-01-19 03:14:07.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38-01-19 03:14:07.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38-01-19 03:14:07.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38-01-19 03:14:07.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38-01-19 03:14:07.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38-01-19 03:14:07.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38-01-19 03:14:07.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38-01-19 03:14:07.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38-01-19 03:14:07.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + } + + subTestBug20391832(props, type, "2038#01$19 03@14%07", "2038-01-19 03:14:07"); + if (withFract) { + subTestBug20391832(props, type, "2038#01$19 03@14%07.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19 03@14%07.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19 03@14%07.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19 03@14%07.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19 03@14%07.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19 03@14%07.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19 03@14%07.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19 03@14%07.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19 03@14%07.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + } + + subTestBug20391832(props, type, "38#01$19 03@14%07", "2038-01-19 03:14:07"); + if (withFract) { + subTestBug20391832(props, type, "38#01$19 03@14%07.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19 03@14%07.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19 03@14%07.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19 03@14%07.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19 03@14%07.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19 03@14%07.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19 03@14%07.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19 03@14%07.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19 03@14%07.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + } + + subTestBug20391832(props, type, "2038#01$19T03@14%07", "2038-01-19 03:14:07"); + if (withFract) { + subTestBug20391832(props, type, "2038#01$19T03@14%07.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19T03@14%07.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19T03@14%07.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19T03@14%07.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19T03@14%07.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19T03@14%07.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19T03@14%07.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19T03@14%07.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#01$19T03@14%07.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + } + + subTestBug20391832(props, type, "38#01$19T03@14%07", "2038-01-19 03:14:07"); + if (withFract) { + subTestBug20391832(props, type, "38#01$19T03@14%07.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19T03@14%07.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19T03@14%07.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19T03@14%07.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19T03@14%07.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19T03@14%07.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19T03@14%07.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19T03@14%07.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "38#01$19T03@14%07.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + } + + subTestBug20391832(props, type, "2038#1$19 3@14%7", "2038-01-19 03:14:07"); + if (withFract) { + subTestBug20391832(props, type, "2038#1$19T3@14%7.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#1$19 3@14%7.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#1$19T3@14%7.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#1$19 3@14%7.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#1$19T3@14%7.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#1$19 3@14%7.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#1$19T3@14%7.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#1$19 3@14%7.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "2038#1$19T3@14%7.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + } + + subTestBug20391832(props, type, "38#1$9 3@4%7", "2038-01-09 03:04:07"); + if (withFract) { + subTestBug20391832(props, type, "38#1$9T3@4%7.1", sendFr ? "2038-01-09 03:04:07.1" : "2038-01-09 03:04:07"); + subTestBug20391832(props, type, "38#1$9T3@4%7.01", sendFr ? "2038-01-09 03:04:07.01" : "2038-01-09 03:04:07"); + subTestBug20391832(props, type, "38#1$9T3@4%7.012", sendFr ? "2038-01-09 03:04:07.012" : "2038-01-09 03:04:07"); + subTestBug20391832(props, type, "38#1$9T3@4%7.0123", sendFr ? "2038-01-09 03:04:07.0123" : "2038-01-09 03:04:07"); + subTestBug20391832(props, type, "38#1$9T3@4%7.01234", sendFr ? "2038-01-09 03:04:07.01234" : "2038-01-09 03:04:07"); + subTestBug20391832(props, type, "38#1$9T3@4%7.012345", sendFr ? "2038-01-09 03:04:07.012345" : "2038-01-09 03:04:07"); + subTestBug20391832(props, type, "38#1$9T3@4%7.0123456", sendFr ? "2038-01-09 03:04:07.012346" : "2038-01-09 03:04:07"); + subTestBug20391832(props, type, "38#1$9T3@4%7.01234567", sendFr ? "2038-01-09 03:04:07.012346" : "2038-01-09 03:04:07"); + subTestBug20391832(props, type, "38#1$9T3@4%7.012345678", sendFr ? "2038-01-09 03:04:07.012346" : "2038-01-09 03:04:07"); + } + + subTestBug20391832(props, type, "20380119031407", "2038-01-19 03:14:07"); + if (withFract) { + subTestBug20391832(props, type, "20380119031407.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "20380119031407.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "20380119031407.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "20380119031407.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "20380119031407.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "20380119031407.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "20380119031407.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "20380119031407.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "20380119031407.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + } + + subTestBug20391832(props, type, "380119031407", "2038-01-19 03:14:07"); + if (withFract) { + subTestBug20391832(props, type, "380119031407.1", sendFr ? "2038-01-19 03:14:07.1" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "380119031407.01", sendFr ? "2038-01-19 03:14:07.01" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "380119031407.012", sendFr ? "2038-01-19 03:14:07.012" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "380119031407.0123", sendFr ? "2038-01-19 03:14:07.0123" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "380119031407.01234", sendFr ? "2038-01-19 03:14:07.01234" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "380119031407.012345", sendFr ? "2038-01-19 03:14:07.012345" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "380119031407.0123456", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "380119031407.01234567", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + subTestBug20391832(props, type, "380119031407.012345678", sendFr ? "2038-01-19 03:14:07.012346" : "2038-01-19 03:14:07"); + } + } + + // ================================================ + + subTestBug20391832(props, MysqlType.TIME, "12 1", "289:0:0"); + subTestBug20391832(props, MysqlType.TIME, "-12 1", "-289:0:0"); + subTestBug20391832(props, MysqlType.TIME, "12 12:04", "300:4:0"); + subTestBug20391832(props, MysqlType.TIME, "-12 12:04", "-300:4:0"); + subTestBug20391832(props, MysqlType.TIME, "12 1:4:1", "289:4:1"); + subTestBug20391832(props, MysqlType.TIME, "-12 1:4:7", "-289:4:7"); + if (withFract) { + subTestBug20391832(props, MysqlType.TIME, "5 1:04:17.123456789", sendFr ? "121:4:17.123457" : "121:4:17"); + subTestBug20391832(props, MysqlType.TIME, "-5 1:04:17.123456789", sendFr ? "-121:4:17.123457" : "-121:4:17"); + } + subTestBug20391832(props, MysqlType.TIME, "25:59:59", "25:59:59"); + subTestBug20391832(props, MysqlType.TIME, "-838:59:59", "-838:59:59"); + + assertThrows(SQLException.class, ".* Conversion from java.time.LocalDate to TIME is not supported.", () -> { + subTestBug20391832(props, MysqlType.TIME, "2038-01-19", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalDate to TIME is not supported.", () -> { + subTestBug20391832(props, MysqlType.TIME, "38-01-19", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalDate to TIME is not supported.", () -> { + subTestBug20391832(props, MysqlType.TIME, "2038#01$19", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalDate to TIME is not supported.", () -> { + subTestBug20391832(props, MysqlType.TIME, "38#01$19", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalDate to TIME is not supported.", () -> { + subTestBug20391832(props, MysqlType.TIME, "20380119", null); + return null; + }); + subTestBug20391832(props, MysqlType.TIME, "120119", "12:01:19"); + subTestBug20391832(props, MysqlType.TIME, "13:04:17", "13:04:17"); + subTestBug20391832(props, MysqlType.TIME, "13:04", "13:04:00"); + subTestBug20391832(props, MysqlType.TIME, "130417", "13:04:17"); + subTestBug20391832(props, MysqlType.TIME, "0417", "00:04:17"); + subTestBug20391832(props, MysqlType.TIME, "17", "00:00:17"); + if (withFract) { + subTestBug20391832(props, MysqlType.TIME, "0417.1234567", sendFr ? "00:04:17.123457" : "00:04:17"); + subTestBug20391832(props, MysqlType.TIME, "17.1234567", sendFr ? "00:00:17.123457" : "00:00:17"); + subTestBug20391832(props, MysqlType.TIME, "03:14:07.1", sendFr ? "03:14:07.1" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "03:14:07.01", sendFr ? "03:14:07.01" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "03:14:07.012", sendFr ? "03:14:07.012" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "03:14:07.0123", sendFr ? "03:14:07.0123" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "3:14:07.0123", sendFr ? "03:14:07.0123" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "03:14:07.01234", sendFr ? "03:14:07.01234" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "03:4:07.012345", sendFr ? "03:04:07.012345" : "03:04:07"); + subTestBug20391832(props, MysqlType.TIME, "03:14:07.012345", sendFr ? "03:14:07.012345" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "3:4:7.0123456", sendFr ? "03:04:07.012346" : "03:04:07"); + subTestBug20391832(props, MysqlType.TIME, "03:14:7.0123456", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "03:14:07.0123456", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "03:14:07.01234567", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "03:14:07.012345678", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "031407.123", sendFr ? "03:14:07.123" : "03:14:07"); + } + + assertThrows(SQLException.class, ".* There is no known date-time pattern for.*", () -> { + subTestBug20391832(props, MysqlType.TIME, "031407#12", null); // wrong delimiter + return null; + }); + + subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07", "03:14:07"); + if (withFract) { + subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.1", sendFr ? "03:14:07.1" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.01", sendFr ? "03:14:07.01" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.012", sendFr ? "03:14:07.012" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.0123", sendFr ? "03:14:07.0123" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.01234", sendFr ? "03:14:07.01234" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.012345", sendFr ? "03:14:07.012345" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.0123456", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.01234567", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038-01-19 03:14:07.012345678", sendFr ? "03:14:07.012346" : "03:14:07"); + } + + subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07", "03:14:07"); + if (withFract) { + subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.1", sendFr ? "03:14:07.1" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.01", sendFr ? "03:14:07.01" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.012", sendFr ? "03:14:07.012" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.0123", sendFr ? "03:14:07.0123" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.01234", sendFr ? "03:14:07.01234" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.012345", sendFr ? "03:14:07.012345" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.0123456", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.01234567", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38-01-19 03:14:07.012345678", sendFr ? "03:14:07.012346" : "03:14:07"); + } + + subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07", "03:14:07"); + if (withFract) { + subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.1", sendFr ? "03:14:07.1" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.01", sendFr ? "03:14:07.01" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.012", sendFr ? "03:14:07.012" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.0123", sendFr ? "03:14:07.0123" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.01234", sendFr ? "03:14:07.01234" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.012345", sendFr ? "03:14:07.012345" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.0123456", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.01234567", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19 03@14%07.012345678", sendFr ? "03:14:07.012346" : "03:14:07"); + } + + subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07", "03:14:07"); + if (withFract) { + subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.1", sendFr ? "03:14:07.1" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.01", sendFr ? "03:14:07.01" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.012", sendFr ? "03:14:07.012" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.0123", sendFr ? "03:14:07.0123" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.01234", sendFr ? "03:14:07.01234" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.012345", sendFr ? "03:14:07.012345" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.0123456", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.01234567", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19 03@14%07.012345678", sendFr ? "03:14:07.012346" : "03:14:07"); + } + + subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07", "03:14:07"); + if (withFract) { + subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.1", sendFr ? "03:14:07.1" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.01", sendFr ? "03:14:07.01" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.012", sendFr ? "03:14:07.012" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.0123", sendFr ? "03:14:07.0123" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.01234", sendFr ? "03:14:07.01234" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.012345", sendFr ? "03:14:07.012345" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.0123456", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.01234567", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "2038#01$19T03@14%07.012345678", sendFr ? "03:14:07.012346" : "03:14:07"); + } + + subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07", "03:14:07"); + if (withFract) { + subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.1", sendFr ? "03:14:07.1" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.01", sendFr ? "03:14:07.01" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.012", sendFr ? "03:14:07.012" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.0123", sendFr ? "03:14:07.0123" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.01234", sendFr ? "03:14:07.01234" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.012345", sendFr ? "03:14:07.012345" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.0123456", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.01234567", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#01$19T03@14%07.012345678", sendFr ? "03:14:07.012346" : "03:14:07"); + } + + subTestBug20391832(props, MysqlType.TIME, "2038#1$19 3@14%7", "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#1$19 3@14%7", "03:14:07"); + if (withFract) { + subTestBug20391832(props, MysqlType.TIME, "38#1$19T3@14%7.1", sendFr ? "03:14:07.1" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#1$19 3@14%7.01", sendFr ? "03:14:07.01" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#1$19T3@14%7.012", sendFr ? "03:14:07.012" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#1$19T3@14%7.0123", sendFr ? "03:14:07.0123" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#1$19T3@14%7.01234", sendFr ? "03:14:07.01234" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#1$19 3@14%7.012345", sendFr ? "03:14:07.012345" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#1$19T3@14%7.0123456", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#1$19T3@14%7.01234567", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "38#1$19 3@14%7.012345678", sendFr ? "03:14:07.012346" : "03:14:07"); + } + + subTestBug20391832(props, MysqlType.TIME, "20380119031407", "03:14:07"); + if (withFract) { + subTestBug20391832(props, MysqlType.TIME, "20380119031407.1", sendFr ? "03:14:07.1" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "20380119031407.01", sendFr ? "03:14:07.01" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "20380119031407.012", sendFr ? "03:14:07.012" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "20380119031407.0123", sendFr ? "03:14:07.0123" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "20380119031407.01234", sendFr ? "03:14:07.01234" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "20380119031407.012345", sendFr ? "03:14:07.012345" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "20380119031407.0123456", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "20380119031407.01234567", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "20380119031407.012345678", sendFr ? "03:14:07.012346" : "03:14:07"); + } + + subTestBug20391832(props, MysqlType.TIME, "380119031407", "03:14:07"); + if (withFract) { + subTestBug20391832(props, MysqlType.TIME, "380119031407.1", sendFr ? "03:14:07.1" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "380119031407.01", sendFr ? "03:14:07.01" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "380119031407.012", sendFr ? "03:14:07.012" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "380119031407.0123", sendFr ? "03:14:07.0123" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "380119031407.01234", sendFr ? "03:14:07.01234" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "380119031407.012345", sendFr ? "03:14:07.012345" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "380119031407.0123456", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "380119031407.01234567", sendFr ? "03:14:07.012346" : "03:14:07"); + subTestBug20391832(props, MysqlType.TIME, "380119031407.012345678", sendFr ? "03:14:07.012346" : "03:14:07"); + } + + // ================================================ + + subTestBug20391832(props, MysqlType.DATE, "2038-01-19", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38-01-19", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038-1-9", "2038-01-09"); + subTestBug20391832(props, MysqlType.DATE, "38-1-9", "2038-01-09"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "20380119", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "380119", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "030417", "2003-04-17"); + + assertThrows(SQLException.class, ".* Conversion from java.time.Duration to DATE is not supported.", () -> { + subTestBug20391832(props, MysqlType.DATE, "12 1", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.Duration to DATE is not supported.", () -> { + subTestBug20391832(props, MysqlType.DATE, "12 13:04", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.Duration to DATE is not supported.", () -> { + subTestBug20391832(props, MysqlType.DATE, "12 1:4:1", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.Duration to DATE is not supported.", () -> { + subTestBug20391832(props, MysqlType.DATE, "12 1:04:17.123456789", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.Duration to DATE is not supported.", () -> { + subTestBug20391832(props, MysqlType.DATE, "-838:59:59", null); + return null; + }); + + assertThrows(SQLException.class, ".* Conversion from java.time.LocalTime to DATE is not supported.", () -> { + subTestBug20391832(props, MysqlType.DATE, "13:04:17", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalTime to DATE is not supported.", () -> { + subTestBug20391832(props, MysqlType.DATE, "13:04", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalTime to DATE is not supported.", () -> { + subTestBug20391832(props, MysqlType.DATE, "0417", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalTime to DATE is not supported.", () -> { + subTestBug20391832(props, MysqlType.DATE, "03:14:07.012", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalTime to DATE is not supported.", () -> { + subTestBug20391832(props, MysqlType.DATE, "031407.123", null); + return null; + }); + + assertThrows(SQLException.class, ".* There is no known date-time pattern for.*", () -> { + subTestBug20391832(props, MysqlType.DATE, "031407#12", null); // wrong delimiter + return null; + }); + + subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.1", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.12", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.012", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.0123", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.01234", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.012345", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.0123456", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.01234567", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038-01-19 03:14:07.012345678", "2038-01-19"); + + subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.1", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.12", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.012", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.0123", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.01234", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.012345", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.0123456", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.01234567", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38-01-19 03:14:07.012345678", "2038-01-19"); + + subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.1", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.12", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.012", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.0123", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.01234", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.012345", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.0123456", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.01234567", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19 03@14%07.012345678", "2038-01-19"); + + subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.1", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.12", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.012", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.0123", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.01234", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.012345", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.0123456", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.01234567", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19 03@14%07.012345678", "2038-01-19"); + + subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.1", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.12", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.012", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.0123", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.01234", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.012345", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.0123456", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.01234567", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "2038#01$19T03@14%07.012345678", "2038-01-19"); + + subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.1", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.12", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.012", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.0123", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.01234", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.012345", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.0123456", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.01234567", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#01$19T03@14%07.012345678", "2038-01-19"); + + subTestBug20391832(props, MysqlType.DATE, "2038#1$19 3@14%7", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "38#1$9 3@4%7", "2038-01-09"); + + subTestBug20391832(props, MysqlType.DATE, "20380119031407", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "20380119031407.1", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "20380119031407.01", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "20380119031407.012", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "20380119031407.0123", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "20380119031407.01234", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "20380119031407.012345", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "20380119031407.0123456", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "20380119031407.01234567", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "20380119031407.012345678", "2038-01-19"); + + subTestBug20391832(props, MysqlType.DATE, "380119031407", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "380119031407.1", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "380119031407.01", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "380119031407.012", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "380119031407.0123", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "380119031407.01234", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "380119031407.012345", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "380119031407.0123456", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "380119031407.01234567", "2038-01-19"); + subTestBug20391832(props, MysqlType.DATE, "380119031407.012345678", "2038-01-19"); + + // ================================================ + + subTestBug20391832(props, MysqlType.YEAR, "2038-01-19", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38-01-19", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "20380119", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "380119", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "030417", "2003"); + + assertThrows(SQLException.class, ".* Conversion from java.time.LocalTime to YEAR is not supported.", () -> { + subTestBug20391832(props, MysqlType.YEAR, "13:04:17", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalTime to YEAR is not supported.", () -> { + subTestBug20391832(props, MysqlType.YEAR, "13:04", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalTime to YEAR is not supported.", () -> { + subTestBug20391832(props, MysqlType.YEAR, "0417", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalTime to YEAR is not supported.", () -> { + subTestBug20391832(props, MysqlType.YEAR, "03:14:07.012", null); + return null; + }); + assertThrows(SQLException.class, ".* Conversion from java.time.LocalTime to YEAR is not supported.", () -> { + subTestBug20391832(props, MysqlType.YEAR, "031407.123", null); + return null; + }); + + assertThrows(SQLException.class, ".* There is no known date-time pattern for.*", () -> { + subTestBug20391832(props, MysqlType.YEAR, "031407#12", null); // wrong delimiter + return null; + }); + + assertThrows(SQLException.class, ".* Conversion from java.time.Duration to YEAR is not supported.", () -> { + subTestBug20391832(props, MysqlType.YEAR, "-838:59:59", null); + return null; + }); + + subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.1", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.12", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.012", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.0123", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.01234", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.012345", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.0123456", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.01234567", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.012345678", "2038"); + + assertThrows(SQLException.class, ".* There is no known date-time pattern for.*", () -> { + subTestBug20391832(props, MysqlType.YEAR, "2038-01-19 03:14:07.0123456789", null); // nanos part is too long + return null; + }); + + subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.1", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.12", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.012", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.0123", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.01234", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.012345", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.0123456", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.01234567", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38-01-19 03:14:07.012345678", "2038"); + + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.1", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.12", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.012", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.0123", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.01234", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.012345", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.0123456", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.01234567", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19 03@14%07.012345678", "2038"); + + subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.1", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.12", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.012", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.0123", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.01234", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.012345", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.0123456", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.01234567", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19 03@14%07.012345678", "2038"); + + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.1", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.12", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.012", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.0123", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.01234", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.012345", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.0123456", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.01234567", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "2038#01$19T03@14%07.012345678", "2038"); + + subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.1", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.12", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.012", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.0123", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.01234", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.012345", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.0123456", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.01234567", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#01$19T03@14%07.012345678", "2038"); + + subTestBug20391832(props, MysqlType.YEAR, "2038#1$19 3@14%7", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "38#1$9 3@4%7", "2038"); + + subTestBug20391832(props, MysqlType.YEAR, "20380119031407", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "20380119031407.1", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "20380119031407.01", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "20380119031407.012", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "20380119031407.0123", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "20380119031407.01234", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "20380119031407.012345", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "20380119031407.0123456", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "20380119031407.01234567", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "20380119031407.012345678", "2038"); + + subTestBug20391832(props, MysqlType.YEAR, "380119031407", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "380119031407.1", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "380119031407.01", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "380119031407.012", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "380119031407.0123", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "380119031407.01234", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "380119031407.012345", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "380119031407.0123456", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "380119031407.01234567", "2038"); + subTestBug20391832(props, MysqlType.YEAR, "380119031407.012345678", "2038"); + + testConn.close(); + } + } + } + + private void subTestBug20391832(Properties props, MysqlType targetType, String val, String exp) throws Exception { + boolean useSSPS = Boolean.parseBoolean(props.getProperty(PropertyKey.useServerPrepStmts.getKeyName())); + + this.pstmt.setObject(1, val, targetType); + String query = this.pstmt.toString().replace(".0)", ")"); + + assertEquals((useSSPS ? "com.mysql.cj.jdbc.ServerPreparedStatement[1]: " : "com.mysql.cj.jdbc.ClientPreparedStatement: ") + + "insert into testBug20391832 values(" + (targetType == MysqlType.YEAR ? exp : "'" + exp + "'") + ")", query); + } + +} diff --git a/src/test/java/testsuite/simple/DateTimeTest.java b/src/test/java/testsuite/simple/DateTimeTest.java index 975eb7980..75d069505 100644 --- a/src/test/java/testsuite/simple/DateTimeTest.java +++ b/src/test/java/testsuite/simple/DateTimeTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 2.0, as published by the @@ -41,6 +41,7 @@ import java.sql.Statement; import java.sql.Time; import java.sql.Timestamp; +import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; @@ -3170,6 +3171,121 @@ public void testZonedDatetimeSetters() throws Exception { } } + @Test + public void testDurationSetters() throws Exception { + boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions + + createTable(tYear, "(id INT, d YEAR)"); + createTable(tDate, "(id INT, d DATE)"); + createTable(tTime, withFract ? "(id INT, d TIME(6))" : "(id INT, d TIME)"); + createTable(tDatetime, withFract ? "(id INT, d DATETIME(6))" : "(id INT, d DATETIME)"); + createTable(tTimestamp, withFract ? "(id INT, d TIMESTAMP(6))" : "(id INT, d TIMESTAMP)"); + createTable(tVarchar, "(id INT, d VARCHAR(30))"); + + id = 0; + + Properties props = new Properties(); + props.setProperty(PropertyKey.cacheDefaultTimeZone.getKeyName(), "false"); + + for (TimeZone senderTz : this.senderTimeZones) { + try { + for (String connectionTZ : this.connectionTimeZones) { + initConnections(senderTz, connectionTZ); + + for (boolean forceConnectionTimeZoneToSession : new boolean[] { false, true }) { + for (boolean preserveInstants : new boolean[] { false, true }) { + for (boolean useSSPS : new boolean[] { false, true }) { + for (boolean sendFractionalSeconds : new boolean[] { false, true }) { + for (boolean sendTimeFract : new boolean[] { false, true }) { + + System.out.println("connTimeZone=" + connectionTZ + "; forceConnTimeZoneToSession=" + forceConnectionTimeZoneToSession + + "; preserveInstants=" + preserveInstants + "; useServerPrepStmts=" + useSSPS + "; sendFractSeconds=" + + sendFractionalSeconds + "; sendFractSecondsForTime=" + sendTimeFract); + + if (connectionTZ == null) { + props.remove(PropertyKey.connectionTimeZone.getKeyName()); + } else { + props.setProperty(PropertyKey.connectionTimeZone.getKeyName(), connectionTZ); + } + props.setProperty(PropertyKey.forceConnectionTimeZoneToSession.getKeyName(), "" + forceConnectionTimeZoneToSession); + props.setProperty(PropertyKey.preserveInstants.getKeyName(), "" + preserveInstants); + props.setProperty(PropertyKey.useServerPrepStmts.getKeyName(), "" + useSSPS); + props.setProperty(PropertyKey.sendFractionalSecondsForTime.getKeyName(), "" + sendTimeFract); + props.setProperty(PropertyKey.sendFractionalSeconds.getKeyName(), "" + sendFractionalSeconds); + + Duration dur_no_fract = Duration.parse("PT300H10M20S"); + Duration dur_with_fract = Duration.parse("PT300H10M20.123S"); + Duration dur = withFract ? dur_with_fract : dur_no_fract; + String expDur = TimeUtil.getDurationString(sendFractionalSeconds ? dur : dur_no_fract); + String expTime = TimeUtil.getDurationString(dur); + + Duration neg_dur_no_fract = Duration.parse("-PT300H10M20S"); + Duration neg_dur = withFract ? Duration.parse("-PT300H10M20.123S") : neg_dur_no_fract; + String expNegDur = TimeUtil.getDurationString(sendFractionalSeconds ? neg_dur : neg_dur_no_fract); + String expNegTime = TimeUtil.getDurationString(neg_dur); + + /* Unsupported conversions */ + + assertThrows(props, tVarchar, dur, MysqlType.DATE, senderTz, + ".* Conversion from java.time.Duration to DATE is not supported."); + assertThrows(props, tVarchar, dur, MysqlType.DATETIME, senderTz, + ".* Conversion from java.time.Duration to DATETIME is not supported."); + assertThrows(props, tVarchar, dur, MysqlType.TIMESTAMP, senderTz, + ".* Conversion from java.time.Duration to TIMESTAMP is not supported."); + assertThrows(props, tVarchar, dur, MysqlType.YEAR, senderTz, + ".* Conversion from java.time.Duration to YEAR is not supported."); + + /* Into TIME field */ + + setObjectFromTz(props, tTime, dur, null, senderTz, expDur); + setObjectFromTz(props, tTime, neg_dur, null, senderTz, expNegDur); + setObjectFromTz(props, tTime, dur, MysqlType.TIME, senderTz, expDur); + setObjectFromTz(props, tTime, neg_dur, MysqlType.TIME, senderTz, expNegDur); + setObjectFromTz(props, tTime, dur, MysqlType.CHAR, senderTz, expTime); + setObjectFromTz(props, tTime, neg_dur, MysqlType.CHAR, senderTz, expNegTime); + setObjectFromTz(props, tTime, dur, MysqlType.VARCHAR, senderTz, expTime); + setObjectFromTz(props, tTime, dur, MysqlType.TINYTEXT, senderTz, expTime); + setObjectFromTz(props, tTime, dur, MysqlType.TEXT, senderTz, expTime); + setObjectFromTz(props, tTime, dur, MysqlType.MEDIUMTEXT, senderTz, expTime); + setObjectFromTz(props, tTime, dur, MysqlType.LONGTEXT, senderTz, expTime); + + /* Into VARCHAR field */ + + String expChar = expDur + (withFract && sendFractionalSeconds && useSSPS ? "000" : ""); + String expChar2 = useSSPS ? TimeUtil.getDurationString(dur_no_fract) : expChar; // TODO milliseconds are ignored by server. Bug ? + String expNegChar = expNegDur + (withFract && sendFractionalSeconds && useSSPS ? "000" : ""); + String expNegChar2 = useSSPS ? TimeUtil.getDurationString(neg_dur_no_fract) : expNegChar; // TODO milliseconds are ignored by server. Bug ? + + setObjectFromTz(props, tVarchar, dur, null, senderTz, expChar2); + setObjectFromTz(props, tVarchar, neg_dur, null, senderTz, expNegChar2); + setObjectFromTz(props, tVarchar, dur, MysqlType.TIME, senderTz, expChar2); + setObjectFromTz(props, tVarchar, neg_dur, MysqlType.TIME, senderTz, expNegChar2); + setObjectFromTz(props, tVarchar, dur, MysqlType.CHAR, senderTz, expTime); + setObjectFromTz(props, tVarchar, neg_dur, MysqlType.CHAR, senderTz, expNegTime); + setObjectFromTz(props, tVarchar, dur, MysqlType.VARCHAR, senderTz, expTime); + setObjectFromTz(props, tVarchar, neg_dur, MysqlType.VARCHAR, senderTz, expNegTime); + setObjectFromTz(props, tVarchar, dur, MysqlType.TINYTEXT, senderTz, expTime); + setObjectFromTz(props, tVarchar, neg_dur, MysqlType.TINYTEXT, senderTz, expNegTime); + setObjectFromTz(props, tVarchar, dur, MysqlType.TEXT, senderTz, expTime); + setObjectFromTz(props, tVarchar, neg_dur, MysqlType.TEXT, senderTz, expNegTime); + setObjectFromTz(props, tVarchar, dur, MysqlType.MEDIUMTEXT, senderTz, expTime); + setObjectFromTz(props, tVarchar, neg_dur, MysqlType.MEDIUMTEXT, senderTz, expNegTime); + setObjectFromTz(props, tVarchar, dur, MysqlType.LONGTEXT, senderTz, expTime); + setObjectFromTz(props, tVarchar, neg_dur, MysqlType.LONGTEXT, senderTz, expNegTime); + + } + } + } + } + } + closeConnections(); + } + } finally { + closeConnections(); + } + } + } + void assertThrows(Properties props, String tableName, Object parameter, SQLType targetSqlType, TimeZone senderTz, String err) throws Exception { assertThrows(SQLException.class, err, new Callable() { public Void call() throws Exception { @@ -3354,6 +3470,12 @@ public void testDateGetters() throws Exception { assertEquals(ldt_20200101_0000.atZone(currZoneId).toOffsetDateTime(), this.rs.getObject(1, OffsetDateTime.class)); assertEquals(ldt_20200101_0000.atZone(currZoneId), this.rs.getObject(1, ZonedDateTime.class)); assertEquals(s_20200101, this.rs.getString(1)); + assertThrows(SQLException.class, + Messages.getString("ResultSet.UnsupportedConversion", new Object[] { "DATE", Duration.class.getName() }), + () -> { + this.rs.getObject(1, Duration.class); + return null; + }); assertTrue(this.rs.next()); exp_instant_tz = ldt_20191231_0000.atZone(currZoneId).toInstant(); @@ -3403,8 +3525,13 @@ public void testDateGetters() throws Exception { public void testTimeGetters() throws Exception { boolean withFract = versionMeetsMinimum(5, 6, 4); // fractional seconds are not supported in previous versions + String dur1 = withFract ? "300:10:20.012300" : "300:10:20"; + String dur2 = withFract ? "-300:10:20.012300" : "-300:10:20"; + createTable(tTime, withFract ? "(d TIME(6))" : "(d TIME)"); this.stmt.executeUpdate("INSERT INTO " + tTime + " VALUES ('" + lt_120000_123456.toString() + "')"); + this.stmt.executeUpdate("INSERT INTO " + tTime + " VALUES ('" + dur1 + "')"); + this.stmt.executeUpdate("INSERT INTO " + tTime + " VALUES ('" + dur2 + "')"); Calendar cal_05 = GregorianCalendar.getInstance(tz_plus_05_00); @@ -3475,6 +3602,134 @@ public void testTimeGetters() throws Exception { assertEquals(exp_ldt.atZone(currZoneId), this.rs.getObject(1, ZonedDateTime.class)); assertEquals(withFract ? s_120000_123456 : s_120000, this.rs.getString(1)); + assertTrue(this.rs.next()); + assertEquals(java.sql.Date.valueOf(ld_19700101), this.rs.getDate(1)); + assertEquals(java.sql.Date.valueOf(ld_19700101), this.rs.getDate(1, cal_05)); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> { + this.rs.getTime(1); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> { + this.rs.getTime(1, cal_05); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> { + this.rs.getTimestamp(1); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> { + this.rs.getTimestamp(1, cal_05); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> { + this.rs.getObject(1); + return null; + }); + assertEquals(java.sql.Date.valueOf(ld_19700101), this.rs.getObject(1, java.sql.Date.class)); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> { + this.rs.getObject(1, java.sql.Time.class); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> { + this.rs.getObject(1, java.sql.Timestamp.class); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> { + this.rs.getObject(1, java.util.Date.class); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> { + this.rs.getObject(1, java.util.Calendar.class); + return null; + }); + assertEquals(ld_19700101, this.rs.getObject(1, LocalDate.class)); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> { + this.rs.getObject(1, LocalTime.class); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> { + this.rs.getObject(1, LocalDateTime.class); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> { + this.rs.getObject(1, OffsetTime.class); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> { + this.rs.getObject(1, OffsetDateTime.class); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur1 }), () -> { + this.rs.getObject(1, ZonedDateTime.class); + return null; + }); + assertEquals(Duration.parse(withFract ? "PT300H10M20.123S" : "PT300H10M20S"), this.rs.getObject(1, Duration.class)); + assertEquals(dur1, this.rs.getString(1)); + + assertTrue(this.rs.next()); + assertEquals(java.sql.Date.valueOf(ld_19700101), this.rs.getDate(1)); + assertEquals(java.sql.Date.valueOf(ld_19700101), this.rs.getDate(1, cal_05)); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> { + DateTimeTest.this.rs.getTime(1); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> { + this.rs.getTime(1, cal_05); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> { + this.rs.getTimestamp(1); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> { + this.rs.getTimestamp(1, cal_05); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> { + this.rs.getObject(1); + return null; + }); + assertEquals(java.sql.Date.valueOf(ld_19700101), this.rs.getObject(1, java.sql.Date.class)); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> { + this.rs.getObject(1, java.sql.Time.class); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> { + this.rs.getObject(1, java.sql.Timestamp.class); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> { + this.rs.getObject(1, java.util.Date.class); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> { + this.rs.getObject(1, java.util.Calendar.class); + return null; + }); + assertEquals(ld_19700101, this.rs.getObject(1, LocalDate.class)); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> { + this.rs.getObject(1, LocalTime.class); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> { + this.rs.getObject(1, LocalDateTime.class); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> { + this.rs.getObject(1, OffsetTime.class); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> { + this.rs.getObject(1, OffsetDateTime.class); + return null; + }); + assertThrows(SQLException.class, Messages.getString("ResultSet.InvalidTimeValue", new Object[] { dur2 }), () -> { + this.rs.getObject(1, ZonedDateTime.class); + return null; + }); + assertEquals(Duration.parse(withFract ? "-PT300H10M20.123S" : "-PT300H10M20S"), this.rs.getObject(1, Duration.class)); + assertEquals(dur2, this.rs.getString(1)); + testConn.close(); } } @@ -3594,6 +3849,12 @@ public void testTimestampGetters() throws Exception { assertEquals(exp_odt.toOffsetDateTime(), this.rs.getObject(1, OffsetDateTime.class)); assertEquals(exp_odt, this.rs.getObject(1, ZonedDateTime.class)); assertEquals(exp_on_wire.toLocalDateTime().format(dateTimeFmt), this.rs.getString(1)); + assertThrows(SQLException.class, + Messages.getString("ResultSet.UnsupportedConversion", new Object[] { "TIMESTAMP", Duration.class.getName() }), + () -> { + this.rs.getObject(1, Duration.class); + return null; + }); testConn.close(); } @@ -3708,6 +3969,12 @@ public void testDatetimeGetters() throws Exception { assertEquals(ldt_20200101_020000_123456.atZone(preserveInstants ? connTz.toZoneId() : currZoneId), this.rs.getObject(1, ZonedDateTime.class)); assertEquals(ldt_20200101_020000_123456.format(dateTimeFmt), this.rs.getString(1)); + assertThrows(SQLException.class, + Messages.getString("ResultSet.UnsupportedConversion", new Object[] { "DATETIME", Duration.class.getName() }), + () -> { + this.rs.getObject(1, Duration.class); + return null; + }); testConn.close(); } @@ -3796,6 +4063,11 @@ public void testYearGetters() throws Exception { assertEquals(ldt_20200101_0000.atZone(currZoneId).toOffsetDateTime(), rs1.getObject(1, OffsetDateTime.class)); assertEquals(ldt_20200101_0000.atZone(currZoneId), rs1.getObject(1, ZonedDateTime.class)); assertEquals(s_20200101, rs1.getString(1)); + assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion", + new Object[] { "DATE", Duration.class.getName() }), () -> { + rs1.getObject(1, Duration.class); + return null; + }); } else { assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion", new Object[] { "LONG", java.sql.Date.class.getName() }), new Callable() { @@ -3916,6 +4188,13 @@ public Void call() throws Exception { return null; } }); + assertThrows(SQLException.class, Messages.getString("ResultSet.UnsupportedConversion", + new Object[] { "LONG", Duration.class.getName() }), new Callable() { + public Void call() throws Exception { + rs1.getObject(1, Duration.class); + return null; + } + }); assertEquals(s_2020, rs1.getString(1)); assertEquals(Short.valueOf((short) 2020), rs1.getObject(1)); }