From 56e36a422d4d97f5e638dc3ff63291beaf9b2177 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Wed, 12 Apr 2023 16:22:39 +0200 Subject: [PATCH] [jOOQ/jOOQ#11485] Support for Trino DB - Correct parser and interpreter name cases - Remove workarounds for https://github.com/trinodb/trino/issues/16489 - Emulate LISTAGG .. FILTER - Emulate INSERT INTO - Support TIMESTAMPTZ literals and binds - Correctly recognise ARRAY types in MetaTable - Add M_SOURCES query to MetaSQL --- .../org/jooq/meta/trino/TrinoDatabase.java | 40 ++++++++++++++++++- .../jooq/impl/AbstractAggregateFunction.java | 2 +- .../java/org/jooq/impl/AbstractDMLQuery.java | 2 +- .../java/org/jooq/impl/DefaultBinding.java | 11 +++++ .../java/org/jooq/impl/DefaultDataType.java | 6 +++ .../main/java/org/jooq/impl/Interpreter.java | 1 + .../java/org/jooq/impl/JSONEntryImpl.java | 6 --- jOOQ/src/main/java/org/jooq/impl/ListAgg.java | 10 +++++ jOOQ/src/main/java/org/jooq/impl/MetaSQL.java | 5 +++ jOOQ/src/main/java/org/jooq/impl/Tools.java | 1 + 10 files changed, 74 insertions(+), 10 deletions(-) diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/trino/TrinoDatabase.java b/jOOQ-meta/src/main/java/org/jooq/meta/trino/TrinoDatabase.java index 204064bf29..5dbe96dfe4 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/trino/TrinoDatabase.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/trino/TrinoDatabase.java @@ -53,6 +53,7 @@ import java.util.List; import org.jooq.DSLContext; import org.jooq.Record; import org.jooq.Record12; +import org.jooq.Record4; import org.jooq.Record6; import org.jooq.ResultQuery; import org.jooq.SQLDialect; @@ -65,6 +66,7 @@ import org.jooq.meta.DefaultRelations; import org.jooq.meta.DomainDefinition; import org.jooq.meta.EnumDefinition; import org.jooq.meta.PackageDefinition; +import org.jooq.meta.ResultQueryDatabase; import org.jooq.meta.RoutineDefinition; import org.jooq.meta.SchemaDefinition; import org.jooq.meta.SequenceDefinition; @@ -73,12 +75,14 @@ import org.jooq.meta.UDTDefinition; import org.jooq.meta.XMLSchemaCollectionDefinition; import org.jooq.meta.hsqldb.HSQLDBDatabase; +import org.jetbrains.annotations.Nullable; + /** * The Trino database * * @author Lukas Eder */ -public class TrinoDatabase extends AbstractDatabase { +public class TrinoDatabase extends AbstractDatabase implements ResultQueryDatabase { @Override protected DSLContext create0() { @@ -96,7 +100,7 @@ public class TrinoDatabase extends AbstractDatabase { when(TABLES.TABLE_TYPE.eq(inline("VIEW")), inline(TableType.VIEW.name())) .else_(inline(TableType.TABLE.name())).trim().as("table_type"), when(VIEWS.VIEW_DEFINITION.lower().like(inline("create%")), VIEWS.VIEW_DEFINITION) - .else_(inline("create view \"").concat(TABLES.TABLE_NAME).concat("\" as ").concat(VIEWS.VIEW_DEFINITION)).as(VIEWS.VIEW_DEFINITION) + .else_(inline("create view \"").concat(TABLES.TABLE_NAME).concat(inline("\" as ")).concat(VIEWS.VIEW_DEFINITION)).as(VIEWS.VIEW_DEFINITION) ) .from(TABLES) .leftJoin(VIEWS) @@ -135,6 +139,38 @@ public class TrinoDatabase extends AbstractDatabase { protected void loadCheckConstraints(DefaultRelations relations) throws SQLException { } + @Override + public final ResultQuery> primaryKeys(List schemas) { + return null; + } + + @Override + public final ResultQuery> uniqueKeys(List schemas) { + return null; + } + + @Override + public final ResultQuery> sequences(List schemas) { + return null; + } + + @Override + public final ResultQuery> sources(List schemas) { + return create() + .select( + inline("").as(VIEWS.TABLE_CATALOG), + VIEWS.TABLE_SCHEMA, + VIEWS.TABLE_NAME, + when(VIEWS.VIEW_DEFINITION.lower().like(inline("create%")), VIEWS.VIEW_DEFINITION) + .else_(inline("create view \"").concat(VIEWS.TABLE_NAME).concat(inline("\" as ")).concat(VIEWS.VIEW_DEFINITION)).as(VIEWS.VIEW_DEFINITION) + ) + .from(VIEWS) + .where(VIEWS.TABLE_SCHEMA.in(schemas)) + .orderBy( + VIEWS.TABLE_SCHEMA, + VIEWS.TABLE_NAME); + } + @Override protected List getDomains0() throws SQLException { return new ArrayList<>(); diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractAggregateFunction.java b/jOOQ/src/main/java/org/jooq/impl/AbstractAggregateFunction.java index 2af5f3b31b..b65ec718eb 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractAggregateFunction.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractAggregateFunction.java @@ -343,7 +343,7 @@ implements } } - private final boolean supportsFilter(Context ctx) { + /* non-final */ boolean supportsFilter(Context ctx) { return !( NO_SUPPORT_FILTER.contains(ctx.dialect()) || NO_SUPPORT_WINDOW_FILTER.contains(ctx.dialect()) && isWindow() diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java index c3f07bd05c..5a3f227ebd 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDMLQuery.java @@ -181,7 +181,7 @@ abstract class AbstractDMLQuery extends AbstractRowCountQuery private static final JooqLogger log = JooqLogger.getLogger(AbstractQuery.class); - private static final Set NO_SUPPORT_INSERT_ALIASED_TABLE = SQLDialect.supportedBy(DERBY, FIREBIRD, H2, MARIADB, MYSQL); + private static final Set NO_SUPPORT_INSERT_ALIASED_TABLE = SQLDialect.supportedBy(DERBY, FIREBIRD, H2, MARIADB, MYSQL, TRINO); private static final Set NO_NATIVE_SUPPORT_INSERT_RETURNING = SQLDialect.supportedUntil(CUBRID, DERBY, H2, HSQLDB, IGNITE, MYSQL, SQLITE, TRINO); private static final Set NO_NATIVE_SUPPORT_UPDATE_RETURNING = SQLDialect.supportedUntil(CUBRID, DERBY, H2, HSQLDB, IGNITE, MYSQL, SQLITE, TRINO); private static final Set NO_NATIVE_SUPPORT_DELETE_RETURNING = SQLDialect.supportedUntil(CUBRID, DERBY, H2, HSQLDB, IGNITE, MYSQL, SQLITE, TRINO); diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java index d38e67e7fe..fac907f18f 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultBinding.java @@ -823,6 +823,16 @@ public class DefaultBinding implements Binding { } } + if (dataType.getType() == OffsetDateTime.class || + dataType.getType() == OffsetTime.class || + dataType.getType() == Instant.class + ) { + switch (ctx.family()) { + case TRINO: + return true; + } + } + @@ -3320,6 +3330,7 @@ public class DefaultBinding implements Binding { case HSQLDB: + case TRINO: ctx.render().visit(K_TIMESTAMP).sql(" '").sql(escape(format(value, family), ctx.render())).sql('\''); break; diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultDataType.java b/jOOQ/src/main/java/org/jooq/impl/DefaultDataType.java index c7ac52a8e8..553015e064 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultDataType.java @@ -48,6 +48,7 @@ import static org.jooq.SQLDialect.MARIADB; import static org.jooq.SQLDialect.MYSQL; import static org.jooq.SQLDialect.POSTGRES; import static org.jooq.SQLDialect.SQLITE; +import static org.jooq.SQLDialect.TRINO; import static org.jooq.SQLDialect.YUGABYTEDB; import static org.jooq.impl.CommentImpl.NO_COMMENT; import static org.jooq.impl.DSL.systemName; @@ -141,6 +142,7 @@ public class DefaultDataType extends AbstractDataTypeX { private static final Set NO_SUPPORT_TIMESTAMP_PRECISION = SQLDialect.supportedBy(FIREBIRD, MYSQL, SQLITE); private static final Set SUPPORT_POSTGRES_ARRAY_NOTATION = SQLDialect.supportedBy(POSTGRES, YUGABYTEDB); private static final Set SUPPORT_HSQLDB_ARRAY_NOTATION = SQLDialect.supportedBy(H2, HSQLDB); + private static final Set SUPPORT_TRINO_ARRAY_NOTATION = SQLDialect.supportedBy(TRINO); /** * A pattern for data type name normalisation. @@ -692,6 +694,10 @@ public class DefaultDataType extends AbstractDataTypeX { else if (result == null && SUPPORT_HSQLDB_ARRAY_NOTATION.contains(dialect) && upper.equals("ARRAY")) result = SQLDataType.OTHER.getArrayDataType(); + // [#11485] Trino lists arrays as array(component_type) + else if (result == null && SUPPORT_TRINO_ARRAY_NOTATION.contains(dialect) && upper.startsWith("ARRAY(")) + result = getDataType(dialect, typeName.substring(6, typeName.length() - 1)).getArrayDataType(); + diff --git a/jOOQ/src/main/java/org/jooq/impl/Interpreter.java b/jOOQ/src/main/java/org/jooq/impl/Interpreter.java index 9c333b9ebf..4abef4c23b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Interpreter.java +++ b/jOOQ/src/main/java/org/jooq/impl/Interpreter.java @@ -1422,6 +1422,7 @@ final class Interpreter { case MARIADB: case MYSQL: case SQLITE: + case TRINO: return InterpreterNameLookupCaseSensitivity.NEVER; case DEFAULT: diff --git a/jOOQ/src/main/java/org/jooq/impl/JSONEntryImpl.java b/jOOQ/src/main/java/org/jooq/impl/JSONEntryImpl.java index d6b6d73d44..595d63aeb7 100644 --- a/jOOQ/src/main/java/org/jooq/impl/JSONEntryImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/JSONEntryImpl.java @@ -298,12 +298,6 @@ final class JSONEntryImpl extends AbstractQueryPart implements JSONEntry, case TRINO: - // [#11485] https://github.com/trinodb/trino/issues/16489 - // This isn't necessary when emulating JSON_OBJECT and JSON_ARRAY - // with CAST(MAP) and CAST(ARRAY), however: - // if (field instanceof Param) - // return inlined(field); - // [#11485] CHAR types can't be cast to JSON: https://trino.io/docs/current/functions/json.html#cast-to-json if (type.getSQLDataType() == SQLDataType.CHAR) return field.cast(VARCHAR); diff --git a/jOOQ/src/main/java/org/jooq/impl/ListAgg.java b/jOOQ/src/main/java/org/jooq/impl/ListAgg.java index 020582b669..8330202b14 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ListAgg.java +++ b/jOOQ/src/main/java/org/jooq/impl/ListAgg.java @@ -180,6 +180,16 @@ final class ListAgg extends AbstractAggregateFunction implements UNotYet return i == 0; } + @Override + boolean supportsFilter(Context ctx) { + switch (ctx.family()) { + case TRINO: + return false; + default: + return super.supportsFilter(ctx); + } + } + /** * [#1273] LIST_AGG emulation for MySQL */ diff --git a/jOOQ/src/main/java/org/jooq/impl/MetaSQL.java b/jOOQ/src/main/java/org/jooq/impl/MetaSQL.java index ad34a1bce4..4769d8dc16 100644 --- a/jOOQ/src/main/java/org/jooq/impl/MetaSQL.java +++ b/jOOQ/src/main/java/org/jooq/impl/MetaSQL.java @@ -103,6 +103,7 @@ final class MetaSQL { + M_SEQUENCES.put(DERBY, "select cast(null as varchar(32672)) as catalog, alias_8805161.SCHEMANAME, SYS.SYSSEQUENCES.SEQUENCENAME, SYS.SYSSEQUENCES.SEQUENCEDATATYPE, cast(null as int) as numeric_precision, cast(null as int) as numeric_scale, nullif(SYS.SYSSEQUENCES.STARTVALUE, 1) as STARTVALUE, nullif(SYS.SYSSEQUENCES.INCREMENT, 1) as INCREMENT, nullif(SYS.SYSSEQUENCES.MINIMUMVALUE, case when cast(SYS.SYSSEQUENCES.SEQUENCEDATATYPE as varchar(32672)) = 'SMALLINT' then -32768 when cast(SYS.SYSSEQUENCES.SEQUENCEDATATYPE as varchar(32672)) = 'INTEGER' then -2147483648 when cast(SYS.SYSSEQUENCES.SEQUENCEDATATYPE as varchar(32672)) = 'BIGINT' then -9223372036854775808 end) as MINIMUMVALUE, nullif(SYS.SYSSEQUENCES.MAXIMUMVALUE, case when cast(SYS.SYSSEQUENCES.SEQUENCEDATATYPE as varchar(32672)) = 'SMALLINT' then 32767 when cast(SYS.SYSSEQUENCES.SEQUENCEDATATYPE as varchar(32672)) = 'INTEGER' then 2147483647 when cast(SYS.SYSSEQUENCES.SEQUENCEDATATYPE as varchar(32672)) = 'BIGINT' then 9223372036854775807 end) as MAXIMUMVALUE, (SYS.SYSSEQUENCES.CYCLEOPTION = 'Y') as CYCLEOPTION, cast(null as bigint) as cache from (SYS.SYSSEQUENCES join SYS.SYSSCHEMAS as alias_8805161 on SYS.SYSSEQUENCES.SCHEMAID = alias_8805161.SCHEMAID) where cast(alias_8805161.SCHEMANAME as varchar(32672)) in (cast(? as varchar(32672))) order by alias_8805161.SCHEMANAME, SYS.SYSSEQUENCES.SEQUENCENAME"); @@ -153,6 +154,7 @@ final class MetaSQL { + M_SEQUENCES_INCLUDING_SYSTEM_SEQUENCES.put(DERBY, "select cast(null as varchar(32672)) as catalog, alias_8805161.SCHEMANAME, SYS.SYSSEQUENCES.SEQUENCENAME, SYS.SYSSEQUENCES.SEQUENCEDATATYPE, cast(null as int) as numeric_precision, cast(null as int) as numeric_scale, nullif(SYS.SYSSEQUENCES.STARTVALUE, 1) as STARTVALUE, nullif(SYS.SYSSEQUENCES.INCREMENT, 1) as INCREMENT, nullif(SYS.SYSSEQUENCES.MINIMUMVALUE, case when cast(SYS.SYSSEQUENCES.SEQUENCEDATATYPE as varchar(32672)) = 'SMALLINT' then -32768 when cast(SYS.SYSSEQUENCES.SEQUENCEDATATYPE as varchar(32672)) = 'INTEGER' then -2147483648 when cast(SYS.SYSSEQUENCES.SEQUENCEDATATYPE as varchar(32672)) = 'BIGINT' then -9223372036854775808 end) as MINIMUMVALUE, nullif(SYS.SYSSEQUENCES.MAXIMUMVALUE, case when cast(SYS.SYSSEQUENCES.SEQUENCEDATATYPE as varchar(32672)) = 'SMALLINT' then 32767 when cast(SYS.SYSSEQUENCES.SEQUENCEDATATYPE as varchar(32672)) = 'INTEGER' then 2147483647 when cast(SYS.SYSSEQUENCES.SEQUENCEDATATYPE as varchar(32672)) = 'BIGINT' then 9223372036854775807 end) as MAXIMUMVALUE, (SYS.SYSSEQUENCES.CYCLEOPTION = 'Y') as CYCLEOPTION, cast(null as bigint) as cache from (SYS.SYSSEQUENCES join SYS.SYSSCHEMAS as alias_8805161 on SYS.SYSSEQUENCES.SCHEMAID = alias_8805161.SCHEMAID) where cast(alias_8805161.SCHEMANAME as varchar(32672)) in (cast(? as varchar(32672))) order by alias_8805161.SCHEMANAME, SYS.SYSSEQUENCES.SEQUENCENAME"); @@ -203,6 +205,7 @@ final class MetaSQL { + M_SOURCES.put(DERBY, "select cast(null as varchar(32672)) as catalog, alias_57844683.SCHEMANAME, SYS.SYSTABLES.TABLENAME, SYS.SYSVIEWS.VIEWDEFINITION from (SYS.SYSTABLES join SYS.SYSSCHEMAS as alias_57844683 on SYS.SYSTABLES.SCHEMAID = alias_57844683.SCHEMAID) left outer join SYS.SYSVIEWS on SYS.SYSTABLES.TABLEID = SYS.SYSVIEWS.TABLEID where cast(alias_57844683.SCHEMANAME as varchar(32672)) in (cast(? as varchar(32672))) order by alias_57844683.SCHEMANAME, SYS.SYSTABLES.TABLENAME"); @@ -213,6 +216,7 @@ final class MetaSQL { M_SOURCES.put(MYSQL, "select information_schema.VIEWS.TABLE_CATALOG, information_schema.VIEWS.TABLE_SCHEMA, information_schema.VIEWS.TABLE_NAME, information_schema.VIEWS.VIEW_DEFINITION from information_schema.VIEWS where information_schema.VIEWS.TABLE_SCHEMA in (?) order by information_schema.VIEWS.TABLE_SCHEMA, information_schema.VIEWS.TABLE_NAME"); M_SOURCES.put(POSTGRES, "select information_schema.views.table_catalog, information_schema.views.table_schema, information_schema.views.table_name, information_schema.views.view_definition from information_schema.views where information_schema.views.table_schema in (?) order by information_schema.views.table_schema, information_schema.views.table_name"); M_SOURCES.put(SQLITE, "select null as catalog, null as schema, sqlite_master.name, sqlite_master.sql from sqlite_master order by sqlite_master.name"); + M_SOURCES.put(TRINO, "select '' TABLE_CATALOG, INFORMATION_SCHEMA.VIEWS.TABLE_SCHEMA, INFORMATION_SCHEMA.VIEWS.TABLE_NAME, case when lower(INFORMATION_SCHEMA.VIEWS.VIEW_DEFINITION) like 'create%' then INFORMATION_SCHEMA.VIEWS.VIEW_DEFINITION else ((('create view \"' || INFORMATION_SCHEMA.VIEWS.TABLE_NAME) || '\" as ') || INFORMATION_SCHEMA.VIEWS.VIEW_DEFINITION) end VIEW_DEFINITION from INFORMATION_SCHEMA.VIEWS where INFORMATION_SCHEMA.VIEWS.TABLE_SCHEMA in (?) order by INFORMATION_SCHEMA.VIEWS.TABLE_SCHEMA, INFORMATION_SCHEMA.VIEWS.TABLE_NAME"); M_SOURCES.put(YUGABYTEDB, "select information_schema.views.table_catalog, information_schema.views.table_schema, information_schema.views.table_name, information_schema.views.view_definition from information_schema.views where information_schema.views.table_schema in (?) order by information_schema.views.table_schema, information_schema.views.table_name"); @@ -283,6 +287,7 @@ final class MetaSQL { + } diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index cfda329dca..94b7b718ca 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -6896,6 +6896,7 @@ final class Tools { case MARIADB: case MYSQL: case SQLITE: + case TRINO: return ParseNameCase.AS_IS; default: