[jOOQ/jOOQ#9509] Add Meta.getEnums()

- Added ResultQueryDatabase::enums
- Added MetaSQL queries
- Added MySQL implementation
This commit is contained in:
Lukas Eder 2023-09-07 09:24:43 +02:00
parent 30b198ea77
commit 6bc82eb3ef
15 changed files with 214 additions and 3 deletions

View File

@ -127,6 +127,25 @@ public interface ResultQueryDatabase extends Database {
@Nullable
ResultQuery<Record12<String, String, String, String, Integer, Integer, Long, Long, BigDecimal, BigDecimal, Boolean, Long>> sequences(List<String> schemas);
/**
* A query that produces enum types and their literals for a set of input schemas.
* <p>
* The resulting columns are:
* <ol>
* <li>Catalog name</li>
* <li>Schema name</li>
* <li>Column name (if applicable, e.g. in MySQL style RDBMS)</li>
* <li>Enum type name (if applicable, e.g. in PostgreSQL style RDBMS)</li>
* <li>Literal value</li>
* <li>Literal position</li>
* </ol>
*
* @return The query or <code>null</code> if this implementation doesn't support the query.
*/
@Internal
@Nullable
ResultQuery<Record6<String, String, String, String, String, Integer>> enums(List<String> schemas);
/**
* A query that produces source code for a set of input schemas.
* <p>

View File

@ -445,6 +445,11 @@ public class DerbyDatabase extends AbstractDatabase implements ResultQueryDataba
return result;
}
@Override
public ResultQuery<Record6<String, String, String, String, String, Integer>> enums(List<String> schemas) {
return null;
}
@Override
protected List<TableDefinition> getTables0() throws SQLException {
List<TableDefinition> result = new ArrayList<>();

View File

@ -284,6 +284,11 @@ public class DuckDBDatabase extends AbstractDatabase implements ResultQueryDatab
return result;
}
@Override
public ResultQuery<Record6<String, String, String, String, String, Integer>> enums(List<String> schemas) {
return null;
}
@Override
protected List<TableDefinition> getTables0() throws SQLException {
List<TableDefinition> result = new ArrayList<>();

View File

@ -504,6 +504,11 @@ public class FirebirdDatabase extends AbstractDatabase implements ResultQueryDat
return result;
}
@Override
public ResultQuery<Record6<String, String, String, String, String, Integer>> enums(List<String> schemas) {
return null;
}
@Override
protected List<TableDefinition> getTables0() throws SQLException {
List<TableDefinition> result = new ArrayList<>();

View File

@ -757,6 +757,11 @@ public class H2Database extends AbstractDatabase implements ResultQueryDatabase
return result;
}
@Override
public ResultQuery<Record6<String, String, String, String, String, Integer>> enums(List<String> schemas) {
return null;
}
static record TableRecord(String schema, String table, TableType type, String comment) {}
@Override

View File

@ -501,6 +501,11 @@ public class HSQLDBDatabase extends AbstractDatabase implements ResultQueryDatab
return result;
}
@Override
public ResultQuery<Record6<String, String, String, String, String, Integer>> enums(List<String> schemas) {
return null;
}
@Override
protected List<TableDefinition> getTables0() throws SQLException {
List<TableDefinition> result = new ArrayList<>();

View File

@ -48,11 +48,19 @@ import static org.jooq.SQLDialect.MYSQL;
// ...
import static org.jooq.impl.DSL.case_;
import static org.jooq.impl.DSL.cast;
import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.length;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DSL.noCondition;
import static org.jooq.impl.DSL.regexpReplaceAll;
import static org.jooq.impl.DSL.regexpReplaceFirst;
import static org.jooq.impl.DSL.replace;
import static org.jooq.impl.DSL.row;
import static org.jooq.impl.DSL.select;
import static org.jooq.impl.DSL.table;
import static org.jooq.impl.DSL.when;
import static org.jooq.impl.SQLDataType.CHAR;
import static org.jooq.impl.SQLDataType.CLOB;
import static org.jooq.impl.SQLDataType.INTEGER;
import static org.jooq.impl.SQLDataType.VARCHAR;
@ -79,6 +87,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.jooq.CommonTableExpression;
import org.jooq.DSLContext;
import org.jooq.Field;
import org.jooq.Record;
@ -121,6 +130,7 @@ import org.jooq.meta.TableDefinition;
import org.jooq.meta.UDTDefinition;
import org.jooq.meta.XMLSchemaCollectionDefinition;
import org.jooq.meta.mariadb.MariaDBDatabase;
import org.jooq.meta.mysql.information_schema.tables.Columns;
import org.jooq.meta.mysql.information_schema.tables.Triggers;
import org.jooq.meta.mysql.mysql.enums.ProcType;
import org.jooq.tools.csv.CSVReader;
@ -469,6 +479,71 @@ public class MySQLDatabase extends AbstractDatabase implements ResultQueryDataba
return result;
}
@Override
public ResultQuery<Record6<String, String, String, String, String, Integer>> enums(List<String> schemas) {
// Recursive query that works with MySQL 8+ only:
// https://stackoverflow.com/a/77057135/521799
Columns c = COLUMNS;
Field<String> e = field(name("e"), VARCHAR);
Field<String> l = field(name("l"), VARCHAR);
Field<Integer> p = field(name("p"), INTEGER);
CommonTableExpression<?> te = name("e").as(
select(
c.TABLE_SCHEMA,
c.TABLE_NAME,
c.COLUMN_NAME,
regexpReplaceAll(c.COLUMN_TYPE, inline("enum\\((.*)\\)"), inline("$1")).as(e))
.from(c)
.where(c.DATA_TYPE.eq(inline("enum")))
);
CommonTableExpression<?> tl = name("l").as(
select(
te.field(c.TABLE_SCHEMA),
te.field(c.TABLE_NAME),
te.field(c.COLUMN_NAME),
e, cast(inline(""), CHAR(32767)).as(l),
inline(0).as(p))
.from(te)
.unionAll(
select(
te.field(c.TABLE_SCHEMA),
te.field(c.TABLE_NAME),
te.field(c.COLUMN_NAME),
regexpReplaceFirst(e, inline("'.*?'(?:,|$)(.*)"), inline("$1")),
replace(
regexpReplaceFirst(e, inline("'(.*?)'(?:,|$).*"), inline("$1")),
inline("''"), inline("'")
),
p.plus(inline(1)))
.from(table(name("l")).as(te))
.where(length(e).gt(inline(0)))
)
);
return create()
.withRecursive(te, tl)
.select(
tl.field(c.TABLE_SCHEMA),
tl.field(c.TABLE_NAME),
tl.field(c.COLUMN_NAME),
inline(null, VARCHAR).as(c.DATA_TYPE),
tl.field(l),
tl.field(p))
.from(tl)
.where(p.gt(inline(0)))
.and(tl.field(c.TABLE_SCHEMA).in(schemas))
.orderBy(
tl.field(c.TABLE_SCHEMA),
tl.field(c.TABLE_NAME),
tl.field(c.COLUMN_NAME),
tl.field(p));
}
@Override
protected List<TableDefinition> getTables0() throws SQLException {
List<TableDefinition> result = new ArrayList<>();

View File

@ -779,6 +779,11 @@ public class PostgresDatabase extends AbstractDatabase implements ResultQueryDat
return result;
}
@Override
public ResultQuery<Record6<String, String, String, String, String, Integer>> enums(List<String> schemas) {
return null;
}
static record Identifier(String schema, String name) {}
@Override

View File

@ -402,6 +402,11 @@ public class SQLiteDatabase extends AbstractDatabase implements ResultQueryDatab
return null;
}
@Override
public ResultQuery<Record6<String, String, String, String, String, Integer>> enums(List<String> schemas) {
return null;
}
@Override
public ResultQuery<Record6<String, String, String, String, String, Integer>> primaryKeys(List<String> schemas) {
return null;

View File

@ -147,6 +147,11 @@ public class TrinoDatabase extends AbstractDatabase implements ResultQueryDataba
return null;
}
@Override
public ResultQuery<Record6<String, String, String, String, String, Integer>> enums(List<String> schemas) {
return null;
}
@Override
public final ResultQuery<Record4<String, String, String, String>> sources(List<String> schemas) {
return create()

View File

@ -0,0 +1,47 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
* database integrations.
*
* For more information, please visit: https://www.jooq.org/legal/licensing
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq;
/**
* A marker interface for renamed schema elements.
*
* @author Lukas Eder
*/
interface RenamedSchemaElement {
}

View File

@ -46,7 +46,7 @@ import org.jooq.impl.TableImpl;
*
* @author Lukas Eder
*/
final class RenamedTable<R extends Record> extends TableImpl<R> {
final class RenamedTable<R extends Record> extends TableImpl<R> implements RenamedSchemaElement {
RenamedTable(Schema schema, Table<R> delegate, String rename) {
super(name(rename), schema);

View File

@ -44,7 +44,7 @@ import org.jooq.impl.UDTImpl;
*
* @author Lukas Eder
*/
final class RenamedUDT<R extends UDTRecord<R>> extends UDTImpl<R> {
final class RenamedUDT<R extends UDTRecord<R>> extends UDTImpl<R> implements RenamedSchemaElement {
RenamedUDT(Schema schema, UDT<R> delegate, String rename) {
super(rename, schema, delegate.getPackage(), delegate.isSynthetic());

View File

@ -540,7 +540,7 @@ public class SchemaMapping implements Serializable {
}
}
if (!(result instanceof RenamedTable))
if (!(result instanceof RenamedSchemaElement))
schemaLoop:
for (MappedSchema s : mapping().getSchemata()) {
if (matches(s, schemaName)) {

View File

@ -11,6 +11,7 @@ final class MetaSQL {
private static final EnumMap<SQLDialect, String> M_UNIQUE_KEYS = new EnumMap<>(SQLDialect.class);
private static final EnumMap<SQLDialect, String> M_SEQUENCES = new EnumMap<>(SQLDialect.class);
private static final EnumMap<SQLDialect, String> M_SEQUENCES_INCLUDING_SYSTEM_SEQUENCES = new EnumMap<>(SQLDialect.class);
private static final EnumMap<SQLDialect, String> M_ENUMS = new EnumMap<>(SQLDialect.class);
private static final EnumMap<SQLDialect, String> M_SOURCES = new EnumMap<>(SQLDialect.class);
private static final EnumMap<SQLDialect, String> M_COMMENTS = new EnumMap<>(SQLDialect.class);
@ -29,6 +30,11 @@ final class MetaSQL {
return result != null ? result : M_SEQUENCES_INCLUDING_SYSTEM_SEQUENCES.get(dialect.family());
}
static final String M_ENUMS(SQLDialect dialect) {
String result = M_ENUMS.get(dialect);
return result != null ? result : M_ENUMS.get(dialect.family());
}
static final String M_SOURCES(SQLDialect dialect) {
String result = M_SOURCES.get(dialect);
return result != null ? result : M_SOURCES.get(dialect.family());
@ -202,6 +208,30 @@ 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");