[jOOQ/jOOQ#2230] H2 code generation support for interval types

This includes: [jOOQ/jOOQ#10451] Use H2 native interval arithmetic instead of DATEADD emulation
This commit is contained in:
Lukas Eder 2020-07-28 14:56:00 +02:00
parent ca2f784d3d
commit c3f404b046
4 changed files with 45 additions and 37 deletions

View File

@ -37,10 +37,12 @@
*/
package org.jooq.meta.h2;
import static org.jooq.impl.DSL.any;
import static org.jooq.impl.DSL.choose;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DSL.noCondition;
import static org.jooq.impl.DSL.when;
import static org.jooq.impl.DSL.zero;
import static org.jooq.meta.h2.information_schema.Tables.COLUMNS;
import static org.jooq.tools.StringUtils.defaultString;
@ -89,7 +91,11 @@ public class H2TableDefinition extends AbstractTableDefinition {
for (Record record : create().select(
COLUMNS.COLUMN_NAME,
COLUMNS.ORDINAL_POSITION,
COLUMNS.TYPE_NAME,
// [#2230] Translate INTERVAL_TYPE to supported types
when(COLUMNS.INTERVAL_TYPE.like(any(inline("%YEAR%"), inline("%MONTH%"))), inline("INTERVAL YEAR TO MONTH"))
.when(COLUMNS.INTERVAL_TYPE.like(any(inline("%DAY%"), inline("%HOUR%"), inline("%MINUTE%"), inline("%SECOND%"))), inline("INTERVAL DAY TO SECOND"))
.else_(COLUMNS.TYPE_NAME).as(COLUMNS.TYPE_NAME),
(((H2Database) getDatabase()).is1_4_197() ? COLUMNS.COLUMN_TYPE : COLUMNS.TYPE_NAME).as(COLUMNS.COLUMN_TYPE),
choose().when(COLUMNS.NUMERIC_PRECISION.eq(maxP).and(COLUMNS.NUMERIC_SCALE.eq(maxS)), zero())
.otherwise(COLUMNS.CHARACTER_MAXIMUM_LENGTH).as(COLUMNS.CHARACTER_MAXIMUM_LENGTH),
@ -120,15 +126,10 @@ public class H2TableDefinition extends AbstractTableDefinition {
// [#7644] H2 puts DATETIME_PRECISION in NUMERIC_SCALE column
boolean isTimestamp = record.get(COLUMNS.TYPE_NAME).trim().toLowerCase().startsWith("timestamp");
// [#10389] The interval subtype is contained in COLUMN_TYPE, not TYPE_NAME
boolean isInterval = record.get(COLUMNS.TYPE_NAME).trim().toLowerCase().equals("interval");
DataTypeDefinition type = new DefaultDataTypeDefinition(
getDatabase(),
getSchema(),
isInterval
? record.get(COLUMNS.COLUMN_TYPE)
: record.get(COLUMNS.TYPE_NAME),
record.get(COLUMNS.TYPE_NAME),
record.get(COLUMNS.CHARACTER_MAXIMUM_LENGTH),
isTimestamp
? record.get(COLUMNS.NUMERIC_SCALE)

View File

@ -38,6 +38,7 @@
package org.jooq.meta.postgres;
import static org.jooq.impl.DSL.any;
import static org.jooq.impl.DSL.count;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.lower;
@ -84,7 +85,10 @@ public class PostgresTableDefinition extends AbstractTableDefinition {
List<ColumnDefinition> result = new ArrayList<>();
PostgresDatabase database = (PostgresDatabase) getDatabase();
Field<String> dataType = COLUMNS.DATA_TYPE;
Field<String> dataType =
when(COLUMNS.INTERVAL_TYPE.like(any(inline("%YEAR%"), inline("%MONTH%"))), inline("INTERVAL YEAR TO MONTH"))
.when(COLUMNS.INTERVAL_TYPE.like(any(inline("%DAY%"), inline("%HOUR%"), inline("%MINUTE%"), inline("%SECOND%"))), inline("INTERVAL DAY TO SECOND"))
.else_(COLUMNS.DATA_TYPE);
Field<Integer> precision = nvl(COLUMNS.DATETIME_PRECISION, COLUMNS.NUMERIC_PRECISION);
Field<String> serialColumnDefault = inline("nextval('%_seq'::regclass)");

View File

@ -160,6 +160,7 @@ import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import java.util.regex.Pattern;
// ...
import org.jooq.Attachable;
@ -226,6 +227,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
*/
private static final long serialVersionUID = -198499389344950496L;
private static final Set<SQLDialect> REQUIRE_JDBC_DATE_LITERAL = SQLDialect.supportedBy(MYSQL);
private static final Pattern P_INTERVAL_LITERAL = Pattern.compile("INTERVAL '([^']+)' .*");
// Taken from org.postgresql.PGStatement 9223372036825200000
private static final long PG_DATE_POSITIVE_INFINITY = 9223372036825200000L;
@ -630,6 +632,7 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
case H2:
case POSTGRES:
return true;
}
@ -2220,10 +2223,8 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
Object object = ctx.resultSet().getObject(ctx.index());
return object == null ? null : PostgresUtils.toDayToSecond(object);
}
else {
String string = ctx.resultSet().getString(ctx.index());
return string == null ? null : DayToSecond.valueOf(string);
}
else
return parseDTS(ctx, ctx.resultSet().getString(ctx.index()));
}
@Override
@ -2232,16 +2233,22 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
Object object = ctx.statement().getObject(ctx.index());
return object == null ? null : PostgresUtils.toDayToSecond(object);
}
else {
String string = ctx.statement().getString(ctx.index());
return string == null ? null : DayToSecond.valueOf(string);
}
else
return parseDTS(ctx, ctx.statement().getString(ctx.index()));
}
@Override
final DayToSecond get0(BindingGetSQLInputContext<U> ctx) throws SQLException {
String string = ctx.input().readString();
return string == null ? null : DayToSecond.valueOf(string);
return parseDTS(ctx, ctx.input().readString());
}
private final DayToSecond parseDTS(Scope scope, String string) {
if (string == null)
return null;
else if (scope.family() == H2)
return DayToSecond.valueOf(P_INTERVAL_LITERAL.matcher(string).replaceFirst("$1"));
else
return DayToSecond.valueOf(string);
}
@Override
@ -4700,10 +4707,8 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
Object object = ctx.resultSet().getObject(ctx.index());
return object == null ? null : PostgresUtils.toYearToMonth(object);
}
else {
String string = ctx.resultSet().getString(ctx.index());
return string == null ? null : YearToMonth.valueOf(string);
}
else
return parseYTM(ctx, ctx.resultSet().getString(ctx.index()));
}
@Override
@ -4712,16 +4717,22 @@ public class DefaultBinding<T, U> implements Binding<T, U> {
Object object = ctx.statement().getObject(ctx.index());
return object == null ? null : PostgresUtils.toYearToMonth(object);
}
else {
String string = ctx.statement().getString(ctx.index());
return string == null ? null : YearToMonth.valueOf(string);
}
else
return parseYTM(ctx, ctx.statement().getString(ctx.index()));
}
@Override
final YearToMonth get0(BindingGetSQLInputContext<U> ctx) throws SQLException {
String string = ctx.input().readString();
return string == null ? null : YearToMonth.valueOf(string);
return parseYTM(ctx, ctx.input().readString());
}
private final YearToMonth parseYTM(Scope scope, String string) {
if (string == null)
return null;
else if (scope.family() == H2)
return YearToMonth.valueOf(P_INTERVAL_LITERAL.matcher(string).replaceFirst("$1"));
else
return YearToMonth.valueOf(string);
}
@Override

View File

@ -401,15 +401,6 @@ final class Expression<T> extends AbstractField<T> {
break;
}
case H2: {
if (rhs.getType() == YearToMonth.class)
ctx.visit(N_DATEADD).sql("('month', ").visit(p(sign * rhsAsYTM().intValue())).sql(", ").visit(lhs).sql(')');
else
ctx.visit(N_DATEADD).sql("('ms', ").visit(p(sign * (long) rhsAsDTS().getTotalMilli())).sql(", ").visit(lhs).sql(')');
break;
}
case SQLITE: {
boolean ytm = rhs.getType() == YearToMonth.class;
Field<?> interval = p(ytm ? rhsAsYTM().intValue() : rhsAsDTS().getTotalSeconds());
@ -584,6 +575,7 @@ final class Expression<T> extends AbstractField<T> {
case H2:
case POSTGRES:
default:
ctx.visit(new DefaultExpression<>(lhs, operator, wrap(rhs)));