diff --git a/jOOQ/src/main/java/org/jooq/impl/AlterTableImpl.java b/jOOQ/src/main/java/org/jooq/impl/AlterTableImpl.java index 5a16ddca81..0ae383ebd9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AlterTableImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/AlterTableImpl.java @@ -154,6 +154,7 @@ import static org.jooq.impl.Keywords.K_WITH_NO_DATACOPY; import static org.jooq.impl.QOM.Cascade.CASCADE; import static org.jooq.impl.QOM.Cascade.RESTRICT; import static org.jooq.impl.SQLDataType.VARCHAR; +import static org.jooq.impl.Tools.NO_SUPPORT_DEFAULT_DATETIME_LITERAL_PREFIX; import static org.jooq.impl.Tools.begin; import static org.jooq.impl.Tools.beginExecuteImmediate; import static org.jooq.impl.Tools.endExecuteImmediate; @@ -168,6 +169,7 @@ import static org.jooq.impl.Tools.toSQLDDLTypeDeclarationIdentityAfterNull; import static org.jooq.impl.Tools.toSQLDDLTypeDeclarationIdentityBeforeNull; import static org.jooq.impl.Tools.tryCatch; import static org.jooq.impl.Tools.BooleanDataKey.DATA_CONSTRAINT_REFERENCE; +import static org.jooq.impl.Tools.ExtendedDataKey.DATA_OMIT_DATETIME_LITERAL_PREFIX; import java.util.Arrays; import java.util.Collection; @@ -207,6 +209,7 @@ import org.jooq.TableElement; // ... import org.jooq.impl.QOM.Cascade; import org.jooq.impl.QOM.UNotYetImplemented; +import org.jooq.impl.Tools.ExtendedDataKey; /** * @author Lukas Eder @@ -1538,8 +1541,14 @@ implements break; } - ctx.sql(' ').visit(alterColumnDefault) - .end(ALTER_TABLE_ALTER_DEFAULT); + ctx.sql(' '); + + if (NO_SUPPORT_DEFAULT_DATETIME_LITERAL_PREFIX.contains(ctx.dialect()) && alterColumnDefault.getDataType().isDateTime()) + ctx.data(DATA_OMIT_DATETIME_LITERAL_PREFIX, true, c -> c.visit(alterColumnDefault)); + else + ctx.visit(alterColumnDefault); + + ctx.end(ALTER_TABLE_ALTER_DEFAULT); } else if (alterColumnDropDefault) { ctx.start(ALTER_TABLE_ALTER_DEFAULT); diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java index 45a0a53cb2..12d465d4c2 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java @@ -177,6 +177,7 @@ import static org.jooq.impl.Tools.map; import static org.jooq.impl.Tools.needsBackslashEscaping; import static org.jooq.impl.Tools.newRecord; import static org.jooq.impl.Tools.uncoerce; +import static org.jooq.impl.Tools.ExtendedDataKey.DATA_OMIT_DATETIME_LITERAL_PREFIX; import static org.jooq.tools.StringUtils.defaultIfNull; import static org.jooq.tools.StringUtils.leftPad; import static org.jooq.tools.jdbc.JDBCUtils.safeFree; @@ -189,7 +190,6 @@ import static org.jooq.util.postgres.PostgresUtils.toPGArrayString; import static org.jooq.util.postgres.PostgresUtils.toPGInterval; import static org.jooq.util.postgres.PostgresUtils.toYearToMonth; -import java.io.IOException; import java.io.Serializable; import java.io.StringReader; import java.lang.reflect.Modifier; @@ -295,7 +295,6 @@ import org.jooq.tools.StringUtils; import org.jooq.tools.jdbc.JDBCUtils; import org.jooq.tools.jdbc.MockArray; import org.jooq.tools.jdbc.MockResultSet; -import org.jooq.tools.json.JSONArray; import org.jooq.tools.json.JSONValue; import org.jooq.types.DayToSecond; import org.jooq.types.UByte; @@ -322,7 +321,11 @@ import org.jooq.util.postgres.PostgresUtils; public class DefaultBinding implements Binding { static final JooqLogger log = JooqLogger.getLogger(DefaultBinding.class); - private static final Set REQUIRE_JDBC_DATE_LITERAL = SQLDialect.supportedBy(MYSQL); + + + + + // Taken from org.postgresql.PGStatement 9223372036825200000 private static final long PG_DATE_POSITIVE_INFINITY = 9223372036825200000L; @@ -2476,9 +2479,16 @@ public class DefaultBinding implements Binding { else if (ctx.family() == DERBY) ctx.render().visit(K_DATE).sql("('").sql(escape(value, ctx.render())).sql("')"); - // [#3648] Circumvent a MySQL bug related to date literals - else if (REQUIRE_JDBC_DATE_LITERAL.contains(ctx.dialect())) - ctx.render().sql("{d '").sql(escape(value, ctx.render())).sql("'}"); + + + + + + + // [#16498] Special cases where the standard datetime literal prefix needs to be omitted + // See: https://bugs.mysql.com/bug.php?id=114450 + else if (ctx.data(DATA_OMIT_DATETIME_LITERAL_PREFIX) != null) + ctx.render().sql('\'').sql(format(value, ctx.render())).sql('\''); // Most dialects implement SQL standard date literals else @@ -4818,9 +4828,18 @@ public class DefaultBinding implements Binding { default: - // [#3648] Circumvent a MySQL bug related to date literals - if (REQUIRE_JDBC_DATE_LITERAL.contains(ctx.dialect())) - ctx.render().sql("{t '").sql(escape(value, ctx.render())).sql("'}"); + + + + + + + + + // [#16498] Special cases where the standard datetime literal prefix needs to be omitted + // See: https://bugs.mysql.com/bug.php?id=114450 + if (ctx.data(DATA_OMIT_DATETIME_LITERAL_PREFIX) != null) + ctx.render().sql('\'').sql(escape(value, ctx.render())).sql('\''); // Most dialects implement SQL standard time literals else @@ -4935,9 +4954,16 @@ public class DefaultBinding implements Binding { else if (ctx.family() == CUBRID) ctx.render().visit(K_DATETIME).sql(" '").sql(escape(value, ctx.render())).sql('\''); - // [#3648] Circumvent a MySQL bug related to date literals - else if (REQUIRE_JDBC_DATE_LITERAL.contains(ctx.dialect())) - ctx.render().sql("{ts '").sql(escape(value, ctx.render())).sql("'}"); + + + + + + + // [#16498] Special cases where the standard datetime literal prefix needs to be omitted + // See: https://bugs.mysql.com/bug.php?id=114450 + else if (ctx.data(DATA_OMIT_DATETIME_LITERAL_PREFIX) != null) + ctx.render().sql('\'').sql(format(value, ctx.render())).sql('\''); // Most dialects implement SQL standard timestamp literals else diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index ff2c486ff2..414829c5ec 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -215,6 +215,7 @@ import static org.jooq.impl.SubqueryCharacteristics.DERIVED_TABLE; import static org.jooq.impl.SubqueryCharacteristics.PREDICAND; import static org.jooq.impl.SubqueryCharacteristics.SET_OPERATION; import static org.jooq.impl.Tools.executeImmediate; +import static org.jooq.impl.Tools.ExtendedDataKey.DATA_OMIT_DATETIME_LITERAL_PREFIX; import static org.jooq.impl.Tools.SimpleDataKey.DATA_BLOCK_NESTING; import static org.jooq.tools.StringUtils.defaultIfNull; @@ -1004,7 +1005,16 @@ final class Tools { /** * [#15982] The base type of an empty array in the current scope. */ - DATA_EMPTY_ARRAY_BASE_TYPE + DATA_EMPTY_ARRAY_BASE_TYPE, + + /** + * [#16498] In some cases, the datetime literal prefix needs to be + * omitted. + *

+ * E.g. instead of DATE '2000-01-01', only + * '2001-01-01' should be rendered. + */ + DATA_OMIT_DATETIME_LITERAL_PREFIX, ; @@ -1057,12 +1067,12 @@ final class Tools { * The default escape character for [a] LIKE [b] ESCAPE […] * clauses. */ - static final char ESCAPE = '!'; + static final char ESCAPE = '!'; /** * A lock for the initialisation of other static members */ - private static final Object initLock = new Object(); + private static final Object initLock = new Object(); /** * Indicating whether JPA (jakarta.persistence) is on the @@ -1084,23 +1094,23 @@ final class Tools { * {@link #consumeExceptions(Configuration, PreparedStatement, SQLException)} * helps prevent infinite loops and {@link OutOfMemoryError}. */ - static int maxConsumedExceptions = 256; - static int maxConsumedResults = 65536; + static int maxConsumedExceptions = 256; + static int maxConsumedResults = 65536; /** * A pattern for the dash line syntax */ - private static final Pattern DASH_PATTERN = Pattern.compile("(-+)"); + private static final Pattern DASH_PATTERN = Pattern.compile("(-+)"); /** * A pattern for the pipe line syntax */ - private static final Pattern PIPE_PATTERN = Pattern.compile("(?<=\\|)([^|]+)(?=\\|)"); + private static final Pattern PIPE_PATTERN = Pattern.compile("(?<=\\|)([^|]+)(?=\\|)"); /** * A pattern for the dash line syntax */ - private static final Pattern PLUS_PATTERN = Pattern.compile("\\+(-+)(?=\\+)"); + private static final Pattern PLUS_PATTERN = Pattern.compile("\\+(-+)(?=\\+)"); /** * All characters that are matched by Java's interpretation of \s. @@ -1110,25 +1120,25 @@ final class Tools { * processing, it is probably safe to ignore most of those alternative * Unicode whitespaces. */ - private static final char[] WHITESPACE_CHARACTERS = " \t\n\u000B\f\r".toCharArray(); + private static final char[] WHITESPACE_CHARACTERS = " \t\n\u000B\f\r".toCharArray(); /** * Acceptable prefixes for JDBC escape syntax. */ - private static final char[][] JDBC_ESCAPE_PREFIXES = { + private static final char[][] JDBC_ESCAPE_PREFIXES = { "{fn ".toCharArray(), "{d ".toCharArray(), "{t ".toCharArray(), "{ts ".toCharArray() }; - private static final char[] TOKEN_SINGLE_LINE_COMMENT = { '-', '-' }; - private static final char[] TOKEN_SINGLE_LINE_COMMENT_C = { '/', '/' }; - private static final char[] TOKEN_HASH = { '#' }; - private static final char[] TOKEN_MULTI_LINE_COMMENT_OPEN = { '/', '*' }; - private static final char[] TOKEN_MULTI_LINE_COMMENT_CLOSE = { '*', '/' }; - private static final char[] TOKEN_APOS = { '\'' }; - private static final char[] TOKEN_ESCAPED_APOS = { '\'', '\'' }; + private static final char[] TOKEN_SINGLE_LINE_COMMENT = { '-', '-' }; + private static final char[] TOKEN_SINGLE_LINE_COMMENT_C = { '/', '/' }; + private static final char[] TOKEN_HASH = { '#' }; + private static final char[] TOKEN_MULTI_LINE_COMMENT_OPEN = { '/', '*' }; + private static final char[] TOKEN_MULTI_LINE_COMMENT_CLOSE = { '*', '/' }; + private static final char[] TOKEN_APOS = { '\'' }; + private static final char[] TOKEN_ESCAPED_APOS = { '\'', '\'' }; /** * "Suffixes" that are placed behind a "?" character to form an operator, @@ -1163,7 +1173,7 @@ final class Tools { *

  • ?|
  • * */ - private static final char[][] NON_BIND_VARIABLE_SUFFIXES = { + private static final char[][] NON_BIND_VARIABLE_SUFFIXES = { { '?' }, { '|' }, { '&' }, @@ -1182,7 +1192,7 @@ final class Tools { * such as "?<>", which is a non-equality operator, not * an operator on its own. */ - private static final char[][] BIND_VARIABLE_SUFFIXES = { + private static final char[][] BIND_VARIABLE_SUFFIXES = { { '<', '>' } }; @@ -1190,8 +1200,8 @@ final class Tools { * All hexadecimal digits accessible through array index, e.g. * HEX_DIGITS[15] == 'f'. */ - private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray(); - private static final byte[] HEX_LOOKUP = { + private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray(); + private static final byte[] HEX_LOOKUP = { /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -1201,17 +1211,18 @@ final class Tools { /* 0x60 */ 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; - static final Set REQUIRES_BACKSLASH_ESCAPING = SQLDialect.supportedBy(MARIADB, MYSQL); - static final Set NO_SUPPORT_NULL = SQLDialect.supportedBy(CLICKHOUSE, DERBY, FIREBIRD, H2, HSQLDB, TRINO); - static final Set NO_SUPPORT_NOT_NULL = SQLDialect.supportedBy(CLICKHOUSE, TRINO); - static final Set NO_SUPPORT_BINARY_TYPE_LENGTH = SQLDialect.supportedBy(POSTGRES, TRINO, YUGABYTEDB); - static final Set NO_SUPPORT_CAST_TYPE_IN_DDL = SQLDialect.supportedBy(MARIADB, MYSQL); - static final Set SUPPORT_NON_BIND_VARIABLE_SUFFIXES = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); - static final Set SUPPORT_POSTGRES_LITERALS = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); - static final Set DEFAULT_BEFORE_NULL = SQLDialect.supportedBy(FIREBIRD, HSQLDB); - static final Set NO_SUPPORT_TIMESTAMP_PRECISION = SQLDialect.supportedBy(DERBY); - static final Set DEFAULT_TIMESTAMP_NOT_NULL = SQLDialect.supportedBy(MARIADB); - static final Set REQUIRES_PARENTHESISED_DEFAULT = SQLDialect.supportedBy(SQLITE); + static final Set REQUIRES_BACKSLASH_ESCAPING = SQLDialect.supportedBy(MARIADB, MYSQL); + static final Set NO_SUPPORT_NULL = SQLDialect.supportedBy(CLICKHOUSE, DERBY, FIREBIRD, H2, HSQLDB, TRINO); + static final Set NO_SUPPORT_NOT_NULL = SQLDialect.supportedBy(CLICKHOUSE, TRINO); + static final Set NO_SUPPORT_BINARY_TYPE_LENGTH = SQLDialect.supportedBy(POSTGRES, TRINO, YUGABYTEDB); + static final Set NO_SUPPORT_CAST_TYPE_IN_DDL = SQLDialect.supportedBy(MARIADB, MYSQL); + static final Set SUPPORT_NON_BIND_VARIABLE_SUFFIXES = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); + static final Set SUPPORT_POSTGRES_LITERALS = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); + static final Set DEFAULT_BEFORE_NULL = SQLDialect.supportedBy(FIREBIRD, HSQLDB); + static final Set NO_SUPPORT_TIMESTAMP_PRECISION = SQLDialect.supportedBy(DERBY); + static final Set DEFAULT_TIMESTAMP_NOT_NULL = SQLDialect.supportedBy(MARIADB); + static final Set REQUIRES_PARENTHESISED_DEFAULT = SQLDialect.supportedBy(SQLITE); + static final Set NO_SUPPORT_DEFAULT_DATETIME_LITERAL_PREFIX = SQLDialect.supportedBy(MARIADB, MYSQL); @@ -6156,6 +6167,12 @@ final class Tools { // expressions, not if actual parentheses are rendered. if (REQUIRES_PARENTHESISED_DEFAULT.contains(ctx.dialect())) ctx.sql('(').visit(v).sql(')'); + + // [#16498] Special cases where the standard datetime literal prefix needs to be omitted + // See: https://bugs.mysql.com/bug.php?id=114450 + else if (NO_SUPPORT_DEFAULT_DATETIME_LITERAL_PREFIX.contains(ctx.dialect()) && type.isDateTime()) + ctx.data(DATA_OMIT_DATETIME_LITERAL_PREFIX, true, c -> c.visit(v)); + else ctx.visit(v); } diff --git a/jOOQ/src/main/java/org/jooq/util/mariadb/MariaDBDataType.java b/jOOQ/src/main/java/org/jooq/util/mariadb/MariaDBDataType.java index f8c21b6c83..ae8e507cbf 100644 --- a/jOOQ/src/main/java/org/jooq/util/mariadb/MariaDBDataType.java +++ b/jOOQ/src/main/java/org/jooq/util/mariadb/MariaDBDataType.java @@ -109,8 +109,8 @@ public class MariaDBDataType { public static final DataType VARBINARY = new BuiltInDataType<>(FAMILY, SQLDataType.VARBINARY, "varbinary", "binary"); public static final DataType DATE = new BuiltInDataType<>(FAMILY, SQLDataType.DATE, "date", "date"); public static final DataType