diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractTypedNamed.java b/jOOQ/src/main/java/org/jooq/impl/AbstractTypedNamed.java index b781ed112c..7bc743030b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractTypedNamed.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractTypedNamed.java @@ -78,6 +78,14 @@ abstract class AbstractTypedNamed extends AbstractNamed implements Typed { return getDataType().getType(); } + /** + * [#11959] A workaround to get access to the actual data type of an + * expression. + */ + /* non-final */ DataType getExpressionDataType() { + return getDataType(); + } + @Override public final DataType getDataType() { return type; diff --git a/jOOQ/src/main/java/org/jooq/impl/DateAdd.java b/jOOQ/src/main/java/org/jooq/impl/DateAdd.java index d9db5e7767..4f99c6c2fa 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DateAdd.java +++ b/jOOQ/src/main/java/org/jooq/impl/DateAdd.java @@ -200,7 +200,11 @@ extends default: throw unsupported(); } - if (getDataType().isDate()) + // [#10258] [#11954] + if (((AbstractField) interval).getExpressionDataType().isInterval()) + ctx.sql('(').visit(date).sql(" + ").visit(interval).sql(')'); + + else if (getDataType().isDate()) // [#10258] Special case for DATE + INTEGER arithmetic if (datePart == DatePart.DAY) diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java index 06ef99c531..1a2322f640 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java @@ -686,6 +686,7 @@ public class DefaultBinding implements Binding { + case H2: case HSQLDB: case POSTGRES: @@ -698,6 +699,7 @@ public class DefaultBinding implements Binding { switch (ctx.family()) { + case POSTGRES: return true; } @@ -2232,8 +2234,8 @@ public class DefaultBinding implements Binding { } static final class DefaultDayToSecondBinding extends AbstractBinding { - private static final Set REQUIRE_PG_INTERVAL_SYNTAX = SQLDialect.supportedBy(POSTGRES); - private static final Set REQUIRE_STANDARD_INTERVAL = SQLDialect.supportedBy(H2); + private static final Set REQUIRE_PG_INTERVAL = SQLDialect.supportedBy(POSTGRES); + private static final Set REQUIRE_STANDARD_INTERVAL = SQLDialect.supportedBy(H2); DefaultDayToSecondBinding(DataType dataType, Converter converter) { super(dataType, converter); @@ -2241,32 +2243,25 @@ public class DefaultBinding implements Binding { @Override void sqlInline0(BindingSQLContext ctx, DayToSecond value) throws SQLException { + // [#566] Interval data types are best bound as Strings - if (REQUIRE_PG_INTERVAL_SYNTAX.contains(ctx.dialect())) { - int sign = value.getSign(); - int days = sign * value.getDays(); - ctx.render().sql('\'') - .sql(days >= 0 ? '+' : '-') - .sql(Math.abs(days)) - .sql(' ') - .sql(sign * value.getHours()) - .sql(':') - .sql(sign * value.getMinutes()) - .sql(':') - .sql(sign * value.getSeconds()) - .sql('.') - .sql(StringUtils.leftPad(Integer.toString(value.getNano()), 9, '0')) - .sql('\''); - } + if (REQUIRE_PG_INTERVAL.contains(ctx.dialect())) + ctx.render().visit(inline(toPGInterval(value).toString())); else - ctx.render().sql('\'').sql(value.toString()).sql('\''); + super.sqlInline0(ctx, value); } @Override final void set0(BindingSetStatementContext ctx, DayToSecond value) throws SQLException { // [#566] Interval data types are best bound as Strings - if (REQUIRE_PG_INTERVAL_SYNTAX.contains(ctx.dialect())) + if (REQUIRE_PG_INTERVAL.contains(ctx.dialect())) + + + + + + ctx.statement().setObject(ctx.index(), toPGInterval(value)); else ctx.statement().setString(ctx.index(), renderDTS(ctx, value)); @@ -2279,7 +2274,7 @@ public class DefaultBinding implements Binding { @Override final DayToSecond get0(BindingGetResultSetContext ctx) throws SQLException { - if (REQUIRE_PG_INTERVAL_SYNTAX.contains(ctx.dialect())) { + if (REQUIRE_PG_INTERVAL.contains(ctx.dialect())) { Object object = ctx.resultSet().getObject(ctx.index()); return object == null ? null : PostgresUtils.toDayToSecond(object); } @@ -2289,7 +2284,7 @@ public class DefaultBinding implements Binding { @Override final DayToSecond get0(BindingGetStatementContext ctx) throws SQLException { - if (REQUIRE_PG_INTERVAL_SYNTAX.contains(ctx.dialect())) { + if (REQUIRE_PG_INTERVAL.contains(ctx.dialect())) { Object object = ctx.statement().getObject(ctx.index()); return object == null ? null : PostgresUtils.toDayToSecond(object); } @@ -4609,11 +4604,27 @@ public class DefaultBinding implements Binding { super(dataType, converter); } + @Override + final void sqlInline0(BindingSQLContext ctx, YearToSecond value) throws SQLException { + + // [#566] Interval data types are best bound as Strings + if (REQUIRE_PG_INTERVAL.contains(ctx.dialect())) + ctx.render().visit(inline(toPGInterval(value).toString())); + else + super.sqlInline0(ctx, value); + } + @Override final void set0(BindingSetStatementContext ctx, YearToSecond value) throws SQLException { // [#566] Interval data types are best bound as Strings if (REQUIRE_PG_INTERVAL.contains(ctx.dialect())) + + + + + + ctx.statement().setObject(ctx.index(), toPGInterval(value)); else ctx.statement().setString(ctx.index(), value.toString()); @@ -4668,11 +4679,27 @@ public class DefaultBinding implements Binding { super(dataType, converter); } + @Override + final void sqlInline0(BindingSQLContext ctx, YearToMonth value) throws SQLException { + + // [#566] Interval data types are best bound as Strings + if (REQUIRE_PG_INTERVAL.contains(ctx.dialect())) + ctx.render().visit(inline(toPGInterval(value).toString())); + else + super.sqlInline0(ctx, value); + } + @Override final void set0(BindingSetStatementContext ctx, YearToMonth value) throws SQLException { // [#566] Interval data types are best bound as Strings if (REQUIRE_PG_INTERVAL.contains(ctx.dialect())) + + + + + + ctx.statement().setObject(ctx.index(), toPGInterval(value)); else ctx.statement().setString(ctx.index(), renderYTM(ctx, value)); diff --git a/jOOQ/src/main/java/org/jooq/impl/Expression.java b/jOOQ/src/main/java/org/jooq/impl/Expression.java index 04fc662cf0..bccd490642 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Expression.java +++ b/jOOQ/src/main/java/org/jooq/impl/Expression.java @@ -166,6 +166,28 @@ final class Expression extends AbstractTransformable { this.rhs = rhs; } + @Override + DataType getExpressionDataType() { + + // [#11959] Workaround for lack of proper data type information for interval based expressions + AbstractField l = (AbstractField) lhs; + AbstractField r = (AbstractField) rhs; + + DataType lt = l.getExpressionDataType(); + DataType rt = r.getExpressionDataType(); + + switch (operator) { + case MULTIPLY: + case DIVIDE: + return rt.isInterval() ? rt : lt; + + case ADD: + return lt.isInterval() ? rt : lt; + } + + return lt; + } + @SuppressWarnings("unchecked") @Override final void accept0(Context ctx) {