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
diff --git a/jOOQ/src/main/java/org/jooq/impl/Diff.java b/jOOQ/src/main/java/org/jooq/impl/Diff.java
index 00dfda879d..fc9962293b 100644
--- a/jOOQ/src/main/java/org/jooq/impl/Diff.java
+++ b/jOOQ/src/main/java/org/jooq/impl/Diff.java
@@ -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));
}
}
};
diff --git a/jOOQ/src/main/java/org/jooq/impl/MetaImpl.java b/jOOQ/src/main/java/org/jooq/impl/MetaImpl.java
index 731dd54cd5..c7960444b7 100644
--- a/jOOQ/src/main/java/org/jooq/impl/MetaImpl.java
+++ b/jOOQ/src/main/java/org/jooq/impl/MetaImpl.java
@@ -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.
*
@@ -120,14 +126,28 @@ final class MetaImpl extends AbstractMeta {
private static final Set EXPRESSION_COLUMN_DEFAULT = SQLDialect.supportedBy(H2, POSTGRES);
private static final Set ENCODED_TIMESTAMP_PRECISION = SQLDialect.supportedBy(HSQLDB, MARIADB);
private static final Set NO_SUPPORT_TIMESTAMP_PRECISION = SQLDialect.supportedBy(MYSQL, SQLITE);
+ private static final Set 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> columnCache;
+ private transient volatile Map> 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 pkColumns = executor.fetch(meta().getPrimaryKeys(catalog, schema, name))
@@ -435,6 +456,47 @@ final class MetaImpl extends AbstractMeta {
return result;
}
+ private final Result 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 result = meta(new MetaFunction() {
+ @Override
+ public Result 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> groups = result.intoGroups(new Field[] { result.field(0), result.field(1), result.field(2) });
+ ukCache = new LinkedHashMap<>();
+
+ for (Entry> entry : groups.entrySet()) {
+ Record key = entry.getKey();
+ Result 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 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 uks;
- MetaTable(String name, Schema schema, Result columns, TableType tableType) {
+ MetaTable(String name, Schema schema, Result columns, Result 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> getKeys() {
+ List> result = new ArrayList<>();
+
UniqueKey pk = getPrimaryKey();
- return pk == null ? Collections.>emptyList() : Collections.>singletonList(pk);
+ if (pk != null)
+ result.add(pk);
+
+ if (uks != null) {
+ Map> groups = uks.intoGroups((Field) uks.field(3));
+
+ for (Entry> group : groups.entrySet()) {
+ Result 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> getReferences() {
+ public final List> getReferences() {
Result result = meta(new MetaFunction() {
@Override
public Result 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 createPrimaryKey(Result result, int columnName) {
+ private final UniqueKey createUniqueKey(Result result, int columnName, int keyName, boolean isPrimary) {
if (result.size() > 0) {
TableField[] f = new TableField[result.size()];
@@ -826,8 +913,8 @@ final class MetaImpl extends AbstractMeta {
f[i] = (TableField) 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 columns) {
+ private final void initColumns(Result columns) {
boolean hasAutoIncrement = false;
for (Record column : columns) {
@@ -989,20 +1076,23 @@ final class MetaImpl extends AbstractMeta {
}
}
- private final class MetaPrimaryKey extends AbstractKey implements UniqueKey {
+ private final class MetaUniqueKey extends AbstractKey implements UniqueKey {
/**
* Generated UID
*/
- private static final long serialVersionUID = 6997258619475953490L;
+ private static final long serialVersionUID = 6997258619475953490L;
+ private final boolean isPrimary;
- MetaPrimaryKey(Table table, String pkName, TableField[] fields) {
- super(table, pkName == null ? null : name(pkName), fields, true);
+ MetaUniqueKey(Table table, String name, TableField[] 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
diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java
index 77611968f2..df88d9dfdc 100644
--- a/jOOQ/src/main/java/org/jooq/impl/Tools.java
+++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java
@@ -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 = {};
diff --git a/jOOQ/src/main/resources/meta/metasql.properties b/jOOQ/src/main/resources/meta/metasql.properties
new file mode 100644
index 0000000000..308a3cbbcd
--- /dev/null
+++ b/jOOQ/src/main/resources/meta/metasql.properties
@@ -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
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index b5c71a9acf..9b1e480a9e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -647,6 +647,7 @@
jOOQ-migrations
+
jOOQ-kotlin
jOOQ-scala_2.13
@@ -671,6 +672,10 @@
+
+
+
+