[jOOQ/jOOQ#252] Added H2 code generation support
This commit is contained in:
parent
196bc03ee5
commit
79ea77c896
@ -54,6 +54,10 @@ import java.util.regex.Pattern;
|
||||
import org.jooq.Name;
|
||||
import org.jooq.SQLDialect;
|
||||
import org.jooq.exception.SQLDialectNotSupportedException;
|
||||
import org.jooq.meta.DataTypeDefinition;
|
||||
import org.jooq.meta.Database;
|
||||
import org.jooq.meta.DefaultDataTypeDefinition;
|
||||
import org.jooq.meta.SchemaDefinition;
|
||||
import org.jooq.util.h2.H2DataType;
|
||||
|
||||
/**
|
||||
@ -484,6 +488,29 @@ class GenerationUtil {
|
||||
return qualifiedJavaType.replaceAll(".*\\.", "");
|
||||
}
|
||||
|
||||
static DataTypeDefinition getArrayBaseType(SQLDialect dialect, DataTypeDefinition type) {
|
||||
Name name = getArrayBaseType(dialect, type.getType(), type.getQualifiedUserType());
|
||||
|
||||
return new DefaultDataTypeDefinition(
|
||||
type.getDatabase(),
|
||||
type.getSchema(),
|
||||
name.last(),
|
||||
type.getLength(),
|
||||
type.getPrecision(),
|
||||
type.getScale(),
|
||||
type.isNullable(),
|
||||
type.isReadonly(),
|
||||
type.getGeneratedAlwaysAs(),
|
||||
type.getDefaultValue(),
|
||||
type.isIdentity(),
|
||||
name,
|
||||
type.getGenerator(),
|
||||
type.getConverter(),
|
||||
type.getBinding(),
|
||||
type.getJavaType()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the base type for an array type, depending on the RDBMS dialect
|
||||
*/
|
||||
|
||||
@ -9179,24 +9179,7 @@ public class JavaGenerator extends AbstractGenerator {
|
||||
|
||||
// [#4388] TODO: Improve array handling
|
||||
if (database.isArrayType(type.getType())) {
|
||||
Name baseType = GenerationUtil.getArrayBaseType(db.getDialect(), type.getType(), type.getQualifiedUserType());
|
||||
return getTypeReference(
|
||||
db,
|
||||
type.getSchema(),
|
||||
out,
|
||||
baseType.last(),
|
||||
type.getPrecision(),
|
||||
type.getScale(),
|
||||
type.getLength(),
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
baseType
|
||||
) + ".getArrayDataType()";
|
||||
return getJavaTypeReference(db, GenerationUtil.getArrayBaseType(db.getDialect(), type), out) + ".getArrayDataType()";
|
||||
}
|
||||
else {
|
||||
return getTypeReference(
|
||||
|
||||
@ -391,6 +391,10 @@ public abstract class AbstractDatabase implements Database {
|
||||
// [A-Za-z_$#][A-Za-z0-9_$#]+ in generated jOOQ-meta code.
|
||||
configuration.settings().setRenderQuotedNames(getRenderQuotedNames());
|
||||
|
||||
// [#252] We're not quoting identifiers. Hence the default name path
|
||||
// separator is illegal
|
||||
configuration.settings().setNamePathSeparator("__");
|
||||
|
||||
if (muteExceptions) {
|
||||
return DSL.using(configuration);
|
||||
}
|
||||
|
||||
@ -56,6 +56,7 @@ import static org.jooq.impl.DSL.not;
|
||||
import static org.jooq.impl.DSL.nullif;
|
||||
import static org.jooq.impl.DSL.nvl;
|
||||
import static org.jooq.impl.DSL.one;
|
||||
import static org.jooq.impl.DSL.row;
|
||||
import static org.jooq.impl.DSL.select;
|
||||
import static org.jooq.impl.DSL.upper;
|
||||
import static org.jooq.impl.DSL.when;
|
||||
@ -87,9 +88,11 @@ import java.math.BigDecimal;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.jooq.DSLContext;
|
||||
import org.jooq.Field;
|
||||
@ -135,6 +138,7 @@ import org.jooq.meta.hsqldb.information_schema.Tables;
|
||||
import org.jooq.meta.hsqldb.information_schema.tables.CheckConstraints;
|
||||
import org.jooq.meta.hsqldb.information_schema.tables.DomainConstraints;
|
||||
import org.jooq.meta.hsqldb.information_schema.tables.Domains;
|
||||
import org.jooq.meta.hsqldb.information_schema.tables.ElementTypes;
|
||||
import org.jooq.meta.hsqldb.information_schema.tables.KeyColumnUsage;
|
||||
import org.jooq.tools.JooqLogger;
|
||||
import org.jooq.tools.StringUtils;
|
||||
@ -151,6 +155,51 @@ public class H2Database extends AbstractDatabase implements ResultQueryDatabase
|
||||
private static final long DEFAULT_SEQUENCE_CACHE = 32;
|
||||
private static final long DEFAULT_SEQUENCE_MAXVALUE = Long.MAX_VALUE;
|
||||
|
||||
static final record ElementTypeLookupKey (String schema, String name, String identifier) {}
|
||||
static final record ElementType (String dataType, Long length, Long precision, Long scale, String identifier, int dimension) {}
|
||||
private Map<ElementTypeLookupKey, ElementType> ELEMENT_TYPE_LOOKUP;
|
||||
|
||||
ElementType elementTypeLookup(ElementTypeLookupKey key) {
|
||||
if (ELEMENT_TYPE_LOOKUP == null) {
|
||||
ElementTypes e = ELEMENT_TYPES;
|
||||
|
||||
ELEMENT_TYPE_LOOKUP = create().fetchMap(
|
||||
select(
|
||||
row(
|
||||
e.OBJECT_SCHEMA,
|
||||
e.OBJECT_NAME,
|
||||
e.COLLECTION_TYPE_IDENTIFIER
|
||||
).mapping(ElementTypeLookupKey::new),
|
||||
row(
|
||||
e.DATA_TYPE,
|
||||
e.CHARACTER_MAXIMUM_LENGTH,
|
||||
coalesce(e.DATETIME_PRECISION, e.NUMERIC_PRECISION),
|
||||
e.NUMERIC_SCALE,
|
||||
e.DTD_IDENTIFIER,
|
||||
inline(1)
|
||||
).mapping(ElementType::new)
|
||||
)
|
||||
.from(e)
|
||||
.where(e.OBJECT_SCHEMA.in(getInputSchemata()))
|
||||
);
|
||||
|
||||
AtomicBoolean repeat = new AtomicBoolean(true);
|
||||
while (repeat.getAndSet(false)) {
|
||||
ELEMENT_TYPE_LOOKUP.replaceAll((k, v) -> {
|
||||
ElementType et = ELEMENT_TYPE_LOOKUP.get(new ElementTypeLookupKey(k.schema(), k.name(), v.identifier()));
|
||||
|
||||
if (et != null) {
|
||||
repeat.set(true);
|
||||
return new ElementType(et.dataType(), et.length(), et.precision(), et.scale(), et.identifier(), v.dimension() + 1); }
|
||||
else
|
||||
return v;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return ELEMENT_TYPE_LOOKUP.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DSLContext create0() {
|
||||
return DSL.using(getConnection(), SQLDialect.H2);
|
||||
|
||||
@ -40,37 +40,34 @@ 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.coalesce;
|
||||
import static org.jooq.impl.DSL.concat;
|
||||
import static org.jooq.impl.DSL.condition;
|
||||
import static org.jooq.impl.DSL.field;
|
||||
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.nvl;
|
||||
import static org.jooq.impl.DSL.when;
|
||||
import static org.jooq.impl.SQLDataType.BOOLEAN;
|
||||
import static org.jooq.impl.SQLDataType.INTEGER;
|
||||
import static org.jooq.meta.h2.information_schema.Tables.COLUMNS;
|
||||
import static org.jooq.meta.hsqldb.information_schema.Tables.ELEMENT_TYPES;
|
||||
import static org.jooq.tools.StringUtils.defaultString;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import org.jooq.Field;
|
||||
import org.jooq.Name;
|
||||
import org.jooq.Param;
|
||||
import org.jooq.Record;
|
||||
import org.jooq.Table;
|
||||
import org.jooq.TableOptions.TableType;
|
||||
import org.jooq.meta.AbstractTableDefinition;
|
||||
import org.jooq.meta.ColumnDefinition;
|
||||
import org.jooq.meta.DataTypeDefinition;
|
||||
import org.jooq.meta.Database;
|
||||
import org.jooq.meta.DefaultColumnDefinition;
|
||||
import org.jooq.meta.DefaultDataTypeDefinition;
|
||||
import org.jooq.meta.SchemaDefinition;
|
||||
import org.jooq.meta.h2.H2Database.ElementType;
|
||||
import org.jooq.meta.h2.H2Database.ElementTypeLookupKey;
|
||||
import org.jooq.meta.hsqldb.information_schema.Tables;
|
||||
|
||||
/**
|
||||
@ -106,23 +103,26 @@ public class H2TableDefinition extends AbstractTableDefinition {
|
||||
|
||||
H2Database db = (H2Database) getDatabase();
|
||||
|
||||
|
||||
// [#252] While recursing on ELEMENT_TYPES to detect multi dimensional
|
||||
// arrays looks like the cleanest approach, H2's recursive SQL
|
||||
// has been troubled by a ton of bugs in the past. Hard-coded
|
||||
// left joins are an ugly option, which is why we opt for doing
|
||||
// this calculation in Java, for once.
|
||||
// See also: https://github.com/jOOQ/jOOQ/issues/252#issuecomment-1240484853
|
||||
for (Record record : create().select(
|
||||
COLUMNS.COLUMN_NAME,
|
||||
COLUMNS.ORDINAL_POSITION,
|
||||
|
||||
// [#13302] Use explicit NULL check to prevent side-effects from H2's compatibility run modes
|
||||
when(ELEMENT_TYPES.DATA_TYPE.isNotNull(), concat(ELEMENT_TYPES.DATA_TYPE, inline(" ARRAY")))
|
||||
// [#2230] [#11733] 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("%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_(Tables.COLUMNS.DATA_TYPE).as(COLUMNS.TYPE_NAME),
|
||||
nvl(ELEMENT_TYPES.CHARACTER_MAXIMUM_LENGTH, COLUMNS.CHARACTER_MAXIMUM_LENGTH).as(COLUMNS.CHARACTER_MAXIMUM_LENGTH),
|
||||
COLUMNS.CHARACTER_MAXIMUM_LENGTH,
|
||||
coalesce(
|
||||
ELEMENT_TYPES.DATETIME_PRECISION,
|
||||
ELEMENT_TYPES.NUMERIC_PRECISION,
|
||||
COLUMNS.DATETIME_PRECISION,
|
||||
COLUMNS.DATETIME_PRECISION.coerce(COLUMNS.NUMERIC_PRECISION),
|
||||
COLUMNS.NUMERIC_PRECISION).as(COLUMNS.NUMERIC_PRECISION),
|
||||
nvl(ELEMENT_TYPES.NUMERIC_SCALE, COLUMNS.NUMERIC_SCALE).as(COLUMNS.NUMERIC_SCALE),
|
||||
COLUMNS.NUMERIC_SCALE,
|
||||
COLUMNS.IS_NULLABLE,
|
||||
Tables.COLUMNS.IS_GENERATED.eq(inline("ALWAYS")).as(COLUMNS.IS_COMPUTED),
|
||||
Tables.COLUMNS.GENERATION_EXPRESSION,
|
||||
@ -130,13 +130,10 @@ public class H2TableDefinition extends AbstractTableDefinition {
|
||||
COLUMNS.REMARKS,
|
||||
Tables.COLUMNS.IS_IDENTITY.eq(inline("YES")).as(Tables.COLUMNS.IS_IDENTITY),
|
||||
COLUMNS.DOMAIN_SCHEMA,
|
||||
COLUMNS.DOMAIN_NAME
|
||||
COLUMNS.DOMAIN_NAME,
|
||||
Tables.COLUMNS.DTD_IDENTIFIER
|
||||
)
|
||||
.from(COLUMNS)
|
||||
.leftJoin(ELEMENT_TYPES)
|
||||
.on(Tables.COLUMNS.TABLE_SCHEMA.equal(ELEMENT_TYPES.OBJECT_SCHEMA))
|
||||
.and(Tables.COLUMNS.TABLE_NAME.equal(ELEMENT_TYPES.OBJECT_NAME))
|
||||
.and(Tables.COLUMNS.DTD_IDENTIFIER.equal(ELEMENT_TYPES.COLLECTION_TYPE_IDENTIFIER))
|
||||
.where(COLUMNS.TABLE_SCHEMA.equal(getSchema().getName()))
|
||||
.and(COLUMNS.TABLE_NAME.equal(getName()))
|
||||
.and(!getDatabase().getIncludeInvisibleColumns()
|
||||
@ -163,13 +160,18 @@ public class H2TableDefinition extends AbstractTableDefinition {
|
||||
? getDatabase().getSchema(record.get(COLUMNS.DOMAIN_SCHEMA))
|
||||
: getSchema();
|
||||
|
||||
ElementType et = db.elementTypeLookup(new ElementTypeLookupKey(getSchema().getName(), getName(), record.get(Tables.COLUMNS.DTD_IDENTIFIER)));
|
||||
|
||||
if (et == null)
|
||||
et = new ElementType(record.get(COLUMNS.TYPE_NAME), record.get(COLUMNS.CHARACTER_MAXIMUM_LENGTH), record.get(COLUMNS.NUMERIC_PRECISION), record.get(COLUMNS.NUMERIC_SCALE), null, 0);
|
||||
|
||||
DataTypeDefinition type = new DefaultDataTypeDefinition(
|
||||
getDatabase(),
|
||||
typeSchema == null ? getSchema() : typeSchema,
|
||||
record.get(COLUMNS.TYPE_NAME),
|
||||
record.get(COLUMNS.CHARACTER_MAXIMUM_LENGTH),
|
||||
record.get(COLUMNS.NUMERIC_PRECISION),
|
||||
record.get(COLUMNS.NUMERIC_SCALE),
|
||||
et.dataType() + IntStream.range(0, et.dimension()).mapToObj(i -> " ARRAY").collect(Collectors.joining()),
|
||||
et.length(),
|
||||
et.precision(),
|
||||
et.scale(),
|
||||
record.get(COLUMNS.IS_NULLABLE, boolean.class),
|
||||
isIdentity || isComputed ? null : record.get(COLUMNS.COLUMN_DEFAULT),
|
||||
userType
|
||||
|
||||
Loading…
Reference in New Issue
Block a user