[jOOQ/jOOQ#8972] jOOQ-meta should query dictionary views for column existence rather than the column itself (WIP)

This commit is contained in:
Lukas Eder 2019-07-22 11:27:01 +02:00
parent 24f703eeb3
commit 83a6711412
7 changed files with 456 additions and 26 deletions

View File

@ -61,6 +61,7 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import org.jooq.Condition;
import org.jooq.Configuration;
import org.jooq.DSLContext;
import org.jooq.ExecuteContext;
@ -68,7 +69,9 @@ import org.jooq.ExecuteListenerProvider;
import org.jooq.Name;
import org.jooq.Query;
import org.jooq.SQLDialect;
import org.jooq.Schema;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.conf.Settings;
import org.jooq.conf.SettingsTools;
import org.jooq.exception.DataAccessException;
@ -200,12 +203,14 @@ public abstract class AbstractDatabase implements Database {
private final List<Definition> all;
private final List<Definition> included;
private final List<Definition> excluded;
private final Map<Table<?>, Boolean> exists;
private final Map<Table<?>, Boolean> existTables;
private final Map<TableField<?, ?>, Boolean> existFields;
private final Patterns patterns;
private final Statements statements;
protected AbstractDatabase() {
exists = new HashMap<>();
existTables = new HashMap<>();
existFields = new HashMap<>();
patterns = new Patterns();
statements = new Statements();
filters = new ArrayList<>();
@ -364,25 +369,102 @@ public abstract class AbstractDatabase implements Database {
}
}
// [#8972] TODO: Implement these for all non-PostgresDatabase databases.
@Override
public final boolean exists(Table<?> table) {
Boolean result = exists.get(table);
public TableQualifier tableQualifier() {
return null;
}
@Override
public ColumnQualifier columnQualifier() {
return null;
}
@Override
public final boolean exists(TableField<?, ?> field) {
Boolean result = existFields.get(field);
if (result == null) {
try {
create(true)
.selectOne()
.from(table)
.where(falseCondition())
.fetch();
ColumnQualifier qualifier = columnQualifier();
result = true;
// [#8972] In the presence of a qualifier, avoid running an error-
// triggering query, which may lead to unwanted log messages
if (qualifier == null) {
try {
create(true)
.select(field)
.from(field.getTable())
.where(falseCondition())
.fetch();
result = true;
}
catch (DataAccessException e) {
result = false;
}
}
catch (DataAccessException e) {
result = false;
else {
Condition condition = qualifier.columnName().eq(field.getName());
Table<?> table = field.getTable();
condition = condition.and(qualifier.tableName().eq(table.getName()));
Schema schema = table.getSchema();
if (schema != null)
condition = condition.and(qualifier.tableSchema().eq(schema.getName()));
result = create().fetchExists(qualifier.table(), condition);
}
exists.put(table, result);
existFields.put(field, result);
}
return result;
}
@Override
public final boolean existAll(TableField<?, ?>... f) {
for (TableField<?, ?> field : f)
if (!exists(field))
return false;
return true;
}
@Override
public final boolean exists(Table<?> table) {
Boolean result = existTables.get(table);
if (result == null) {
TableQualifier qualifier = tableQualifier();
// [#8972] In the presence of a qualifier, avoid running an error-
// triggering query, which may lead to unwanted log messages
if (qualifier == null) {
try {
create(true)
.selectOne()
.from(table)
.where(falseCondition())
.fetch();
result = true;
}
catch (DataAccessException e) {
result = false;
}
}
else {
Condition condition = qualifier.tableName().eq(table.getName());
Schema schema = table.getSchema();
if (schema != null)
condition = condition.and(qualifier.tableSchema().eq(schema.getName()));
result = create().fetchExists(qualifier.table(), condition);
}
existTables.put(table, result);
}
return result;

View File

@ -0,0 +1,75 @@
/*
* 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 org.jooq.Table;
import org.jooq.TableField;
/**
* A qualifier for columns in the dictionary views.
*
* @author Lukas Eder
*/
public interface ColumnQualifier {
/**
* The table containing column meta information.
*/
Table<?> table();
/**
* The field specifying the column table's catalog name.
*/
TableField<?, String> tableCatalog();
/**
* The field specifying the column table's schema name.
*/
TableField<?, String> tableSchema();
/**
* The field specifying the column table's table name.
*/
TableField<?, String> tableName();
/**
* The field specifying the column name.
*/
TableField<?, String> columnName();
}

View File

@ -47,6 +47,7 @@ import org.jooq.DSLContext;
import org.jooq.Name;
import org.jooq.SQLDialect;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.meta.jaxb.CatalogMappingType;
import org.jooq.meta.jaxb.CustomType;
import org.jooq.meta.jaxb.Embeddable;
@ -903,6 +904,28 @@ public interface Database extends AutoCloseable {
*/
boolean tableValuedFunctions();
/**
* The table qualifier used by this database, or <code>null</code>, if this
* database doesn't specify such a qualifier.
*/
TableQualifier tableQualifier();
/**
* The column qualifier used by this database, or <code>null</code>, if this
* database doesn't specify such a qualifier.
*/
ColumnQualifier columnQualifier();
/**
* Check for the existence of a table field in the dictionary views.
*/
boolean exists(TableField<?, ?> field);
/**
* Check for the existence of several table fields in the dictionary views.
*/
boolean existAll(TableField<?, ?>... fields);
/**
* Check for the existence of a table in the dictionary views.
*/

View File

@ -0,0 +1,92 @@
/*
* 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 org.jooq.Table;
import org.jooq.TableField;
/**
* @author Lukas Eder
*/
public class DefaultColumnQualifier implements ColumnQualifier {
private final Table<?> table;
private final TableField<?, String> tableCatalog;
private final TableField<?, String> tableSchema;
private final TableField<?, String> tableName;
private final TableField<?, String> columnName;
public DefaultColumnQualifier(
Table<?> table,
TableField<?, String> tableCatalog,
TableField<?, String> tableSchema,
TableField<?, String> tableName,
TableField<?, String> columnName
) {
this.table = table;
this.tableCatalog = tableCatalog;
this.tableSchema = tableSchema;
this.tableName = tableName;
this.columnName = columnName;
}
@Override
public Table<?> table() {
return table;
}
@Override
public TableField<?, String> tableCatalog() {
return tableCatalog;
}
@Override
public TableField<?, String> tableSchema() {
return tableSchema;
}
@Override
public TableField<?, String> tableName() {
return tableName;
}
@Override
public TableField<?, String> columnName() {
return columnName;
}
}

View File

@ -0,0 +1,84 @@
/*
* 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 org.jooq.Table;
import org.jooq.TableField;
/**
* @author Lukas Eder
*/
public class DefaultTableQualifier implements TableQualifier {
private final Table<?> table;
private final TableField<?, String> tableCatalog;
private final TableField<?, String> tableSchema;
private final TableField<?, String> tableName;
public DefaultTableQualifier(
Table<?> table,
TableField<?, String> tableCatalog,
TableField<?, String> tableSchema,
TableField<?, String> tableName
) {
this.table = table;
this.tableCatalog = tableCatalog;
this.tableSchema = tableSchema;
this.tableName = tableName;
}
@Override
public Table<?> table() {
return table;
}
@Override
public TableField<?, String> tableCatalog() {
return tableCatalog;
}
@Override
public TableField<?, String> tableSchema() {
return tableSchema;
}
@Override
public TableField<?, String> tableName() {
return tableName;
}
}

View File

@ -0,0 +1,70 @@
/*
* 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 org.jooq.Table;
import org.jooq.TableField;
/**
* A qualifier for tables in the dictionary views.
*
* @author Lukas Eder
*/
public interface TableQualifier {
/**
* The table containing table meta information.
*/
Table<?> table();
/**
* The field specifying the table's catalog name.
*/
TableField<?, String> tableCatalog();
/**
* The field specifying the table's schema name.
*/
TableField<?, String> tableSchema();
/**
* The field specifying the table's table name.
*/
TableField<?, String> tableName();
}

View File

@ -57,6 +57,7 @@ import static org.jooq.impl.DSL.select;
import static org.jooq.impl.DSL.when;
import static org.jooq.meta.postgres.information_schema.Tables.ATTRIBUTES;
import static org.jooq.meta.postgres.information_schema.Tables.CHECK_CONSTRAINTS;
import static org.jooq.meta.postgres.information_schema.Tables.COLUMNS;
import static org.jooq.meta.postgres.information_schema.Tables.KEY_COLUMN_USAGE;
import static org.jooq.meta.postgres.information_schema.Tables.PARAMETERS;
import static org.jooq.meta.postgres.information_schema.Tables.ROUTINES;
@ -102,14 +103,17 @@ import org.jooq.meta.AbstractIndexDefinition;
import org.jooq.meta.ArrayDefinition;
import org.jooq.meta.CatalogDefinition;
import org.jooq.meta.ColumnDefinition;
import org.jooq.meta.ColumnQualifier;
import org.jooq.meta.DataTypeDefinition;
import org.jooq.meta.DefaultCheckConstraintDefinition;
import org.jooq.meta.DefaultColumnQualifier;
import org.jooq.meta.DefaultDataTypeDefinition;
import org.jooq.meta.DefaultDomainDefinition;
import org.jooq.meta.DefaultEnumDefinition;
import org.jooq.meta.DefaultIndexColumnDefinition;
import org.jooq.meta.DefaultRelations;
import org.jooq.meta.DefaultSequenceDefinition;
import org.jooq.meta.DefaultTableQualifier;
import org.jooq.meta.DomainDefinition;
import org.jooq.meta.EnumDefinition;
import org.jooq.meta.IndexColumnDefinition;
@ -119,6 +123,7 @@ import org.jooq.meta.RoutineDefinition;
import org.jooq.meta.SchemaDefinition;
import org.jooq.meta.SequenceDefinition;
import org.jooq.meta.TableDefinition;
import org.jooq.meta.TableQualifier;
import org.jooq.meta.UDTDefinition;
import org.jooq.meta.hsqldb.HSQLDBDatabase;
import org.jooq.meta.postgres.information_schema.tables.CheckConstraints;
@ -871,23 +876,22 @@ public class PostgresDatabase extends AbstractDatabase {
// [#7785] pg_proc.prokind was added in PostgreSQL 11 only, and
// pg_proc.proisagg was removed, incompatibly
try {
create(true)
.select(PG_PROC.PROKIND)
.from(PG_PROC)
.where(falseCondition())
.fetch();
is11 = true;
}
catch (DataAccessException e) {
is11 = false;
}
is11 = exists(PG_PROC.PROKIND);
}
return is11;
}
@Override
public TableQualifier tableQualifier() {
return new DefaultTableQualifier(TABLES, TABLES.TABLE_CATALOG, TABLES.TABLE_SCHEMA, TABLES.TABLE_NAME);
}
@Override
public ColumnQualifier columnQualifier() {
return new DefaultColumnQualifier(COLUMNS, COLUMNS.TABLE_CATALOG, COLUMNS.TABLE_SCHEMA, COLUMNS.TABLE_NAME, COLUMNS.COLUMN_NAME);
}
boolean canCombineArrays() {
if (canCombineArrays == null) {