From f58fc103ed8dc92d47fd631fec4926a91874dc9f Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Mon, 18 Sep 2023 14:49:19 +0200 Subject: [PATCH] [jOOQ/jOOQ#9483] Include sources of materialized views in PostgresDatabase::sources --- .../jooq/meta/postgres/PostgresDatabase.java | 43 ++++++++++++++++--- jOOQ/src/main/java/org/jooq/TableOptions.java | 12 +++++- .../src/main/java/org/jooq/impl/MetaImpl.java | 8 +++- jOOQ/src/main/java/org/jooq/impl/MetaSQL.java | 4 +- 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/postgres/PostgresDatabase.java b/jOOQ-meta/src/main/java/org/jooq/meta/postgres/PostgresDatabase.java index 82cc82c929..dd88508800 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/postgres/PostgresDatabase.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/postgres/PostgresDatabase.java @@ -55,6 +55,7 @@ import static org.jooq.impl.DSL.cast; import static org.jooq.impl.DSL.coalesce; import static org.jooq.impl.DSL.condition; import static org.jooq.impl.DSL.count; +import static org.jooq.impl.DSL.currentCatalog; import static org.jooq.impl.DSL.decode; import static org.jooq.impl.DSL.falseCondition; import static org.jooq.impl.DSL.field; @@ -137,6 +138,7 @@ import org.jooq.Result; import org.jooq.ResultQuery; import org.jooq.SQLDialect; import org.jooq.Select; +import org.jooq.SelectOrderByStep; import org.jooq.SortOrder; import org.jooq.Table; import org.jooq.TableField; @@ -188,6 +190,8 @@ import org.jooq.meta.postgres.pg_catalog.tables.PgInherits; import org.jooq.meta.postgres.pg_catalog.tables.PgType; import org.jooq.tools.JooqLogger; +import org.jetbrains.annotations.NotNull; + /** * Postgres uses the ANSI default INFORMATION_SCHEMA, but unfortunately ships * with a non-capitalised version of it: information_schema. Hence @@ -661,21 +665,46 @@ public class PostgresDatabase extends AbstractDatabase implements ResultQueryDat @Override public ResultQuery> sources(List schemas) { - return create() - .select( + Select> s = + select( 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) + .from(VIEWS); + + // [#9483] Some dialects include materialized views in the INFORMATION_SCHEMA.VIEWS view + if (!informationSchemaViewsContainsMaterializedViews()) { + s = s.unionAll( + select( + currentCatalog(), + PG_CLASS.pgNamespace().NSPNAME, + PG_CLASS.RELNAME, + inline("create materialized view \"").concat(PG_CLASS.RELNAME).concat(inline("\" as ")).concat(field("pg_get_viewdef({0})", VARCHAR, PG_CLASS.OID))) + .from(PG_CLASS) + .where(PG_CLASS.RELKIND.eq(inline("m"))) + ); + } + + Table t = s.asTable("t"); + + return create() + .select( + t.field(VIEWS.TABLE_CATALOG), + t.field(VIEWS.TABLE_SCHEMA), + t.field(VIEWS.TABLE_NAME), + t.field(VIEWS.VIEW_DEFINITION)) + .from(t) + .where(t.field(VIEWS.TABLE_SCHEMA).in(schemas)) + .orderBy(1, 2, 3) ; } + protected boolean informationSchemaViewsContainsMaterializedViews() { + return false; + } + @Override public ResultQuery> comments(List schemas) { return null; diff --git a/jOOQ/src/main/java/org/jooq/TableOptions.java b/jOOQ/src/main/java/org/jooq/TableOptions.java index 6eba7dff13..16ce98fefa 100644 --- a/jOOQ/src/main/java/org/jooq/TableOptions.java +++ b/jOOQ/src/main/java/org/jooq/TableOptions.java @@ -61,7 +61,7 @@ public final class TableOptions implements Serializable { private static final TableOptions C_EXPRESSION = new TableOptions(TableType.EXPRESSION); private static final TableOptions C_FUNCTION = new TableOptions(TableType.FUNCTION); - private static final TableOptions C_MATERIALIZED_VIEW = materializedView(null); + private static final TableOptions C_MATERIALIZED_VIEW = materializedView((String) null); private static final TableOptions C_TABLE = new TableOptions(TableType.TABLE); private static final TableOptions C_TEMPORARY = new TableOptions(TableType.TEMPORARY); private static final TableOptions C_VIEW = view((String) null); @@ -204,6 +204,16 @@ public final class TableOptions implements Serializable { return new TableOptions(TableType.MATERIALIZED_VIEW, select); } + /** + * Create a new {@link TableOptions} object for a + * {@link TableType#MATERIALIZED_VIEW} of unknown content. + */ + @NotNull + public static final TableOptions materializedView(String source) { + return new TableOptions(TableType.MATERIALIZED_VIEW, source); + } + + /** * Create a new {@link TableOptions} object for a {@link TableType#EXPRESSION}. */ diff --git a/jOOQ/src/main/java/org/jooq/impl/MetaImpl.java b/jOOQ/src/main/java/org/jooq/impl/MetaImpl.java index c6357e0460..76530d7d1a 100644 --- a/jOOQ/src/main/java/org/jooq/impl/MetaImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/MetaImpl.java @@ -63,6 +63,7 @@ import static org.jooq.SQLDialect.SQLITE; // ... import static org.jooq.SQLDialect.TRINO; import static org.jooq.SQLDialect.YUGABYTEDB; +import static org.jooq.TableOptions.TableType.MATERIALIZED_VIEW; import static org.jooq.impl.AbstractNamed.findIgnoreCase; import static org.jooq.impl.DSL.comment; import static org.jooq.impl.DSL.condition; @@ -814,11 +815,14 @@ final class MetaImpl extends AbstractMeta { }; private static final TableOptions tableOption(DSLContext ctx, MetaSchema schema, String tableName, TableType tableType) { - if (tableType == TableType.VIEW) { + if (tableType.isView()) { String sql = M_SOURCES(ctx.dialect()); if (sql != null) - return TableOptions.view(schema.source(tableName)); + if (tableType == MATERIALIZED_VIEW) + return TableOptions.materializedView(schema.source(tableName)); + else + return TableOptions.view(schema.source(tableName)); } return TableOptions.of(tableType); diff --git a/jOOQ/src/main/java/org/jooq/impl/MetaSQL.java b/jOOQ/src/main/java/org/jooq/impl/MetaSQL.java index 8d0599c5ee..2f3616d7e0 100644 --- a/jOOQ/src/main/java/org/jooq/impl/MetaSQL.java +++ b/jOOQ/src/main/java/org/jooq/impl/MetaSQL.java @@ -260,10 +260,10 @@ final class MetaSQL { M_SOURCES.put(HSQLDB, "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 (cast(? as varchar(128))) order by INFORMATION_SCHEMA.VIEWS.TABLE_SCHEMA, INFORMATION_SCHEMA.VIEWS.TABLE_NAME"); M_SOURCES.put(MARIADB, "select information_schema.VIEWS.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 concat(concat(concat('create view `', information_schema.VIEWS.TABLE_NAME), '` as '), information_schema.VIEWS.VIEW_DEFINITION) end as 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(MYSQL, "select information_schema.VIEWS.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 concat(concat(concat('create view `', information_schema.VIEWS.TABLE_NAME), '` as '), information_schema.VIEWS.VIEW_DEFINITION) end as 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, 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 as 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 t.table_catalog, t.table_schema, t.table_name, t.view_definition from (select information_schema.views.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 as view_definition from information_schema.views union all select current_database(), alias_87241969.nspname, pg_catalog.pg_class.relname, ((('create materialized view \"' || pg_catalog.pg_class.relname) || '\" as ') || pg_get_viewdef(pg_catalog.pg_class.oid)) from (pg_catalog.pg_class join pg_catalog.pg_namespace as alias_87241969 on pg_catalog.pg_class.relnamespace = alias_87241969.oid) where pg_catalog.pg_class.relkind = 'm') as t where t.table_schema in (?) order by 1, 2, 3"); 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, 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 as 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 t.table_catalog, t.table_schema, t.table_name, t.view_definition from (select information_schema.views.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 as view_definition from information_schema.views union all select current_database(), alias_87241969.nspname, pg_catalog.pg_class.relname, ((('create materialized view \"' || pg_catalog.pg_class.relname) || '\" as ') || pg_get_viewdef(pg_catalog.pg_class.oid)) from (pg_catalog.pg_class join pg_catalog.pg_namespace as alias_87241969 on pg_catalog.pg_class.relnamespace = alias_87241969.oid) where pg_catalog.pg_class.relkind = 'm') as t where t.table_schema in (?) order by 1, 2, 3");