From 83a6711412d2aa2f54fbc98db3e7ef2fe08f076e Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Mon, 22 Jul 2019 11:27:01 +0200 Subject: [PATCH] [jOOQ/jOOQ#8972] jOOQ-meta should query dictionary views for column existence rather than the column itself (WIP) --- .../java/org/jooq/meta/AbstractDatabase.java | 110 +++++++++++++++--- .../java/org/jooq/meta/ColumnQualifier.java | 75 ++++++++++++ .../src/main/java/org/jooq/meta/Database.java | 23 ++++ .../org/jooq/meta/DefaultColumnQualifier.java | 92 +++++++++++++++ .../org/jooq/meta/DefaultTableQualifier.java | 84 +++++++++++++ .../java/org/jooq/meta/TableQualifier.java | 70 +++++++++++ .../jooq/meta/postgres/PostgresDatabase.java | 28 +++-- 7 files changed, 456 insertions(+), 26 deletions(-) create mode 100644 jOOQ-meta/src/main/java/org/jooq/meta/ColumnQualifier.java create mode 100644 jOOQ-meta/src/main/java/org/jooq/meta/DefaultColumnQualifier.java create mode 100644 jOOQ-meta/src/main/java/org/jooq/meta/DefaultTableQualifier.java create mode 100644 jOOQ-meta/src/main/java/org/jooq/meta/TableQualifier.java diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/AbstractDatabase.java b/jOOQ-meta/src/main/java/org/jooq/meta/AbstractDatabase.java index 76d7248f5c..254dfb3111 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/AbstractDatabase.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/AbstractDatabase.java @@ -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 all; private final List included; private final List excluded; - private final Map, Boolean> exists; + private final Map, Boolean> existTables; + private final Map, 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; diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/ColumnQualifier.java b/jOOQ-meta/src/main/java/org/jooq/meta/ColumnQualifier.java new file mode 100644 index 0000000000..cd98d56092 --- /dev/null +++ b/jOOQ-meta/src/main/java/org/jooq/meta/ColumnQualifier.java @@ -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 tableCatalog(); + + /** + * The field specifying the column table's schema name. + */ + TableField tableSchema(); + + /** + * The field specifying the column table's table name. + */ + TableField tableName(); + + /** + * The field specifying the column name. + */ + TableField columnName(); + +} diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/Database.java b/jOOQ-meta/src/main/java/org/jooq/meta/Database.java index 9232adef52..4c5749dffc 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/Database.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/Database.java @@ -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 null, if this + * database doesn't specify such a qualifier. + */ + TableQualifier tableQualifier(); + + /** + * The column qualifier used by this database, or null, 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. */ diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/DefaultColumnQualifier.java b/jOOQ-meta/src/main/java/org/jooq/meta/DefaultColumnQualifier.java new file mode 100644 index 0000000000..eac3ec9258 --- /dev/null +++ b/jOOQ-meta/src/main/java/org/jooq/meta/DefaultColumnQualifier.java @@ -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 tableCatalog; + private final TableField tableSchema; + private final TableField tableName; + private final TableField columnName; + + public DefaultColumnQualifier( + Table table, + TableField tableCatalog, + TableField tableSchema, + TableField tableName, + TableField 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 tableCatalog() { + return tableCatalog; + } + + @Override + public TableField tableSchema() { + return tableSchema; + } + + @Override + public TableField tableName() { + return tableName; + } + + @Override + public TableField columnName() { + return columnName; + } +} diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/DefaultTableQualifier.java b/jOOQ-meta/src/main/java/org/jooq/meta/DefaultTableQualifier.java new file mode 100644 index 0000000000..9248a8ea36 --- /dev/null +++ b/jOOQ-meta/src/main/java/org/jooq/meta/DefaultTableQualifier.java @@ -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 tableCatalog; + private final TableField tableSchema; + private final TableField tableName; + + public DefaultTableQualifier( + Table table, + TableField tableCatalog, + TableField tableSchema, + TableField tableName + ) { + this.table = table; + this.tableCatalog = tableCatalog; + this.tableSchema = tableSchema; + this.tableName = tableName; + } + + @Override + public Table table() { + return table; + } + + @Override + public TableField tableCatalog() { + return tableCatalog; + } + + @Override + public TableField tableSchema() { + return tableSchema; + } + + @Override + public TableField tableName() { + return tableName; + } +} diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/TableQualifier.java b/jOOQ-meta/src/main/java/org/jooq/meta/TableQualifier.java new file mode 100644 index 0000000000..b522488f1a --- /dev/null +++ b/jOOQ-meta/src/main/java/org/jooq/meta/TableQualifier.java @@ -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 tableCatalog(); + + /** + * The field specifying the table's schema name. + */ + TableField tableSchema(); + + /** + * The field specifying the table's table name. + */ + TableField tableName(); + +} 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 f228d3b394..8af491b3b8 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 @@ -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) {