From 2d0515d5139f80dca63474566c0f51b72555ce31 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Thu, 9 Jul 2020 14:07:28 +0200 Subject: [PATCH] [jOOQ/jOOQ#10373] Derby CHAR casts erroneously casts to VARCHAR This contains WIP for: - [jOOQ/jOOQ#10372] Refactor internal org.jooq.impl.Cast.Native for better reuse --- jOOQ/src/main/java/org/jooq/impl/Cast.java | 77 +++++++------------ .../org/jooq/util/derby/DerbyDataType.java | 6 +- 2 files changed, 30 insertions(+), 53 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/Cast.java b/jOOQ/src/main/java/org/jooq/impl/Cast.java index 17f6c28259..329df02117 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Cast.java +++ b/jOOQ/src/main/java/org/jooq/impl/Cast.java @@ -40,17 +40,16 @@ package org.jooq.impl; import static org.jooq.impl.DSL.inline; import static org.jooq.impl.Keywords.K_AS; import static org.jooq.impl.Keywords.K_CAST; -import static org.jooq.impl.Keywords.K_DECIMAL; -import static org.jooq.impl.Keywords.K_TRIM; import static org.jooq.impl.Names.N_CAST; import static org.jooq.impl.Names.N_TO_CLOB; import static org.jooq.impl.Names.N_TO_DATE; import static org.jooq.impl.Names.N_TO_TIMESTAMP; import static org.jooq.impl.SQLDataType.BOOLEAN; +import static org.jooq.impl.SQLDataType.CHAR; +import static org.jooq.impl.SQLDataType.DECIMAL; import static org.jooq.impl.SQLDataType.DOUBLE; import static org.jooq.impl.SQLDataType.FLOAT; import static org.jooq.impl.SQLDataType.REAL; -import static org.jooq.impl.SQLDataType.VARCHAR; import java.sql.Date; import java.sql.Time; @@ -106,7 +105,7 @@ final class Cast extends AbstractField { break; default: - ctx.visit(new Native()); + ctx.visit(new CastNative<>(field, getDataType())); break; } } @@ -157,13 +156,17 @@ final class Cast extends AbstractField { - private class CastDerby extends Native { + private class CastDerby extends CastNative { /** * Generated UID */ private static final long serialVersionUID = -8737153188122391258L; + CastDerby() { + super(field, Cast.this.getDataType()); + } + @SuppressWarnings("unchecked") private final Field asDecodeNumberToBoolean() { @@ -186,62 +189,29 @@ final class Cast extends AbstractField { .otherwise(inline(true)); } + @SuppressWarnings("unchecked") @Override public final void accept(Context ctx) { - - // Avoid casting bind values inside an explicit cast... - CastMode castMode = ctx.castMode(); DataType type = getSQLDataType(); // [#857] Interestingly, Derby does not allow for casting numeric // types directly to VARCHAR. An intermediary cast to CHAR is needed - if (field.getDataType().isNumeric() && VARCHAR.equals(type)) { - ctx.visit(K_TRIM).sql('(') - .visit(K_CAST).sql('(') - .visit(K_CAST).sql('(') - .castMode(CastMode.NEVER) - .visit(field) - .castMode(castMode) - .sql(' ').visit(K_AS).sql(" char(38))") - .sql(' ').visit(K_AS).sql(' ') - .sql(getDataType(ctx.configuration()).getCastTypeName(ctx.configuration())) - .sql("))"); - - return; - } + if (field.getDataType().isNumeric() && type.isString() && !CHAR.equals(type)) + ctx.visit(DSL.trim(new CastNative<>(new CastNative<>(field, CHAR(38)), (DataType) getDataType()))); // [#888] ... neither does casting character types to FLOAT (and similar) - else if (field.getDataType().isString() && - (FLOAT.equals(type) || DOUBLE.equals(type) || REAL.equals(type))) { - - ctx.visit(K_CAST).sql('(') - .visit(K_CAST).sql('(') - .castMode(CastMode.NEVER) - .visit(field) - .castMode(castMode) - .sql(' ').visit(K_AS).sql(' ').visit(K_DECIMAL) - .sql(") ") - .visit(K_AS) - .sql(' ') - .sql(getDataType(ctx.configuration()).getCastTypeName(ctx.configuration())) - .sql(')'); - - return; - } + else if (field.getDataType().isString() && (FLOAT.equals(type) || DOUBLE.equals(type) || REAL.equals(type))) + ctx.visit(new CastNative<>(new CastNative<>(field, DECIMAL), getDataType())); // [#859] ... neither does casting numeric types to BOOLEAN - else if (field.getDataType().isNumeric() && BOOLEAN.equals(type)) { + else if (field.getDataType().isNumeric() && BOOLEAN.equals(type)) ctx.visit(asDecodeNumberToBoolean()); - return; - } // [#859] ... neither does casting character types to BOOLEAN - else if (field.getDataType().isString() && BOOLEAN.equals(type)) { + else if (field.getDataType().isString() && BOOLEAN.equals(type)) ctx.visit(asDecodeVarcharToBoolean()); - return; - } - - super.accept(ctx); + else + super.accept(ctx); } } @@ -317,15 +287,22 @@ final class Cast extends AbstractField { - private class Native extends AbstractField { + + + + + private static class CastNative extends AbstractField { /** * Generated UID */ private static final long serialVersionUID = -8497561014419483312L; + private final Field field; - Native() { - super(N_CAST, Cast.this.getDataType()); + CastNative(Field field, DataType type) { + super(N_CAST, type); + + this.field = field; } @Override 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 8d381735fa..a3547b1058 100644 --- a/jOOQ/src/main/java/org/jooq/util/derby/DerbyDataType.java +++ b/jOOQ/src/main/java/org/jooq/util/derby/DerbyDataType.java @@ -92,8 +92,8 @@ public class DerbyDataType { public static final DataType NUMERIC = new DefaultDataType<>(FAMILY, SQLDataType.NUMERIC, "numeric"); public static final DataType VARCHAR = new DefaultDataType<>(FAMILY, SQLDataType.VARCHAR, "varchar", "varchar(32672)"); public static final DataType LONGVARCHAR = new DefaultDataType<>(FAMILY, SQLDataType.LONGVARCHAR, "long varchar"); - public static final DataType CHAR = new DefaultDataType<>(FAMILY, SQLDataType.CHAR, "char", "varchar(32672)"); - public static final DataType CHARACTER = new DefaultDataType<>(FAMILY, SQLDataType.CHAR, "character", "varchar(32672)"); + public static final DataType CHAR = new DefaultDataType<>(FAMILY, SQLDataType.CHAR, "char", "char(1)"); + public static final DataType CHARACTER = new DefaultDataType<>(FAMILY, SQLDataType.CHAR, "character", "character(1)"); public static final DataType CLOB = new DefaultDataType<>(FAMILY, SQLDataType.CLOB, "clob"); public static final DataType CHARACTERLARGEOBJECT = new DefaultDataType<>(FAMILY, SQLDataType.CLOB, "character large object"); public static final DataType CHARVARYING = new DefaultDataType<>(FAMILY, SQLDataType.VARCHAR, "char varying", "char varying(32672)"); @@ -112,7 +112,7 @@ public class DerbyDataType { protected static final DataType __BINARY = new DefaultDataType<>(FAMILY, SQLDataType.BINARY, "blob"); protected static final DataType __BIT = new DefaultDataType<>(FAMILY, SQLDataType.BIT, "boolean"); protected static final DataType __LONGVARBINARY = new DefaultDataType<>(FAMILY, SQLDataType.LONGVARBINARY, "blob"); - protected static final DataType __NCHAR = new DefaultDataType<>(FAMILY, SQLDataType.NCHAR, "char", "varchar(32672)"); + protected static final DataType __NCHAR = new DefaultDataType<>(FAMILY, SQLDataType.NCHAR, "char", "char(1)"); protected static final DataType __NCLOB = new DefaultDataType<>(FAMILY, SQLDataType.NCLOB, "clob"); protected static final DataType __LONGNVARCHAR = new DefaultDataType<>(FAMILY, SQLDataType.LONGNVARCHAR, "long varchar"); protected static final DataType __NVARCHAR = new DefaultDataType<>(FAMILY, SQLDataType.NVARCHAR, "varchar", "varchar(32672)");