diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/ResultQueryDatabase.java b/jOOQ-meta/src/main/java/org/jooq/meta/ResultQueryDatabase.java
index 998b87f520..1f7a0b28ac 100644
--- a/jOOQ-meta/src/main/java/org/jooq/meta/ResultQueryDatabase.java
+++ b/jOOQ-meta/src/main/java/org/jooq/meta/ResultQueryDatabase.java
@@ -232,4 +232,24 @@ public interface ResultQueryDatabase extends Database {
+ /**
+ * A query that produces generator expressions for computed columns for a
+ * set of input schemas.
+ *
+ * The resulting columns are:
+ *
+ * - Catalog name
+ * - Schema name
+ * - Table name
+ * - Column name
+ * - Generator expression as in {@link DataType#generatedAlwaysAs()}
+ * - Generation option as in {@link DataType#generationOption()}
+ *
+ *
+ * @return The query or null if this implementation doesn't
+ * support the query.
+ */
+ @Internal
+ @Nullable
+ ResultQuery> generators(List schemas);
}
diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/clickhouse/ClickHouseDatabase.java b/jOOQ-meta/src/main/java/org/jooq/meta/clickhouse/ClickHouseDatabase.java
index 40440f09d8..2214d75169 100644
--- a/jOOQ-meta/src/main/java/org/jooq/meta/clickhouse/ClickHouseDatabase.java
+++ b/jOOQ-meta/src/main/java/org/jooq/meta/clickhouse/ClickHouseDatabase.java
@@ -366,6 +366,11 @@ public class ClickHouseDatabase extends AbstractDatabase implements ResultQueryD
+ @Override
+ public ResultQuery> generators(List schemas) {
+ return null;
+ }
+
@Override
protected List getXMLSchemaCollections0() throws SQLException {
List result = new ArrayList<>();
diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/derby/DerbyDatabase.java b/jOOQ-meta/src/main/java/org/jooq/meta/derby/DerbyDatabase.java
index 3bea131fc5..2766d1530f 100644
--- a/jOOQ-meta/src/main/java/org/jooq/meta/derby/DerbyDatabase.java
+++ b/jOOQ-meta/src/main/java/org/jooq/meta/derby/DerbyDatabase.java
@@ -658,6 +658,11 @@ public class DerbyDatabase extends AbstractDatabase implements ResultQueryDataba
+
+ @Override
+ public ResultQuery> generators(List schemas) {
+ return null;
+ }
@Override
protected List getXMLSchemaCollections0() throws SQLException {
diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/duckdb/DuckDBDatabase.java b/jOOQ-meta/src/main/java/org/jooq/meta/duckdb/DuckDBDatabase.java
index c037c723ce..dd131d1c74 100644
--- a/jOOQ-meta/src/main/java/org/jooq/meta/duckdb/DuckDBDatabase.java
+++ b/jOOQ-meta/src/main/java/org/jooq/meta/duckdb/DuckDBDatabase.java
@@ -418,6 +418,11 @@ public class DuckDBDatabase extends AbstractDatabase implements ResultQueryDatab
+ @Override
+ public ResultQuery> generators(List schemas) {
+ return null;
+ }
+
@Override
public ResultQuery> sequences(List schemas) {
return create()
diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/firebird/FirebirdDatabase.java b/jOOQ-meta/src/main/java/org/jooq/meta/firebird/FirebirdDatabase.java
index e7229caac7..1d9d664452 100644
--- a/jOOQ-meta/src/main/java/org/jooq/meta/firebird/FirebirdDatabase.java
+++ b/jOOQ-meta/src/main/java/org/jooq/meta/firebird/FirebirdDatabase.java
@@ -709,6 +709,11 @@ public class FirebirdDatabase extends AbstractDatabase implements ResultQueryDat
+
+ @Override
+ public ResultQuery> generators(List schemas) {
+ return null;
+ }
@Override
protected List getXMLSchemaCollections0() throws SQLException {
diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/h2/H2Database.java b/jOOQ-meta/src/main/java/org/jooq/meta/h2/H2Database.java
index 228d58dc57..689eccbccb 100644
--- a/jOOQ-meta/src/main/java/org/jooq/meta/h2/H2Database.java
+++ b/jOOQ-meta/src/main/java/org/jooq/meta/h2/H2Database.java
@@ -774,6 +774,11 @@ public class H2Database extends AbstractDatabase implements ResultQueryDatabase
+
+ @Override
+ public ResultQuery> generators(List schemas) {
+ return null;
+ }
@Override
protected List getSequences0() throws SQLException {
diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/hsqldb/HSQLDBDatabase.java b/jOOQ-meta/src/main/java/org/jooq/meta/hsqldb/HSQLDBDatabase.java
index cd706e69f5..5f59192618 100644
--- a/jOOQ-meta/src/main/java/org/jooq/meta/hsqldb/HSQLDBDatabase.java
+++ b/jOOQ-meta/src/main/java/org/jooq/meta/hsqldb/HSQLDBDatabase.java
@@ -713,6 +713,11 @@ public class HSQLDBDatabase extends AbstractDatabase implements ResultQueryDatab
+
+ @Override
+ public ResultQuery> generators(List schemas) {
+ return null;
+ }
@Override
protected List getXMLSchemaCollections0() throws SQLException {
diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/mysql/MySQLDatabase.java b/jOOQ-meta/src/main/java/org/jooq/meta/mysql/MySQLDatabase.java
index fd979cc51d..f63a572430 100644
--- a/jOOQ-meta/src/main/java/org/jooq/meta/mysql/MySQLDatabase.java
+++ b/jOOQ-meta/src/main/java/org/jooq/meta/mysql/MySQLDatabase.java
@@ -112,6 +112,7 @@ import org.jooq.TableOptions.TableType;
// ...
import org.jooq.impl.DSL;
import org.jooq.impl.QOM.ForeignKeyRule;
+import org.jooq.impl.QOM.GenerationOption;
import org.jooq.meta.AbstractDatabase;
import org.jooq.meta.AbstractIndexDefinition;
import org.jooq.meta.ArrayDefinition;
@@ -716,6 +717,28 @@ public class MySQLDatabase extends AbstractDatabase implements ResultQueryDataba
+
+ @Override
+ public ResultQuery> generators(List schemas) {
+ return create()
+ .select(
+ inline(null, VARCHAR).as("table_catalog"),
+ COLUMNS.TABLE_SCHEMA,
+ COLUMNS.TABLE_NAME,
+ COLUMNS.COLUMN_NAME,
+ generationExpression(COLUMNS.GENERATION_EXPRESSION),
+ when(COLUMNS.EXTRA.in(inline("VIRTUAL"), inline("VIRTUAL GENERATED")), inline(GenerationOption.VIRTUAL.name()))
+ .when(COLUMNS.EXTRA.in(inline("PERSISTENT"), inline("STORED GENERATED")), inline(GenerationOption.STORED.name()))
+ .as("generationOption"))
+ .from(COLUMNS)
+ .where(COLUMNS.TABLE_SCHEMA.in(schemas))
+ .and(COLUMNS.EXTRA.in(
+ inline("VIRTUAL"),
+ inline("VIRTUAL GENERATED"),
+ inline("PERSISTENT"),
+ inline("STORED GENERATED")))
+ .orderBy(COLUMNS.ORDINAL_POSITION);
+ }
@Override
protected List getXMLSchemaCollections0() throws SQLException {
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 712390fe0f..5b7642e3c3 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
@@ -1082,6 +1082,11 @@ public class PostgresDatabase extends AbstractDatabase implements ResultQueryDat
+
+ @Override
+ public ResultQuery> generators(List schemas) {
+ return null;
+ }
@Override
protected List getXMLSchemaCollections0() throws SQLException {
diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/sqlite/SQLiteDatabase.java b/jOOQ-meta/src/main/java/org/jooq/meta/sqlite/SQLiteDatabase.java
index 92839324b3..0fbcb0309a 100644
--- a/jOOQ-meta/src/main/java/org/jooq/meta/sqlite/SQLiteDatabase.java
+++ b/jOOQ-meta/src/main/java/org/jooq/meta/sqlite/SQLiteDatabase.java
@@ -616,6 +616,11 @@ public class SQLiteDatabase extends AbstractDatabase implements ResultQueryDatab
+
+ @Override
+ public ResultQuery> generators(List schemas) {
+ return null;
+ }
@Override
protected List getXMLSchemaCollections0() throws SQLException {
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 224b237fd2..f82fbf275c 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
@@ -198,6 +198,11 @@ public class TrinoDatabase extends AbstractDatabase implements ResultQueryDataba
+ @Override
+ public ResultQuery> generators(List schemas) {
+ return null;
+ }
+
@Override
protected List getCatalogs0() throws SQLException {
List result = new ArrayList<>();
diff --git a/jOOQ/src/main/java/org/jooq/impl/MetaImpl.java b/jOOQ/src/main/java/org/jooq/impl/MetaImpl.java
index f5dc114403..0d0f5535b4 100644
--- a/jOOQ/src/main/java/org/jooq/impl/MetaImpl.java
+++ b/jOOQ/src/main/java/org/jooq/impl/MetaImpl.java
@@ -90,7 +90,6 @@ import static org.jooq.impl.SQLDataType.SMALLINT;
import static org.jooq.impl.SQLDataType.VARCHAR;
import static org.jooq.impl.Tools.EMPTY_OBJECT;
import static org.jooq.impl.Tools.EMPTY_SORTFIELD;
-import static org.jooq.impl.Tools.anyMatch;
import static org.jooq.impl.Tools.flatMap;
import static org.jooq.impl.Tools.map;
import static org.jooq.tools.StringUtils.defaultIfEmpty;
@@ -111,7 +110,6 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -153,12 +151,9 @@ import org.jooq.exception.DataAccessException;
import org.jooq.exception.DataDefinitionException;
import org.jooq.exception.DataTypeException;
import org.jooq.exception.SQLDialectNotSupportedException;
-import org.jooq.impl.QOM.ForeignKeyRule;
+import org.jooq.impl.QOM.GenerationOption;
import org.jooq.tools.JooqLogger;
import org.jooq.tools.StringUtils;
-import org.jooq.tools.jdbc.JDBCUtils;
-
-import org.jetbrains.annotations.Nullable;
/**
* An implementation of the public {@link Meta} type.
@@ -465,6 +460,8 @@ final class MetaImpl extends AbstractMeta {
}
}
+ static final record Generator(String expression, GenerationOption option) {}
+
private final class MetaSchema extends SchemaImpl {
private final boolean empty;
private transient volatile Map> columnCache;
@@ -472,6 +469,7 @@ final class MetaImpl extends AbstractMeta {
private transient volatile Map> sequenceCache;
private transient volatile Map sourceCache;
private transient volatile Map commentCache;
+ private transient volatile Map generatorCache;
@@ -1001,6 +999,38 @@ final class MetaImpl extends AbstractMeta {
return null;
}
+ final Generator generator(String tableName, String columnName) {
+ if (generatorCache == null) {
+ String sql = MetaSQL.M_GENERATORS(dialect());
+
+ if (sql != null) {
+ Result result = meta(() -> "Error while fetching sources for schema " + this, meta ->
+ withCatalog(getCatalog(), ctx(meta), ctx ->
+ ctx.resultQuery(patchSchema(sql), MetaSchema.this.getName()).fetch()
+ )
+ );
+
+ // TODO Support catalogs as well
+ generatorCache = new LinkedHashMap<>();
+ for (Record r : result) {
+ String e = r.get(4, String.class);
+
+ if (e != null) {
+ generatorCache.put(
+ name(r.get(1, String.class), r.get(2, String.class), r.get(3, String.class)),
+ new Generator(r.get(4, String.class), r.get(5, GenerationOption.class))
+ );
+ }
+ }
+ }
+ }
+
+ if (generatorCache != null)
+ return generatorCache.get(name(MetaSchema.this.getName(), tableName, columnName));
+ else
+ return null;
+ }
+
final String comment(String tableName) {
return comment(tableName, null);
}
@@ -1705,6 +1735,10 @@ final class MetaImpl extends AbstractMeta {
type = new DefaultDataType(family(), Object.class, typeName);
}
+ // [#18988] MySQL JDBC reports DEFAULT CURRENT_TIMESTAMP columns as generated, not defaulted.
+ Generator g = schema.generator(getName(), columnName);
+ if (isGenerated && g == null && MYSQL == family())
+ isGenerated = false;
diff --git a/jOOQ/src/main/java/org/jooq/impl/MetaSQL.java b/jOOQ/src/main/java/org/jooq/impl/MetaSQL.java
index 8af851be66..2c7b7eb774 100644
--- a/jOOQ/src/main/java/org/jooq/impl/MetaSQL.java
+++ b/jOOQ/src/main/java/org/jooq/impl/MetaSQL.java
@@ -14,6 +14,7 @@ final class MetaSQL {
private static final EnumMap M_ENUMS = new EnumMap<>(SQLDialect.class);
private static final EnumMap M_SOURCES = new EnumMap<>(SQLDialect.class);
private static final EnumMap M_COMMENTS = new EnumMap<>(SQLDialect.class);
+ private static final EnumMap M_GENERATORS = new EnumMap<>(SQLDialect.class);
private static final EnumMap M_TRIGGERS = new EnumMap<>(SQLDialect.class);
private static final EnumMap M_SYNONYMS = new EnumMap<>(SQLDialect.class);
@@ -47,6 +48,11 @@ final class MetaSQL {
return result != null ? result : M_COMMENTS.get(dialect.family());
}
+ static final String M_GENERATORS(SQLDialect dialect) {
+ String result = M_GENERATORS.get(dialect);
+ return result != null ? result : M_GENERATORS.get(dialect.family());
+ }
+
static final String M_TRIGGERS(SQLDialect dialect) {
String result = M_TRIGGERS.get(dialect);
return result != null ? result : M_TRIGGERS.get(dialect.family());
@@ -254,8 +260,8 @@ final class MetaSQL {
- M_ENUMS.put(MARIADB, "with recursive e as (select information_schema.COLUMNS.TABLE_SCHEMA, information_schema.COLUMNS.TABLE_NAME, information_schema.COLUMNS.COLUMN_NAME, regexp_replace(information_schema.COLUMNS.COLUMN_TYPE, 'enum\\((.*)\\)', '$1') as e from information_schema.COLUMNS where information_schema.COLUMNS.DATA_TYPE = 'enum'), l as (select e.TABLE_SCHEMA, e.TABLE_NAME, e.COLUMN_NAME, e, cast('' as char(32767)) as l, 0 as p from e union all select e.TABLE_SCHEMA, e.TABLE_NAME, e.COLUMN_NAME, regexp_replace(e, '''.*?''(?:,|$)(.*)', '$1', 1, 1), replace(regexp_replace(e, '''(.*?)''(?:,|$).*', '$1', 1, 1), '''''', ''''), (p + 1) from l as e where char_length(e) > 0) select l.TABLE_SCHEMA, l.TABLE_NAME, l.COLUMN_NAME, null as DATA_TYPE, l.l, l.p from l where (p > 0 and l.TABLE_SCHEMA in (?)) order by l.TABLE_SCHEMA, l.TABLE_NAME, l.COLUMN_NAME, l.p");
- M_ENUMS.put(MYSQL, "with recursive e as (select information_schema.COLUMNS.TABLE_SCHEMA, information_schema.COLUMNS.TABLE_NAME, information_schema.COLUMNS.COLUMN_NAME, regexp_replace(information_schema.COLUMNS.COLUMN_TYPE, 'enum\\((.*)\\)', '$1') as e from information_schema.COLUMNS where information_schema.COLUMNS.DATA_TYPE = 'enum'), l as (select e.TABLE_SCHEMA, e.TABLE_NAME, e.COLUMN_NAME, e, cast('' as char(32767)) as l, 0 as p from e union all select e.TABLE_SCHEMA, e.TABLE_NAME, e.COLUMN_NAME, regexp_replace(e, '''.*?''(?:,|$)(.*)', '$1', 1, 1), replace(regexp_replace(e, '''(.*?)''(?:,|$).*', '$1', 1, 1), '''''', ''''), (p + 1) from l as e where char_length(e) > 0) select l.TABLE_SCHEMA, l.TABLE_NAME, l.COLUMN_NAME, null as DATA_TYPE, l.l, l.p from l where (p > 0 and l.TABLE_SCHEMA in (?)) order by l.TABLE_SCHEMA, l.TABLE_NAME, l.COLUMN_NAME, l.p");
+ M_ENUMS.put(MARIADB, "with recursive e as (select information_schema.COLUMNS.TABLE_SCHEMA, information_schema.COLUMNS.TABLE_NAME, information_schema.COLUMNS.COLUMN_NAME, regexp_replace(information_schema.COLUMNS.COLUMN_TYPE, 'enum\\\\((.*)\\\\)', '$1') as e from information_schema.COLUMNS where information_schema.COLUMNS.DATA_TYPE = 'enum'), l as (select e.TABLE_SCHEMA, e.TABLE_NAME, e.COLUMN_NAME, e, cast('' as char(32767)) as l, 0 as p from e union all select e.TABLE_SCHEMA, e.TABLE_NAME, e.COLUMN_NAME, regexp_replace(e, '''.*?''(?:,|$)(.*)', '$1', 1, 1), replace(regexp_replace(e, '''(.*?)''(?:,|$).*', '$1', 1, 1), '''''', ''''), (p + 1) from l as e where char_length(e) > 0) select l.TABLE_SCHEMA, l.TABLE_NAME, l.COLUMN_NAME, null as DATA_TYPE, l.l, l.p from l where (p > 0 and l.TABLE_SCHEMA in (?)) order by l.TABLE_SCHEMA, l.TABLE_NAME, l.COLUMN_NAME, l.p");
+ M_ENUMS.put(MYSQL, "with recursive e as (select information_schema.COLUMNS.TABLE_SCHEMA, information_schema.COLUMNS.TABLE_NAME, information_schema.COLUMNS.COLUMN_NAME, regexp_replace(information_schema.COLUMNS.COLUMN_TYPE, 'enum\\\\((.*)\\\\)', '$1') as e from information_schema.COLUMNS where information_schema.COLUMNS.DATA_TYPE = 'enum'), l as (select e.TABLE_SCHEMA, e.TABLE_NAME, e.COLUMN_NAME, e, cast('' as char(32767)) as l, 0 as p from e union all select e.TABLE_SCHEMA, e.TABLE_NAME, e.COLUMN_NAME, regexp_replace(e, '''.*?''(?:,|$)(.*)', '$1', 1, 1), replace(regexp_replace(e, '''(.*?)''(?:,|$).*', '$1', 1, 1), '''''', ''''), (p + 1) from l as e where char_length(e) > 0) select l.TABLE_SCHEMA, l.TABLE_NAME, l.COLUMN_NAME, null as DATA_TYPE, l.l, l.p from l where (p > 0 and l.TABLE_SCHEMA in (?)) order by l.TABLE_SCHEMA, l.TABLE_NAME, l.COLUMN_NAME, l.p");
@@ -425,6 +431,31 @@ final class MetaSQL {
+
+
+
+ M_GENERATORS.put(MARIADB, "select null as table_catalog, information_schema.COLUMNS.TABLE_SCHEMA, information_schema.COLUMNS.TABLE_NAME, information_schema.COLUMNS.COLUMN_NAME, information_schema.COLUMNS.GENERATION_EXPRESSION, case when information_schema.COLUMNS.EXTRA in ('VIRTUAL', 'VIRTUAL GENERATED') then 'VIRTUAL' when information_schema.COLUMNS.EXTRA in ('PERSISTENT', 'STORED GENERATED') then 'STORED' end as generationOption from information_schema.COLUMNS where (information_schema.COLUMNS.TABLE_SCHEMA in (?) and information_schema.COLUMNS.EXTRA in ('VIRTUAL', 'VIRTUAL GENERATED', 'PERSISTENT', 'STORED GENERATED')) order by information_schema.COLUMNS.ORDINAL_POSITION");
+ M_GENERATORS.put(MYSQL, "select null as table_catalog, information_schema.COLUMNS.TABLE_SCHEMA, information_schema.COLUMNS.TABLE_NAME, information_schema.COLUMNS.COLUMN_NAME, information_schema.COLUMNS.GENERATION_EXPRESSION, case when information_schema.COLUMNS.EXTRA in ('VIRTUAL', 'VIRTUAL GENERATED') then 'VIRTUAL' when information_schema.COLUMNS.EXTRA in ('PERSISTENT', 'STORED GENERATED') then 'STORED' end as generationOption from information_schema.COLUMNS where (information_schema.COLUMNS.TABLE_SCHEMA in (?) and information_schema.COLUMNS.EXTRA in ('VIRTUAL', 'VIRTUAL GENERATED', 'PERSISTENT', 'STORED GENERATED')) order by information_schema.COLUMNS.ORDINAL_POSITION");
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+