From 1bd9b56c32abdf0d480750ee9802cdaeb8ef8236 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Thu, 19 Jan 2023 14:10:11 +0100 Subject: [PATCH] [jOOQ/jOOQ#2500] Add support for the MySQL YEAR data type - Added SQLDataType.YEAR - Added parser support - Added DDL support - Added DefaultYearBinding - Added CAST support - Update Val::getJavaValueString - Update DefaultRecordBinding - Implement Convert logic (DefaultConverterProvider) - Support MULTISET and ROW usage --- jOOQ/src/main/java/org/jooq/impl/Convert.java | 15 ++++++ .../java/org/jooq/impl/DefaultBinding.java | 47 +++++++++++++++++++ .../main/java/org/jooq/impl/ParserImpl.java | 9 +++- .../main/java/org/jooq/impl/SQLDataType.java | 10 ++++ jOOQ/src/main/java/org/jooq/impl/Val.java | 3 ++ .../src/main/java/org/jooq/tools/Convert.java | 15 ++++++ .../org/jooq/util/cubrid/CUBRIDDataType.java | 2 + .../org/jooq/util/derby/DerbyDataType.java | 2 + .../jooq/util/firebird/FirebirdDataType.java | 2 + .../org/jooq/util/hsqldb/HSQLDBDataType.java | 2 + .../org/jooq/util/ignite/IgniteDataType.java | 18 +++---- .../jooq/util/mariadb/MariaDBDataType.java | 3 +- .../org/jooq/util/mysql/MySQLDataType.java | 3 +- .../jooq/util/postgres/PostgresDataType.java | 2 + .../org/jooq/util/sqlite/SQLiteDataType.java | 2 + .../util/yugabytedb/YugabyteDBDataType.java | 2 + 16 files changed, 126 insertions(+), 11 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/Convert.java b/jOOQ/src/main/java/org/jooq/impl/Convert.java index 2a7c4b1172..811861fc6e 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Convert.java +++ b/jOOQ/src/main/java/org/jooq/impl/Convert.java @@ -75,6 +75,7 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.OffsetTime; +import java.time.Year; import java.time.format.DateTimeParseException; import java.time.temporal.Temporal; import java.util.ArrayList; @@ -779,6 +780,9 @@ final class Convert { if (wrapperFrom == Boolean.class) return (U) (((Boolean) from) ? Long.valueOf(1L) : Long.valueOf(0L)); + if (wrapperFrom == Year.class) + return (U) (Long) (long) ((Year) from).getValue(); + if (java.util.Date.class.isAssignableFrom(fromClass)) return (U) Long.valueOf(((java.util.Date) from).getTime()); @@ -916,6 +920,17 @@ final class Convert { return null; } } + else if (toClass == Year.class) { + if (Number.class.isAssignableFrom(wrapperFrom)) + return (U) Year.of((((Number) from).intValue())); + + try { + return (U) Year.parse(from.toString().trim()); + } + catch (DateTimeParseException e) { + return null; + } + } else if (wrapperTo == Boolean.class) { String s = from.toString().toLowerCase().trim(); diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java index a60cd757b3..7a52cf0863 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java @@ -201,6 +201,7 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.OffsetTime; +import java.time.Year; import java.time.ZoneOffset; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; @@ -473,6 +474,8 @@ public class DefaultBinding implements Binding { return new DefaultYearToSecondBinding(dataType, converter); else if (type == YearToMonth.class) return new DefaultYearToMonthBinding(dataType, converter); + else if (type == Year.class) + return new DefaultYearBinding(dataType, converter); // Subtypes of array types etc. // The type byte[] is handled earlier. byte[][] can be handled here @@ -3991,6 +3994,8 @@ public class DefaultBinding implements Binding { return converter.from((T) UUID.fromString(string), ctx.converterContext()); else if (type == XML.class) return converter.from((T) XML.xml(string), ctx.converterContext()); + else if (type == Year.class) + return converter.from((T) Year.parse(string), ctx.converterContext()); else if (type.isArray()) return converter.from((T) pgNewArray(ctx, field, type, string), ctx.converterContext()); @@ -5735,6 +5740,48 @@ public class DefaultBinding implements Binding { } } + static final class DefaultYearBinding extends InternalBinding { + + DefaultYearBinding(DataType dataType, Converter converter) { + super(dataType, converter); + } + + @Override + final void sqlInline0(BindingSQLContext ctx, Year value) { + ctx.render().sql(value.getValue()); + } + + @Override + final void set0(BindingSetStatementContext ctx, Year value) throws SQLException { + ctx.statement().setInt(ctx.index(), value.getValue()); + } + + @Override + final void set0(BindingSetSQLOutputContext ctx, Year value) throws SQLException { + ctx.output().writeInt(value.getValue()); + } + + @Override + final Year get0(BindingGetResultSetContext ctx) throws SQLException { + return wasNull(ctx.resultSet(), Year.of(ctx.resultSet().getInt(ctx.index()))); + } + + @Override + final Year get0(BindingGetStatementContext ctx) throws SQLException { + return wasNull(ctx.statement(), Year.of(ctx.statement().getInt(ctx.index()))); + } + + @Override + final Year get0(BindingGetSQLInputContext ctx) throws SQLException { + return wasNull(ctx.input(), Year.of(ctx.input().readInt())); + } + + @Override + final int sqltype(Statement statement, Configuration configuration) { + return Types.SMALLINT; + } + } + static final class DefaultYearToMonthBinding extends InternalBinding { private static final Set REQUIRE_PG_INTERVAL = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); private static final Set REQUIRE_STANDARD_INTERVAL = SQLDialect.supportedBy(H2); diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index e8117ce926..a4c753e86c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -458,6 +458,7 @@ import static org.jooq.impl.SQLDataType.TINYINT; import static org.jooq.impl.SQLDataType.VARBINARY; import static org.jooq.impl.SQLDataType.VARCHAR; import static org.jooq.impl.SQLDataType.XML; +import static org.jooq.impl.SQLDataType.YEAR; import static org.jooq.impl.SelectQueryImpl.EMULATE_SELECT_INTO_AS_CTAS; import static org.jooq.impl.SelectQueryImpl.NO_SUPPORT_FOR_UPDATE_OF_FIELDS; import static org.jooq.impl.Tools.CONFIG; @@ -8420,7 +8421,7 @@ final class DefaultParseContext extends AbstractScope implements ParseContext { return field; else if (parseFunctionNameIf("ADD_YEARS")) - return parseFieldAddDatePart(YEAR); + return parseFieldAddDatePart(DatePart.YEAR); else if (parseFunctionNameIf("ADD_MONTHS")) return parseFieldAddDatePart(MONTH); else if (parseFunctionNameIf("ADD_DAYS")) @@ -12673,6 +12674,12 @@ final class DefaultParseContext extends AbstractScope implements ParseContext { return SQLDataType.XML; break; + + case 'Y': + if (parseKeywordOrIdentifierIf("YEAR")) + return parseDataTypeLength(SQLDataType.YEAR); + + break; } Name name; diff --git a/jOOQ/src/main/java/org/jooq/impl/SQLDataType.java b/jOOQ/src/main/java/org/jooq/impl/SQLDataType.java index 65b0ad4158..13f1f101ae 100644 --- a/jOOQ/src/main/java/org/jooq/impl/SQLDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/SQLDataType.java @@ -82,6 +82,7 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.OffsetTime; +import java.time.Year; import java.time.ZonedDateTime; import java.util.UUID; @@ -96,6 +97,7 @@ import org.jooq.Record; import org.jooq.Result; import org.jooq.RowId; import org.jooq.SQLDialect; +import org.jooq.SQLDialectCategory; import org.jooq.XML; import org.jooq.types.DayToSecond; import org.jooq.types.UByte; @@ -610,6 +612,14 @@ public final class SQLDataType { return INSTANT.precision(precision); } + /** + * A {@link Types#SMALLINT} type that represents a year. + *

+ * While JDBC does not support this type, some dialects do, specifically + * those of the {@link SQLDialectCategory#MYSQL} category. + */ + public static final DataType YEAR = new BuiltInDataType<>(Year.class, "year"); + // ------------------------------------------------------------------------- // Binary types // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/Val.java b/jOOQ/src/main/java/org/jooq/impl/Val.java index 43f0bb6886..414a66eccc 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Val.java +++ b/jOOQ/src/main/java/org/jooq/impl/Val.java @@ -62,6 +62,7 @@ import java.sql.Timestamp; import java.time.Instant; import java.time.OffsetDateTime; import java.time.OffsetTime; +import java.time.Year; import java.util.Arrays; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -321,6 +322,8 @@ final class Val extends AbstractParam implements UEmpty { + + diff --git a/jOOQ/src/main/java/org/jooq/tools/Convert.java b/jOOQ/src/main/java/org/jooq/tools/Convert.java index eeb8c70eaa..7939c93eff 100644 --- a/jOOQ/src/main/java/org/jooq/tools/Convert.java +++ b/jOOQ/src/main/java/org/jooq/tools/Convert.java @@ -72,6 +72,7 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.OffsetTime; +import java.time.Year; import java.time.format.DateTimeParseException; import java.time.temporal.Temporal; import java.util.ArrayList; @@ -724,6 +725,9 @@ public final class Convert { if (wrapperFrom == Boolean.class) return (U) (((Boolean) from) ? Long.valueOf(1L) : Long.valueOf(0L)); + if (wrapperFrom == Year.class) + return (U) (Long) (long) ((Year) from).getValue(); + if (java.util.Date.class.isAssignableFrom(fromClass)) return (U) Long.valueOf(((java.util.Date) from).getTime()); @@ -861,6 +865,17 @@ public final class Convert { return null; } } + else if (toClass == Year.class) { + if (Number.class.isAssignableFrom(wrapperFrom)) + return (U) Year.of((((Number) from).intValue())); + + try { + return (U) Year.parse(from.toString().trim()); + } + catch (DateTimeParseException e) { + return null; + } + } else if (wrapperTo == Boolean.class) { String s = from.toString().toLowerCase().trim(); diff --git a/jOOQ/src/main/java/org/jooq/util/cubrid/CUBRIDDataType.java b/jOOQ/src/main/java/org/jooq/util/cubrid/CUBRIDDataType.java index e20484c4cc..5967b43236 100644 --- a/jOOQ/src/main/java/org/jooq/util/cubrid/CUBRIDDataType.java +++ b/jOOQ/src/main/java/org/jooq/util/cubrid/CUBRIDDataType.java @@ -43,6 +43,7 @@ import java.math.BigInteger; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; +import java.time.Year; import java.util.UUID; import org.jooq.DataType; @@ -126,6 +127,7 @@ public class CUBRIDDataType { protected static final DataType __SMALLINTUNSIGNED = new BuiltInDataType<>(FAMILY, SQLDataType.SMALLINTUNSIGNED, "int"); protected static final DataType __INTEGERUNSIGNED = new BuiltInDataType<>(FAMILY, SQLDataType.INTEGERUNSIGNED, "bigint"); protected static final DataType __BIGINTUNSIGNED = new BuiltInDataType<>(FAMILY, SQLDataType.BIGINTUNSIGNED, "decimal"); + protected static final DataType __YEAR = new BuiltInDataType<>(FAMILY, SQLDataType.YEAR, "smallint"); // ------------------------------------------------------------------------- // Compatibility types for supported Java types diff --git a/jOOQ/src/main/java/org/jooq/util/derby/DerbyDataType.java b/jOOQ/src/main/java/org/jooq/util/derby/DerbyDataType.java index de392b966e..41f3b02204 100644 --- a/jOOQ/src/main/java/org/jooq/util/derby/DerbyDataType.java +++ b/jOOQ/src/main/java/org/jooq/util/derby/DerbyDataType.java @@ -43,6 +43,7 @@ import java.math.BigInteger; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; +import java.time.Year; import java.util.UUID; import org.jooq.DataType; @@ -124,6 +125,7 @@ public class DerbyDataType { protected static final DataType __BIGINTUNSIGNED = new BuiltInDataType<>(FAMILY, SQLDataType.BIGINTUNSIGNED, "decimal", "decimal(20)"); protected static final DataType __JSON = new BuiltInDataType<>(FAMILY, SQLDataType.JSON, "clob"); protected static final DataType __JSONB = new BuiltInDataType<>(FAMILY, SQLDataType.JSONB, "blob"); + protected static final DataType __YEAR = new BuiltInDataType<>(FAMILY, SQLDataType.YEAR, "smallint"); // ------------------------------------------------------------------------- // Compatibility types for supported Java types diff --git a/jOOQ/src/main/java/org/jooq/util/firebird/FirebirdDataType.java b/jOOQ/src/main/java/org/jooq/util/firebird/FirebirdDataType.java index b35ea1b5d6..99103015b2 100644 --- a/jOOQ/src/main/java/org/jooq/util/firebird/FirebirdDataType.java +++ b/jOOQ/src/main/java/org/jooq/util/firebird/FirebirdDataType.java @@ -43,6 +43,7 @@ import java.math.BigInteger; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; +import java.time.Year; import java.util.UUID; import org.jooq.DataType; @@ -131,6 +132,7 @@ public class FirebirdDataType { protected static final DataType __BIGINTUNSIGNED = new BuiltInDataType<>(FAMILY, SQLDataType.BIGINTUNSIGNED, "decimal", "varchar(20)"); // There are no large numbers in firebird...? protected static final DataType __JSON = new BuiltInDataType<>(FAMILY, SQLDataType.JSON, "blob sub_type text"); protected static final DataType __JSONB = new BuiltInDataType<>(FAMILY, SQLDataType.JSONB, "blob"); + protected static final DataType __YEAR = new BuiltInDataType<>(FAMILY, SQLDataType.YEAR, "smallint"); // ------------------------------------------------------------------------- // Compatibility types for supported Java types diff --git a/jOOQ/src/main/java/org/jooq/util/hsqldb/HSQLDBDataType.java b/jOOQ/src/main/java/org/jooq/util/hsqldb/HSQLDBDataType.java index 68a0d0bd82..e4034f6a7b 100644 --- a/jOOQ/src/main/java/org/jooq/util/hsqldb/HSQLDBDataType.java +++ b/jOOQ/src/main/java/org/jooq/util/hsqldb/HSQLDBDataType.java @@ -46,6 +46,7 @@ import java.sql.Timestamp; import java.time.Instant; import java.time.OffsetDateTime; import java.time.OffsetTime; +import java.time.Year; import java.util.UUID; import org.jooq.DataType; @@ -142,6 +143,7 @@ public class HSQLDBDataType { protected static final DataType __BIGINTUNSIGNED = new BuiltInDataType<>(FAMILY, SQLDataType.BIGINTUNSIGNED, "decimal(p, s)"); protected static final DataType __JSON = new BuiltInDataType<>(FAMILY, SQLDataType.JSON, "clob"); protected static final DataType __JSONB = new BuiltInDataType<>(FAMILY, SQLDataType.JSONB, "blob"); + protected static final DataType __YEAR = new BuiltInDataType<>(FAMILY, SQLDataType.YEAR, "smallint"); // ------------------------------------------------------------------------- // Compatibility types for supported Java types diff --git a/jOOQ/src/main/java/org/jooq/util/ignite/IgniteDataType.java b/jOOQ/src/main/java/org/jooq/util/ignite/IgniteDataType.java index 19a9426479..6bd52386f2 100644 --- a/jOOQ/src/main/java/org/jooq/util/ignite/IgniteDataType.java +++ b/jOOQ/src/main/java/org/jooq/util/ignite/IgniteDataType.java @@ -42,6 +42,7 @@ import java.math.BigInteger; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; +import java.time.Year; import java.util.UUID; import org.jooq.DataType; @@ -89,14 +90,14 @@ public class IgniteDataType { public static final DataType