This includes:

- [jOOQ/jOOQ#16498] Render standard SQL DATE literal on MySQL, instead of JDBC date literal
- [jOOQ/jOOQ#16539] DDL statements should produce DATETIME data type for SQLDataType.TIMESTAMP in MySQL
This commit is contained in:
Lukas Eder 2024-04-04 11:31:19 +02:00
parent 166ee5da84
commit 3079e116c5
5 changed files with 100 additions and 48 deletions

View File

@ -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);

View File

@ -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<T, U> implements Binding<T, U> {
static final JooqLogger log = JooqLogger.getLogger(DefaultBinding.class);
private static final Set<SQLDialect> 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<T, U> implements Binding<T, U> {
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<T, U> implements Binding<T, U> {
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<T, U> implements Binding<T, U> {
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

View File

@ -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.
* <p>
* E.g. instead of <code>DATE '2000-01-01'</code>, only
* <code>'2001-01-01'</code> should be rendered.
*/
DATA_OMIT_DATETIME_LITERAL_PREFIX,
;
@ -1057,12 +1067,12 @@ final class Tools {
* The default escape character for <code>[a] LIKE [b] ESCAPE []</code>
* 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 (<code>jakarta.persistence</code>) 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 {
* <li>?|</li>
* </ul>
*/
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 <code>"?&lt;&gt;"</code>, 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.
* <code>HEX_DIGITS[15] == 'f'</code>.
*/
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<SQLDialect> REQUIRES_BACKSLASH_ESCAPING = SQLDialect.supportedBy(MARIADB, MYSQL);
static final Set<SQLDialect> NO_SUPPORT_NULL = SQLDialect.supportedBy(CLICKHOUSE, DERBY, FIREBIRD, H2, HSQLDB, TRINO);
static final Set<SQLDialect> NO_SUPPORT_NOT_NULL = SQLDialect.supportedBy(CLICKHOUSE, TRINO);
static final Set<SQLDialect> NO_SUPPORT_BINARY_TYPE_LENGTH = SQLDialect.supportedBy(POSTGRES, TRINO, YUGABYTEDB);
static final Set<SQLDialect> NO_SUPPORT_CAST_TYPE_IN_DDL = SQLDialect.supportedBy(MARIADB, MYSQL);
static final Set<SQLDialect> SUPPORT_NON_BIND_VARIABLE_SUFFIXES = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB);
static final Set<SQLDialect> SUPPORT_POSTGRES_LITERALS = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB);
static final Set<SQLDialect> DEFAULT_BEFORE_NULL = SQLDialect.supportedBy(FIREBIRD, HSQLDB);
static final Set<SQLDialect> NO_SUPPORT_TIMESTAMP_PRECISION = SQLDialect.supportedBy(DERBY);
static final Set<SQLDialect> DEFAULT_TIMESTAMP_NOT_NULL = SQLDialect.supportedBy(MARIADB);
static final Set<SQLDialect> REQUIRES_PARENTHESISED_DEFAULT = SQLDialect.supportedBy(SQLITE);
static final Set<SQLDialect> REQUIRES_BACKSLASH_ESCAPING = SQLDialect.supportedBy(MARIADB, MYSQL);
static final Set<SQLDialect> NO_SUPPORT_NULL = SQLDialect.supportedBy(CLICKHOUSE, DERBY, FIREBIRD, H2, HSQLDB, TRINO);
static final Set<SQLDialect> NO_SUPPORT_NOT_NULL = SQLDialect.supportedBy(CLICKHOUSE, TRINO);
static final Set<SQLDialect> NO_SUPPORT_BINARY_TYPE_LENGTH = SQLDialect.supportedBy(POSTGRES, TRINO, YUGABYTEDB);
static final Set<SQLDialect> NO_SUPPORT_CAST_TYPE_IN_DDL = SQLDialect.supportedBy(MARIADB, MYSQL);
static final Set<SQLDialect> SUPPORT_NON_BIND_VARIABLE_SUFFIXES = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB);
static final Set<SQLDialect> SUPPORT_POSTGRES_LITERALS = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB);
static final Set<SQLDialect> DEFAULT_BEFORE_NULL = SQLDialect.supportedBy(FIREBIRD, HSQLDB);
static final Set<SQLDialect> NO_SUPPORT_TIMESTAMP_PRECISION = SQLDialect.supportedBy(DERBY);
static final Set<SQLDialect> DEFAULT_TIMESTAMP_NOT_NULL = SQLDialect.supportedBy(MARIADB);
static final Set<SQLDialect> REQUIRES_PARENTHESISED_DEFAULT = SQLDialect.supportedBy(SQLITE);
static final Set<SQLDialect> 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);
}

View File

@ -109,8 +109,8 @@ public class MariaDBDataType {
public static final DataType<byte[]> VARBINARY = new BuiltInDataType<>(FAMILY, SQLDataType.VARBINARY, "varbinary", "binary");
public static final DataType<Date> DATE = new BuiltInDataType<>(FAMILY, SQLDataType.DATE, "date", "date");
public static final DataType<Time> TIME = new BuiltInDataType<>(FAMILY, SQLDataType.TIME, "time", "time");
public static final DataType<Timestamp> TIMESTAMP = new BuiltInDataType<>(FAMILY, SQLDataType.TIMESTAMP, "timestamp", "datetime");
public static final DataType<Timestamp> DATETIME = new BuiltInDataType<>(FAMILY, SQLDataType.TIMESTAMP, "datetime", "datetime");
public static final DataType<Timestamp> TIMESTAMP = new BuiltInDataType<>(FAMILY, SQLDataType.TIMESTAMP, "timestamp", "datetime");
public static final DataType<JSON> JSON = new BuiltInDataType<>(FAMILY, SQLDataType.JSON, "json");
// -------------------------------------------------------------------------

View File

@ -109,8 +109,8 @@ public class MySQLDataType {
public static final DataType<byte[]> VARBINARY = new BuiltInDataType<>(FAMILY, SQLDataType.VARBINARY, "varbinary", "binary");
public static final DataType<Date> DATE = new BuiltInDataType<>(FAMILY, SQLDataType.DATE, "date", "date");
public static final DataType<Time> TIME = new BuiltInDataType<>(FAMILY, SQLDataType.TIME, "time", "time");
public static final DataType<Timestamp> TIMESTAMP = new BuiltInDataType<>(FAMILY, SQLDataType.TIMESTAMP, "timestamp", "datetime");
public static final DataType<Timestamp> DATETIME = new BuiltInDataType<>(FAMILY, SQLDataType.TIMESTAMP, "datetime", "datetime");
public static final DataType<Timestamp> TIMESTAMP = new BuiltInDataType<>(FAMILY, SQLDataType.TIMESTAMP, "timestamp", "datetime");
// -------------------------------------------------------------------------
// Compatibility types for supported SQLDialect.MYSQL, SQLDataTypes