[jOOQ/jOOQ#5179] OSS edition sync

This commit is contained in:
Lukas Eder 2020-10-30 11:12:12 +01:00
parent fcbe607ff9
commit b8e4d4a759
10 changed files with 313 additions and 93 deletions

View File

@ -0,0 +1,73 @@
/*
* 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
*
* http://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: http://www.jooq.org/licenses
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.meta;
import java.util.List;
import org.jooq.Internal;
import org.jooq.Meta;
import org.jooq.Record6;
import org.jooq.ResultQuery;
/**
* An interface for all {@link AbstractDatabase} implementations that can
* produce {@link ResultQuery} objects to query meta data.
* <p>
* These queries will be used to generate some queries in the core library's
* {@link Meta} API.
*
* @author Lukas Eder
*/
@Internal
public interface ResultQueryDatabase {
/**
* A query that produces unique keys for a set of input schemas.
* <p>
* The resulting columns are:
* <ol>
* <li>Catalog name</li>
* <li>Schema name</li>
* <li>Table name</li>
* <li>Constraint name</li>
* <li>Column name</li>
* <li>Column sequence</li>
* </ol>
*/
ResultQuery<Record6<String, String, String, String, String, Integer>> uniqueKeysQuery(List<String> schemas);
}

View File

@ -50,6 +50,8 @@ import static org.jooq.impl.DSL.select;
import static org.jooq.impl.DSL.trim;
import static org.jooq.impl.DSL.when;
import static org.jooq.impl.DSL.zero;
import static org.jooq.impl.SQLDataType.INTEGER;
import static org.jooq.impl.SQLDataType.VARCHAR;
import static org.jooq.meta.firebird.rdb.Tables.RDB$CHECK_CONSTRAINTS;
import static org.jooq.meta.firebird.rdb.Tables.RDB$FIELDS;
import static org.jooq.meta.firebird.rdb.Tables.RDB$GENERATORS;
@ -70,9 +72,10 @@ import java.util.Map.Entry;
import org.jooq.DSLContext;
import org.jooq.Field;
import org.jooq.Record;
import org.jooq.Record3;
import org.jooq.Record4;
import org.jooq.Record6;
import org.jooq.Result;
import org.jooq.ResultQuery;
import org.jooq.SQLDialect;
import org.jooq.SortOrder;
import org.jooq.TableOptions.TableType;
@ -93,6 +96,7 @@ import org.jooq.meta.EnumDefinition;
import org.jooq.meta.IndexColumnDefinition;
import org.jooq.meta.IndexDefinition;
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;
@ -112,7 +116,7 @@ import org.jooq.util.firebird.FirebirdDataType;
/**
* @author Sugiharto Lim - Initial contribution
*/
public class FirebirdDatabase extends AbstractDatabase {
public class FirebirdDatabase extends AbstractDatabase implements ResultQueryDatabase {
private static Boolean is30;
@ -131,10 +135,10 @@ public class FirebirdDatabase extends AbstractDatabase {
@Override
protected void loadPrimaryKeys(DefaultRelations r) throws SQLException {
for (Record record : fetchKeys("PRIMARY KEY")) {
String tableName = record.get(RDB$RELATION_CONSTRAINTS.RDB$RELATION_NAME.trim());
String fieldName = record.get(RDB$INDEX_SEGMENTS.RDB$FIELD_NAME.trim());
String key = record.get(RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_NAME.trim());
for (Record record : keysQuery("PRIMARY KEY")) {
String tableName = record.get(RDB$RELATION_CONSTRAINTS.RDB$RELATION_NAME);
String fieldName = record.get(RDB$INDEX_SEGMENTS.RDB$FIELD_NAME);
String key = record.get(RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_NAME);
TableDefinition td = getTable(this.getSchemata().get(0), tableName);
if (td != null)
@ -144,10 +148,10 @@ public class FirebirdDatabase extends AbstractDatabase {
@Override
protected void loadUniqueKeys(DefaultRelations r) throws SQLException {
for (Record record : fetchKeys("UNIQUE")) {
String tableName = record.get(RDB$RELATION_CONSTRAINTS.RDB$RELATION_NAME.trim());
String fieldName = record.get(RDB$INDEX_SEGMENTS.RDB$FIELD_NAME.trim());
String key = record.get(RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_NAME.trim());
for (Record record : keysQuery("UNIQUE")) {
String tableName = record.get(RDB$RELATION_CONSTRAINTS.RDB$RELATION_NAME);
String fieldName = record.get(RDB$INDEX_SEGMENTS.RDB$FIELD_NAME);
String key = record.get(RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_NAME);
TableDefinition td = getTable(this.getSchemata().get(0), tableName);
if (td != null)
@ -155,20 +159,27 @@ public class FirebirdDatabase extends AbstractDatabase {
}
}
private Result<Record3<String, String, String>> fetchKeys(String constraintType) {
@Override
public ResultQuery<Record6<String, String, String, String, String, Integer>> uniqueKeysQuery(List<String> schemas) {
return keysQuery("UNIQUE");
}
private ResultQuery<Record6<String, String, String, String, String, Integer>> keysQuery(String constraintType) {
return create()
.select(
RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_NAME.trim(),
RDB$RELATION_CONSTRAINTS.RDB$RELATION_NAME.trim(),
RDB$INDEX_SEGMENTS.RDB$FIELD_NAME.trim())
inline(null, VARCHAR).as("catalog"),
inline(null, VARCHAR).as("schema"),
RDB$RELATION_CONSTRAINTS.RDB$RELATION_NAME.trim().as(RDB$RELATION_CONSTRAINTS.RDB$RELATION_NAME),
RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_NAME.trim().as(RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_NAME),
RDB$INDEX_SEGMENTS.RDB$FIELD_NAME.trim().as(RDB$INDEX_SEGMENTS.RDB$FIELD_NAME),
RDB$INDEX_SEGMENTS.RDB$FIELD_POSITION.coerce(INTEGER))
.from(RDB$RELATION_CONSTRAINTS)
.join(RDB$INDEX_SEGMENTS)
.on(RDB$INDEX_SEGMENTS.RDB$INDEX_NAME.eq(RDB$RELATION_CONSTRAINTS.RDB$INDEX_NAME))
.where(RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_TYPE.eq(constraintType))
.where(RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_TYPE.eq(inline(constraintType)))
.orderBy(
RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_NAME.asc(),
RDB$INDEX_SEGMENTS.RDB$FIELD_POSITION.asc())
.fetch();
RDB$INDEX_SEGMENTS.RDB$FIELD_POSITION.asc());
}
@Override

View File

@ -48,6 +48,7 @@ import static org.jooq.impl.DSL.nullif;
import static org.jooq.impl.DSL.one;
import static org.jooq.impl.DSL.select;
import static org.jooq.impl.DSL.when;
import static org.jooq.impl.SQLDataType.INTEGER;
import static org.jooq.meta.h2.information_schema.Tables.COLUMNS;
import static org.jooq.meta.h2.information_schema.Tables.CONSTRAINTS;
import static org.jooq.meta.h2.information_schema.Tables.CROSS_REFERENCES;
@ -63,6 +64,7 @@ import static org.jooq.meta.h2.information_schema.Tables.VIEWS;
import java.io.StringReader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@ -71,7 +73,9 @@ import org.jooq.DSLContext;
import org.jooq.Field;
import org.jooq.Record;
import org.jooq.Record4;
import org.jooq.Record6;
import org.jooq.Result;
import org.jooq.ResultQuery;
import org.jooq.SQLDialect;
import org.jooq.Select;
import org.jooq.SortOrder;
@ -97,6 +101,7 @@ import org.jooq.meta.EnumDefinition;
import org.jooq.meta.IndexColumnDefinition;
import org.jooq.meta.IndexDefinition;
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;
@ -111,7 +116,7 @@ import org.jooq.util.h2.H2DataType;
*
* @author Espen Stromsnes
*/
public class H2Database extends AbstractDatabase {
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;
@ -217,7 +222,7 @@ public class H2Database extends AbstractDatabase {
protected void loadPrimaryKeys(DefaultRelations relations) throws SQLException {
// Workaround for https://github.com/h2database/h2database/issues/1000
for (Record record : fetchKeys("PRIMARY KEY", "PRIMARY_KEY")) {
for (Record record : keysQuery(getInputSchemata(), Arrays.<Field<String>>asList(inline("PRIMARY KEY"), inline("PRIMARY_KEY")))) {
SchemaDefinition schema = getSchema(record.get(CONSTRAINTS.TABLE_SCHEMA));
if (schema != null) {
@ -234,7 +239,7 @@ public class H2Database extends AbstractDatabase {
@Override
protected void loadUniqueKeys(DefaultRelations relations) throws SQLException {
for (Record record : fetchKeys("UNIQUE")) {
for (Record record : uniqueKeysQuery(getInputSchemata())) {
SchemaDefinition schema = getSchema(record.get(CONSTRAINTS.TABLE_SCHEMA));
if (schema != null) {
@ -249,24 +254,30 @@ public class H2Database extends AbstractDatabase {
}
}
private Result<Record4<String, String, String, String>> fetchKeys(String... constraintTypes) {
@Override
public ResultQuery<Record6<String, String, String, String, String, Integer>> uniqueKeysQuery(List<String> schemas) {
return keysQuery(schemas, Arrays.<Field<String>>asList(inline("UNIQUE")));
}
private ResultQuery<Record6<String, String, String, String, String, Integer>> keysQuery(List<String> schemas, List<Field<String>> constraintTypes) {
return create().select(
CONSTRAINTS.TABLE_CATALOG,
CONSTRAINTS.TABLE_SCHEMA,
CONSTRAINTS.TABLE_NAME,
CONSTRAINTS.CONSTRAINT_NAME,
INDEXES.COLUMN_NAME)
INDEXES.COLUMN_NAME,
INDEXES.ORDINAL_POSITION.coerce(INTEGER))
.from(CONSTRAINTS)
.join(INDEXES)
.on(CONSTRAINTS.TABLE_SCHEMA.eq(INDEXES.TABLE_SCHEMA))
.and(CONSTRAINTS.TABLE_NAME.eq(INDEXES.TABLE_NAME))
.and(CONSTRAINTS.UNIQUE_INDEX_NAME.eq(INDEXES.INDEX_NAME))
.where(CONSTRAINTS.TABLE_SCHEMA.in(getInputSchemata()))
.where(CONSTRAINTS.TABLE_SCHEMA.in(schemas))
.and(CONSTRAINTS.CONSTRAINT_TYPE.in(constraintTypes))
.orderBy(
CONSTRAINTS.TABLE_SCHEMA,
CONSTRAINTS.CONSTRAINT_NAME,
INDEXES.ORDINAL_POSITION)
.fetch();
INDEXES.ORDINAL_POSITION);
}
@Override

View File

@ -42,6 +42,7 @@ import static org.jooq.impl.DSL.falseCondition;
import static org.jooq.impl.DSL.inline;
import static org.jooq.impl.DSL.noCondition;
import static org.jooq.impl.DSL.when;
import static org.jooq.impl.SQLDataType.INTEGER;
import static org.jooq.meta.mysql.information_schema.Tables.CHECK_CONSTRAINTS;
import static org.jooq.meta.mysql.information_schema.Tables.COLUMNS;
import static org.jooq.meta.mysql.information_schema.Tables.KEY_COLUMN_USAGE;
@ -59,6 +60,7 @@ import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.Map.Entry;
import org.jooq.DSLContext;
@ -67,12 +69,14 @@ import org.jooq.Record;
import org.jooq.Record5;
import org.jooq.Record6;
import org.jooq.Result;
import org.jooq.ResultQuery;
import org.jooq.SQLDialect;
import org.jooq.SortOrder;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.TableOptions.TableType;
import org.jooq.impl.DSL;
import org.jooq.impl.SQLDataType;
import org.jooq.meta.AbstractDatabase;
import org.jooq.meta.AbstractIndexDefinition;
import org.jooq.meta.ArrayDefinition;
@ -87,6 +91,7 @@ import org.jooq.meta.EnumDefinition;
import org.jooq.meta.IndexColumnDefinition;
import org.jooq.meta.IndexDefinition;
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;
@ -99,7 +104,7 @@ import org.jooq.tools.csv.CSVReader;
/**
* @author Lukas Eder
*/
public class MySQLDatabase extends AbstractDatabase {
public class MySQLDatabase extends AbstractDatabase implements ResultQueryDatabase {
private static Boolean is8;
private static Boolean is8_0_16;
@ -129,11 +134,7 @@ public class MySQLDatabase extends AbstractDatabase {
STATISTICS.COLUMN_NAME,
STATISTICS.SEQ_IN_INDEX)
.from(from)
// [#5213] Duplicate schema value to work around MySQL issue https://bugs.mysql.com/bug.php?id=86022
.where(STATISTICS.TABLE_SCHEMA.in(getInputSchemata()).or(
getInputSchemata().size() == 1
? STATISTICS.TABLE_SCHEMA.in(getInputSchemata())
: falseCondition()))
.where(STATISTICS.TABLE_SCHEMA.in(workaroundFor5213(getInputSchemata())))
.and(getIncludeSystemIndexes()
? noCondition()
: TABLE_CONSTRAINTS.CONSTRAINT_NAME.isNull()
@ -203,7 +204,7 @@ public class MySQLDatabase extends AbstractDatabase {
@Override
protected void loadPrimaryKeys(DefaultRelations relations) throws SQLException {
for (Record record : fetchKeys(true)) {
for (Record record : keysQuery(getInputSchemata(), true)) {
SchemaDefinition schema = getSchema(record.get(STATISTICS.TABLE_SCHEMA));
String constraintName = record.get(STATISTICS.INDEX_NAME);
String tableName = record.get(STATISTICS.TABLE_NAME);
@ -219,7 +220,7 @@ public class MySQLDatabase extends AbstractDatabase {
@Override
protected void loadUniqueKeys(DefaultRelations relations) throws SQLException {
for (Record record : fetchKeys(false)) {
for (Record record : uniqueKeysQuery(getInputSchemata())) {
SchemaDefinition schema = getSchema(record.get(STATISTICS.TABLE_SCHEMA));
String constraintName = record.get(STATISTICS.INDEX_NAME);
String tableName = record.get(STATISTICS.TABLE_NAME);
@ -255,24 +256,29 @@ public class MySQLDatabase extends AbstractDatabase {
return is8_0_16;
}
private Result<?> fetchKeys(boolean primary) {
@Override
public ResultQuery<Record6<String, String, String, String, String, Integer>> uniqueKeysQuery(List<String> schemas) {
return keysQuery(schemas, false);
}
private ResultQuery<Record6<String, String, String, String, String, Integer>> keysQuery(List<String> inputSchemata, boolean primary) {
// [#3560] It has been shown that querying the STATISTICS table is much faster on
// very large databases than going through TABLE_CONSTRAINTS and KEY_COLUMN_USAGE
// [#2059] In MemSQL primary key indexes are typically duplicated
// (once with INDEX_TYPE = 'SHARD' and once with INDEX_TYPE = 'BTREE)
return create().selectDistinct(
// Don't use the actual catalog value, which is meaningless.
// Besides, MetaImpl will rely on the TABLE_SCHEMA acting as the catalog.
inline(null, STATISTICS.TABLE_CATALOG).as(STATISTICS.TABLE_CATALOG),
STATISTICS.TABLE_SCHEMA,
STATISTICS.TABLE_NAME,
STATISTICS.COLUMN_NAME,
STATISTICS.INDEX_NAME,
STATISTICS.SEQ_IN_INDEX)
STATISTICS.COLUMN_NAME,
STATISTICS.SEQ_IN_INDEX.coerce(INTEGER))
.from(STATISTICS)
// [#5213] Duplicate schema value to work around MySQL issue https://bugs.mysql.com/bug.php?id=86022
.where(STATISTICS.TABLE_SCHEMA.in(getInputSchemata()).or(
getInputSchemata().size() == 1
? STATISTICS.TABLE_SCHEMA.in(getInputSchemata())
: falseCondition()))
.where(STATISTICS.TABLE_SCHEMA.in(workaroundFor5213(inputSchemata)))
.and(primary
? STATISTICS.INDEX_NAME.eq(inline("PRIMARY"))
: STATISTICS.INDEX_NAME.ne(inline("PRIMARY")).and(STATISTICS.NON_UNIQUE.eq(inline(0))))
@ -280,8 +286,7 @@ public class MySQLDatabase extends AbstractDatabase {
STATISTICS.TABLE_SCHEMA,
STATISTICS.TABLE_NAME,
STATISTICS.INDEX_NAME,
STATISTICS.SEQ_IN_INDEX)
.fetch();
STATISTICS.SEQ_IN_INDEX);
}
@Override
@ -299,11 +304,7 @@ public class MySQLDatabase extends AbstractDatabase {
.on(REFERENTIAL_CONSTRAINTS.CONSTRAINT_SCHEMA.equal(KEY_COLUMN_USAGE.CONSTRAINT_SCHEMA))
.and(REFERENTIAL_CONSTRAINTS.CONSTRAINT_NAME.equal(KEY_COLUMN_USAGE.CONSTRAINT_NAME))
.and(REFERENTIAL_CONSTRAINTS.TABLE_NAME.equal(KEY_COLUMN_USAGE.TABLE_NAME))
// [#5213] Duplicate schema value to work around MySQL issue https://bugs.mysql.com/bug.php?id=86022
.where(REFERENTIAL_CONSTRAINTS.CONSTRAINT_SCHEMA.in(getInputSchemata()).or(
getInputSchemata().size() == 1
? REFERENTIAL_CONSTRAINTS.CONSTRAINT_SCHEMA.in(getInputSchemata())
: falseCondition()))
.where(REFERENTIAL_CONSTRAINTS.CONSTRAINT_SCHEMA.in(workaroundFor5213(getInputSchemata())))
.orderBy(
KEY_COLUMN_USAGE.CONSTRAINT_SCHEMA.asc(),
KEY_COLUMN_USAGE.CONSTRAINT_NAME.asc(),
@ -429,12 +430,7 @@ public class MySQLDatabase extends AbstractDatabase {
.leftJoin(VIEWS)
.on(TABLES.TABLE_SCHEMA.eq(VIEWS.TABLE_SCHEMA))
.and(TABLES.TABLE_NAME.eq(VIEWS.TABLE_NAME))
// [#5213] Duplicate schema value to work around MySQL issue https://bugs.mysql.com/bug.php?id=86022
.where(TABLES.TABLE_SCHEMA.in(getInputSchemata()).or(
getInputSchemata().size() == 1
? TABLES.TABLE_SCHEMA.in(getInputSchemata())
: falseCondition()))
.where(TABLES.TABLE_SCHEMA.in(workaroundFor5213(getInputSchemata())))
// [#9291] MariaDB treats sequences as tables
.and(TABLES.TABLE_TYPE.ne(inline("SEQUENCE")))
@ -469,11 +465,7 @@ public class MySQLDatabase extends AbstractDatabase {
.from(COLUMNS)
.where(
COLUMNS.COLUMN_TYPE.like("enum(%)").and(
// [#5213] Duplicate schema value to work around MySQL issue https://bugs.mysql.com/bug.php?id=86022
COLUMNS.TABLE_SCHEMA.in(getInputSchemata()).or(
getInputSchemata().size() == 1
? COLUMNS.TABLE_SCHEMA.in(getInputSchemata())
: falseCondition())))
COLUMNS.TABLE_SCHEMA.in(workaroundFor5213(getInputSchemata()))))
.orderBy(
COLUMNS.TABLE_SCHEMA.asc(),
COLUMNS.TABLE_NAME.asc(),
@ -618,4 +610,19 @@ public class MySQLDatabase extends AbstractDatabase {
protected boolean exists0(Table<?> table) {
return exists1(table, TABLES.TABLES, TABLES.TABLE_SCHEMA, TABLES.TABLE_NAME);
}
private List<Field<String>> workaroundFor5213(List<String> inputSchemata) {
// [#5213] Add a dummy schema to single element lists to work around MySQL issue https://bugs.mysql.com/bug.php?id=86022
List<Field<String>> schemas = new ArrayList<>();
for (String schema : inputSchemata)
schemas.add(DSL.val(schema));
// Random UUID generated by fair dice roll
if (schemas.size() == 1)
schemas.add(DSL.inline("ee7f6174-34f2-484b-8d81-20a4d9fc866d"));
return schemas;
}
}

View File

@ -102,10 +102,10 @@ import org.jooq.Name;
// ...
import org.jooq.Record;
import org.jooq.Record2;
import org.jooq.Record4;
import org.jooq.Record5;
import org.jooq.Record6;
import org.jooq.Result;
import org.jooq.ResultQuery;
import org.jooq.SQLDialect;
import org.jooq.Select;
import org.jooq.SortOrder;
@ -133,6 +133,7 @@ import org.jooq.meta.EnumDefinition;
import org.jooq.meta.IndexColumnDefinition;
import org.jooq.meta.IndexDefinition;
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;
@ -157,7 +158,7 @@ import org.jooq.tools.JooqLogger;
*
* @author Lukas Eder
*/
public class PostgresDatabase extends AbstractDatabase {
public class PostgresDatabase extends AbstractDatabase implements ResultQueryDatabase {
private static final JooqLogger log = JooqLogger.getLogger(PostgresDatabase.class);
@ -259,7 +260,7 @@ public class PostgresDatabase extends AbstractDatabase {
@Override
protected void loadPrimaryKeys(DefaultRelations relations) throws SQLException {
for (Record record : fetchKeys("PRIMARY KEY")) {
for (Record record : keysQuery(getInputSchemata(), inline("PRIMARY KEY"))) {
SchemaDefinition schema = getSchema(record.get(KEY_COLUMN_USAGE.TABLE_SCHEMA));
String key = record.get(KEY_COLUMN_USAGE.CONSTRAINT_NAME);
String tableName = record.get(KEY_COLUMN_USAGE.TABLE_NAME);
@ -273,7 +274,7 @@ public class PostgresDatabase extends AbstractDatabase {
@Override
protected void loadUniqueKeys(DefaultRelations relations) throws SQLException {
for (Record record : fetchKeys("UNIQUE")) {
for (Record record : uniqueKeysQuery(getInputSchemata())) {
SchemaDefinition schema = getSchema(record.get(KEY_COLUMN_USAGE.TABLE_SCHEMA));
String key = record.get(KEY_COLUMN_USAGE.CONSTRAINT_NAME);
String tableName = record.get(KEY_COLUMN_USAGE.TABLE_NAME);
@ -285,22 +286,28 @@ public class PostgresDatabase extends AbstractDatabase {
}
}
private Result<Record4<String, String, String, String>> fetchKeys(String constraintType) {
@Override
public ResultQuery<Record6<String, String, String, String, String, Integer>> uniqueKeysQuery(List<String> schemas) {
return keysQuery(schemas, inline("UNIQUE"));
}
private ResultQuery<Record6<String, String, String, String, String, Integer>> keysQuery(List<String> schemas, Field<String> constraintType) {
return create()
.select(
KEY_COLUMN_USAGE.CONSTRAINT_NAME,
KEY_COLUMN_USAGE.TABLE_CATALOG,
KEY_COLUMN_USAGE.TABLE_SCHEMA,
KEY_COLUMN_USAGE.TABLE_NAME,
KEY_COLUMN_USAGE.COLUMN_NAME)
KEY_COLUMN_USAGE.CONSTRAINT_NAME,
KEY_COLUMN_USAGE.COLUMN_NAME,
KEY_COLUMN_USAGE.ORDINAL_POSITION)
.from(KEY_COLUMN_USAGE)
.where(KEY_COLUMN_USAGE.tableConstraints().CONSTRAINT_TYPE.equal(constraintType))
.and(KEY_COLUMN_USAGE.tableConstraints().TABLE_SCHEMA.in(getInputSchemata()))
.where(KEY_COLUMN_USAGE.tableConstraints().CONSTRAINT_TYPE.eq(constraintType))
.and(KEY_COLUMN_USAGE.tableConstraints().TABLE_SCHEMA.in(schemas))
.orderBy(
KEY_COLUMN_USAGE.TABLE_SCHEMA.asc(),
KEY_COLUMN_USAGE.TABLE_NAME.asc(),
KEY_COLUMN_USAGE.CONSTRAINT_NAME.asc(),
KEY_COLUMN_USAGE.ORDINAL_POSITION.asc())
.fetch();
KEY_COLUMN_USAGE.ORDINAL_POSITION.asc());
}
@Override

View File

@ -89,6 +89,7 @@ import org.jooq.Sequence;
import org.jooq.Table;
import org.jooq.TableOptions.TableType;
import org.jooq.UniqueKey;
import org.jooq.tools.StringUtils;
/**
* A class producing a diff between two {@link Meta} objects.
@ -147,7 +148,8 @@ final class Diff {
@Override
public void drop(DiffResult r, Schema s) {
if (s.getTables().isEmpty() && s.getSequences().isEmpty()) {
r.queries.add(ctx.dropSchema(s));
if (!StringUtils.isEmpty(s.getName()))
r.queries.add(ctx.dropSchema(s));
}
else if (migrateConf.dropSchemaCascade()) {
@ -157,7 +159,8 @@ final class Diff {
for (ForeignKey<?, ?> fk : uk.getReferences())
r.droppedFks.add(fk);
r.queries.add(ctx.dropSchema(s).cascade());
if (!StringUtils.isEmpty(s.getName()))
r.queries.add(ctx.dropSchema(s).cascade());
}
else {
for (Table<?> t : s.getTables())
@ -166,7 +169,8 @@ final class Diff {
for (Sequence<?> seq : s.getSequences())
DROP_SEQUENCE.drop(r, seq);
r.queries.add(ctx.dropSchema(s));
if (!StringUtils.isEmpty(s.getName()))
r.queries.add(ctx.dropSchema(s));
}
}
};

View File

@ -43,6 +43,7 @@ import static java.lang.Boolean.TRUE;
// ...
// ...
// ...
import static org.jooq.SQLDialect.FIREBIRD;
import static org.jooq.SQLDialect.H2;
import static org.jooq.SQLDialect.HSQLDB;
// ...
@ -55,16 +56,17 @@ import static org.jooq.SQLDialect.SQLITE;
import static org.jooq.impl.AbstractNamed.findIgnoreCase;
import static org.jooq.impl.DSL.condition;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.Tools.EMPTY_OBJECT;
import static org.jooq.impl.Tools.EMPTY_SORTFIELD;
import static org.jooq.tools.StringUtils.defaultString;
import java.io.IOException;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@ -72,6 +74,8 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import java.util.Properties;
import java.util.Set;
import org.jooq.Catalog;
@ -103,6 +107,8 @@ import org.jooq.exception.SQLDialectNotSupportedException;
import org.jooq.tools.JooqLogger;
import org.jooq.tools.StringUtils;
import org.jetbrains.annotations.NotNull;
/**
* An implementation of the public {@link Meta} type.
* <p>
@ -120,14 +126,28 @@ final class MetaImpl extends AbstractMeta {
private static final Set<SQLDialect> EXPRESSION_COLUMN_DEFAULT = SQLDialect.supportedBy(H2, POSTGRES);
private static final Set<SQLDialect> ENCODED_TIMESTAMP_PRECISION = SQLDialect.supportedBy(HSQLDB, MARIADB);
private static final Set<SQLDialect> NO_SUPPORT_TIMESTAMP_PRECISION = SQLDialect.supportedBy(MYSQL, SQLITE);
private static final Set<SQLDialect> NO_SUPPORT_SCHEMAS = SQLDialect.supportedBy(FIREBIRD, SQLITE);
private static final Pattern P_DERBY_SYSINDEX = Pattern.compile("^(?:SQL\\d{14,}).*$");
private static final Pattern P_H2_SYSINDEX = Pattern.compile("^(?:PRIMARY_KEY_|UK_INDEX_|FK_INDEX_).*$");
private final DatabaseMetaData databaseMetaData;
private final boolean inverseSchemaCatalog;
private static final Properties META_SQL = new Properties();
static {
try {
META_SQL.load(MetaImpl.class.getResourceAsStream("/meta/metasql.properties"));
}
catch (IOException e) {
log.error("Cannot load metasql.properties", e);
}
}
MetaImpl(Configuration configuration, DatabaseMetaData databaseMetaData) {
super(configuration);
@ -326,6 +346,7 @@ final class MetaImpl extends AbstractMeta {
*/
private static final long serialVersionUID = -2621899850912554198L;
private transient volatile Map<Name, Result<Record>> columnCache;
private transient volatile Map<Name, Result<Record>> ukCache;
MetaSchema(String name, Catalog catalog) {
super(name, catalog);
@ -423,7 +444,7 @@ final class MetaImpl extends AbstractMeta {
: TableType.TABLE;
result.add(new MetaTable(name, this, getColumns(catalog, schema, name), tableType));
result.add(new MetaTable(name, this, getColumns(catalog, schema, name), getUks(catalog, schema, name), tableType));
// TODO: Find a more efficient way to do this
// Result<Record> pkColumns = executor.fetch(meta().getPrimaryKeys(catalog, schema, name))
@ -435,6 +456,47 @@ final class MetaImpl extends AbstractMeta {
return result;
}
private final Result<Record> getUks(final String catalog, final String schema, final String table) {
if (ukCache == null) {
final String sql = META_SQL.getProperty("uniqueKeysQuery." + family());
if (sql != null) {
Result<Record> result = meta(new MetaFunction() {
@Override
public Result<Record> run(DatabaseMetaData meta) throws SQLException {
return DSL.using(meta.getConnection(), family()).resultQuery(
sql,
NO_SUPPORT_SCHEMAS.contains(dialect())
? EMPTY_OBJECT
: inverseSchemaCatalog
? new Object[] { catalog }
: new Object[] { schema }
).fetch();
}
});
// TODO Support catalogs as well
Map<Record, Result<Record>> groups = result.intoGroups(new Field[] { result.field(0), result.field(1), result.field(2) });
ukCache = new LinkedHashMap<>();
for (Entry<Record, Result<Record>> entry : groups.entrySet()) {
Record key = entry.getKey();
Result<Record> value = entry.getValue();
ukCache.put(name(
catalog == null ? null : key.get(0, String.class),
key.get(1, String.class),
key.get(2, String.class)
), value);
}
}
}
if (ukCache != null)
return ukCache.get(name(catalog, schema, table));
else
return null;
}
@SuppressWarnings("unchecked")
private final Result<Record> getColumns(String catalog, String schema, String table) {
@ -554,15 +616,18 @@ final class MetaImpl extends AbstractMeta {
/**
* Generated UID
*/
private static final long serialVersionUID = 4843841667753000233L;
private static final long serialVersionUID = 4843841667753000233L;
private final Result<Record> uks;
MetaTable(String name, Schema schema, Result<Record> columns, TableType tableType) {
MetaTable(String name, Schema schema, Result<Record> columns, Result<Record> uks, TableType tableType) {
super(name(name), schema, null, null, null, null, null, TableOptions.of(tableType));
// Possible scenarios for columns being null:
// - The "table" is in fact a SYNONYM
if (columns != null)
init(columns);
initColumns(columns);
this.uks = uks;
}
@Override
@ -629,10 +694,16 @@ final class MetaImpl extends AbstractMeta {
if (constraints.contains(indexName))
it.remove();
// In H2, system indexes are called PRIMARY_KEY_xx_y
//
else switch (family()) {
case DERBY:
if (P_DERBY_SYSINDEX.matcher(indexName).matches())
it.remove();
break;
case H2:
if (indexName.startsWith("PRIMARY_KEY_") || indexName.startsWith("FK_INDEX_"))
if (P_H2_SYSINDEX.matcher(indexName).matches())
it.remove();
break;
@ -642,10 +713,26 @@ final class MetaImpl extends AbstractMeta {
return result;
}
@SuppressWarnings("unchecked")
@Override
public final List<UniqueKey<Record>> getKeys() {
List<UniqueKey<Record>> result = new ArrayList<>();
UniqueKey<Record> pk = getPrimaryKey();
return pk == null ? Collections.<UniqueKey<Record>>emptyList() : Collections.<UniqueKey<Record>>singletonList(pk);
if (pk != null)
result.add(pk);
if (uks != null) {
Map<String, Result<Record>> groups = uks.intoGroups((Field<String>) uks.field(3));
for (Entry<String, Result<Record>> group : groups.entrySet()) {
Result<Record> columns = group.getValue();
columns.sortAsc(5);
result.add(createUniqueKey(columns, 4, 3, false));
}
}
return result;
}
@Override
@ -689,12 +776,12 @@ final class MetaImpl extends AbstractMeta {
// Sort by KEY_SEQ
result.sortAsc(4);
return createPrimaryKey(result, 3);
return createUniqueKey(result, 3, 5, true);
}
@Override
@SuppressWarnings("unchecked")
public List<ForeignKey<Record, ?>> getReferences() {
public final List<ForeignKey<Record, ?>> getReferences() {
Result<Record> result = meta(new MetaFunction() {
@Override
public Result<Record> run(DatabaseMetaData meta) throws SQLException {
@ -754,7 +841,7 @@ final class MetaImpl extends AbstractMeta {
this,
name(fkName),
fkFields,
new MetaPrimaryKey(pkTable, pkName, pkFields),
new MetaUniqueKey(pkTable, pkName, pkFields, true), // TODO: Can we know whether it is a PK or UK?
pkFields,
true
));
@ -808,7 +895,7 @@ final class MetaImpl extends AbstractMeta {
@SuppressWarnings("unchecked")
private final UniqueKey<Record> createPrimaryKey(Result<Record> result, int columnName) {
private final UniqueKey<Record> createUniqueKey(Result<Record> result, int columnName, int keyName, boolean isPrimary) {
if (result.size() > 0) {
TableField<Record, ?>[] f = new TableField[result.size()];
@ -826,8 +913,8 @@ final class MetaImpl extends AbstractMeta {
f[i] = (TableField<Record, ?>) field;
}
String indexName = result.get(0).get(5, String.class);
return new MetaPrimaryKey(this, indexName, f);
String indexName = result.get(0).get(keyName, String.class);
return new MetaUniqueKey(this, indexName, f, isPrimary);
}
else {
return null;
@ -880,7 +967,7 @@ final class MetaImpl extends AbstractMeta {
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private final void init(Result<Record> columns) {
private final void initColumns(Result<Record> columns) {
boolean hasAutoIncrement = false;
for (Record column : columns) {
@ -989,20 +1076,23 @@ final class MetaImpl extends AbstractMeta {
}
}
private final class MetaPrimaryKey extends AbstractKey<Record> implements UniqueKey<Record> {
private final class MetaUniqueKey extends AbstractKey<Record> implements UniqueKey<Record> {
/**
* Generated UID
*/
private static final long serialVersionUID = 6997258619475953490L;
private static final long serialVersionUID = 6997258619475953490L;
private final boolean isPrimary;
MetaPrimaryKey(Table<Record> table, String pkName, TableField<Record, ?>[] fields) {
super(table, pkName == null ? null : name(pkName), fields, true);
MetaUniqueKey(Table<Record> table, String name, TableField<Record, ?>[] fields, boolean isPrimary) {
super(table, name == null ? null : name(name), fields, true);
this.isPrimary = isPrimary;
}
@Override
public final boolean isPrimary() {
return true;
return isPrimary;
}
@Override

View File

@ -321,6 +321,7 @@ final class Tools {
static final int[] EMPTY_INT = {};
static final JSONEntry<?>[] EMPTY_JSONENTRY = {};
static final Name[] EMPTY_NAME = {};
static final Object[] EMPTY_OBJECT = {};
static final Param<?>[] EMPTY_PARAM = {};
static final OrderField<?>[] EMPTY_ORDERFIELD = {};
static final Query[] EMPTY_QUERY = {};

View File

@ -0,0 +1,11 @@
# The queries generated from the various jOOQ-meta ResultQueryDatabase types.
uniqueKeysQuery.FIREBIRD=select null catalog, null schema, trim(RDB$RELATION_CONSTRAINTS.RDB$RELATION_NAME) RDB$RELATION_NAME, trim(RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_NAME) RDB$CONSTRAINT_NAME, trim(RDB$INDEX_SEGMENTS.RDB$FIELD_NAME) RDB$FIELD_NAME, RDB$INDEX_SEGMENTS.RDB$FIELD_POSITION from RDB$RELATION_CONSTRAINTS join RDB$INDEX_SEGMENTS on RDB$INDEX_SEGMENTS.RDB$INDEX_NAME = RDB$RELATION_CONSTRAINTS.RDB$INDEX_NAME where RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_TYPE = 'UNIQUE' order by RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_NAME asc, RDB$INDEX_SEGMENTS.RDB$FIELD_POSITION asc
uniqueKeysQuery.H2=select INFORMATION_SCHEMA.CONSTRAINTS.TABLE_CATALOG, INFORMATION_SCHEMA.CONSTRAINTS.TABLE_SCHEMA, INFORMATION_SCHEMA.CONSTRAINTS.TABLE_NAME, INFORMATION_SCHEMA.CONSTRAINTS.CONSTRAINT_NAME, INFORMATION_SCHEMA.INDEXES.COLUMN_NAME, INFORMATION_SCHEMA.INDEXES.ORDINAL_POSITION from INFORMATION_SCHEMA.CONSTRAINTS join INFORMATION_SCHEMA.INDEXES on (INFORMATION_SCHEMA.CONSTRAINTS.TABLE_SCHEMA = INFORMATION_SCHEMA.INDEXES.TABLE_SCHEMA and INFORMATION_SCHEMA.CONSTRAINTS.TABLE_NAME = INFORMATION_SCHEMA.INDEXES.TABLE_NAME and INFORMATION_SCHEMA.CONSTRAINTS.UNIQUE_INDEX_NAME = INFORMATION_SCHEMA.INDEXES.INDEX_NAME) where (INFORMATION_SCHEMA.CONSTRAINTS.TABLE_SCHEMA in (cast(? as varchar(2147483647))) and INFORMATION_SCHEMA.CONSTRAINTS.CONSTRAINT_TYPE in ('UNIQUE')) order by INFORMATION_SCHEMA.CONSTRAINTS.TABLE_SCHEMA, INFORMATION_SCHEMA.CONSTRAINTS.CONSTRAINT_NAME, INFORMATION_SCHEMA.INDEXES.ORDINAL_POSITION
uniqueKeysQuery.MARIADB=select distinct null as TABLE_CATALOG, information_schema.STATISTICS.TABLE_SCHEMA, information_schema.STATISTICS.TABLE_NAME, information_schema.STATISTICS.INDEX_NAME, information_schema.STATISTICS.COLUMN_NAME, information_schema.STATISTICS.SEQ_IN_INDEX from information_schema.STATISTICS where (information_schema.STATISTICS.TABLE_SCHEMA in (?, 'ee7f6174-34f2-484b-8d81-20a4d9fc866d') and information_schema.STATISTICS.INDEX_NAME <> 'PRIMARY' and information_schema.STATISTICS.NON_UNIQUE = 0) order by information_schema.STATISTICS.TABLE_SCHEMA, information_schema.STATISTICS.TABLE_NAME, information_schema.STATISTICS.INDEX_NAME, information_schema.STATISTICS.SEQ_IN_INDEX
uniqueKeysQuery.MYSQL=select distinct null as TABLE_CATALOG, information_schema.STATISTICS.TABLE_SCHEMA, information_schema.STATISTICS.TABLE_NAME, information_schema.STATISTICS.INDEX_NAME, information_schema.STATISTICS.COLUMN_NAME, information_schema.STATISTICS.SEQ_IN_INDEX from information_schema.STATISTICS where (information_schema.STATISTICS.TABLE_SCHEMA in (?, 'ee7f6174-34f2-484b-8d81-20a4d9fc866d') and information_schema.STATISTICS.INDEX_NAME <> 'PRIMARY' and information_schema.STATISTICS.NON_UNIQUE = 0) order by information_schema.STATISTICS.TABLE_SCHEMA, information_schema.STATISTICS.TABLE_NAME, information_schema.STATISTICS.INDEX_NAME, information_schema.STATISTICS.SEQ_IN_INDEX
uniqueKeysQuery.POSTGRES=select information_schema.key_column_usage.table_catalog, information_schema.key_column_usage.table_schema, information_schema.key_column_usage.table_name, information_schema.key_column_usage.constraint_name, information_schema.key_column_usage.column_name, information_schema.key_column_usage.ordinal_position from (information_schema.key_column_usage left outer join information_schema.table_constraints as alias_99043051 on (information_schema.key_column_usage.constraint_catalog = alias_99043051.constraint_catalog and information_schema.key_column_usage.constraint_schema = alias_99043051.constraint_schema and information_schema.key_column_usage.constraint_name = alias_99043051.constraint_name)) where (alias_99043051.constraint_type = 'UNIQUE' and alias_99043051.table_schema in (?)) order by information_schema.key_column_usage.table_schema asc, information_schema.key_column_usage.table_name asc, information_schema.key_column_usage.constraint_name asc, information_schema.key_column_usage.ordinal_position asc

View File

@ -647,6 +647,7 @@
<module>jOOQ-migrations</module>
<module>jOOQ-kotlin</module>
<module>jOOQ-scala_2.13</module>
@ -671,6 +672,10 @@
</modules>