This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit e8631df70bae6656aaa89c5b7e5ff832dfd6cde7 Author: Martin Desruisseaux <[email protected]> AuthorDate: Wed Jul 4 16:53:40 2018 +0200 Begins a review of storage/sis-sql module. The constants by database reflection API moved to sis-metadata internal package, for sharing by other classes doing similar operations. --- .../sis/internal/metadata/sql/Reflection.java | 185 ++++++ .../sis/internal/metadata/sql/SQLBuilder.java | 16 +- .../sis/internal/sql/feature/ColumnMetaModel.java | 14 +- .../sis/internal/sql/feature/DataBaseModel.java | 620 ++++++++++----------- .../apache/sis/internal/sql/feature/Dialect.java | 32 +- .../internal/sql/feature/MetaDataConstants.java | 515 ----------------- .../sql/feature/SingleAttributeTypeBuilder.java | 260 --------- .../sis/internal/sql/postgres/PostgresDialect.java | 25 +- 8 files changed, 494 insertions(+), 1173 deletions(-) diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Reflection.java b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Reflection.java new file mode 100644 index 0000000..45f21b2 --- /dev/null +++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/Reflection.java @@ -0,0 +1,185 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ +package org.apache.sis.internal.metadata.sql; + +import java.sql.DatabaseMetaData; + + +/** + * Column names used in database reflection API. Reflection provides information about schemas, tables, columns, + * constraints, <i>etc.</i> in the form of database tables. The main JDBC methods for those reflections are: + * + * <ul> + * <li>{@link DatabaseMetaData#getSchemas()}</li> + * <li>{@link DatabaseMetaData#getTables(String, String, String, String[])}</li> + * <li>{@link DatabaseMetaData#getColumns(String, String, String, String)}</li> + * </ul> + * + * This class enumerates all the constants used by Apache SIS, and only those constants (this give a way to have + * an overview of which database metadata are needed by SIS). Unless specified otherwise, the columns with those + * names contain only {@code String} values. The main exceptions are {@link #DATA_TYPE}, {@link #COLUMN_SIZE} and + * {@link #DELETE_RULE}, which contain integers. + * + * @author Johann Sorel (Geomatys) + * @version 1.0 + * @since 1.0 + * @module + */ +public final class Reflection { + /** + * The {@value} key for getting a schema name. This column appears in all reflection + * operations (listing schemas, tables, columns, constraints, <i>etc.</i>) used by SIS. + * The value in that column may be null. + */ + public static final String TABLE_SCHEM = "TABLE_SCHEM"; + + /** + * The {@value} key for getting a table name. This column appears in most reflection + * operations (listing tables, columns, constraints, <i>etc.</i>) used by SIS. + */ + public static final String TABLE_NAME = "TABLE_NAME"; + + /** + * The {@value} key for getting a table type. The values that may appear in this column + * are listed by {@link DatabaseMetaData#getTableTypes()}. Typical values are "TABLE", + * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM". + */ + public static final String TABLE_TYPE = "TABLE_TYPE"; + + /** + * The {@value} key for getting a column name. + */ + public static final String COLUMN_NAME = "COLUMN_NAME"; + + /** + * The {@value} key for getting the data type as one of {@link java.sql.Types} constants. + * + * <p>Values in this column are integers ({@code int}) rather than {@code String}.</p> + */ + public static final String DATA_TYPE = "DATA_TYPE"; + + /** + * Data source dependent type name. For a UDT the type name is fully qualified. + */ + public static final String TYPE_NAME = "TYPE_NAME"; + + /** + * The {@value} key for the size for of a column. For numeric data, this is the maximum precision. + * For character data, this is the length in characters. + * + * <p>Values in this column are integers ({@code int}) rather than {@code String}.</p> + */ + public static final String COLUMN_SIZE = "COLUMN_SIZE"; + + /** + * The {@value} key for the number of fractional digits. + * Null is returned for data types where {@code DECIMAL_DIGITS} is not applicable. + * + * <p>Values in this column are integers ({@code int}) rather than {@code String}.</p> + */ + public static final String DECIMAL_DIGITS = "DECIMAL_DIGITS"; + + /** + * The {@value} key for the nullability of a column. Possible values are {@code "YES"} if + * the parameter can include NULLs, {@code "NO"} if the parameter cannot include NULLs, + * and empty string if the nullability for the parameter is unknown. + */ + public static final String IS_NULLABLE = "IS_NULLABLE"; + + /** + * The {@value} key for indicating whether this column is auto incremented. Possible values + * are {@code "YES"} if the column is auto incremented, {@code "NO"} if the column is not + * auto incremented, or empty string if whether the column is auto incremented is unknown. + */ + public static final String IS_AUTOINCREMENT = "IS_AUTOINCREMENT"; + + /** + * The {@value} key for comment describing columns. + * Values in this column may be null. + */ + public static final String REMARKS = "REMARKS"; + + /** + * The {@value} key for primary key name. + * Values in this column may be null. + */ + public static final String PK_NAME = "PK_NAME"; + + /** + * The {@value} key for the primary key table schema being imported. + * Values in this column may be null. + */ + public static final String PKTABLE_SCHEM = "PKTABLE_SCHEM"; + + /** + * The {@value} key for the primary key table name being imported. + */ + public static final String PKTABLE_NAME = "PKTABLE_NAME"; + + /** + * The {@value} key for the primary key column name being imported. + */ + public static final String PKCOLUMN_NAME = "PKCOLUMN_NAME"; + + /** + * The {@value} key for foreign key name. + * Values in this column may be null. + */ + public static final String FK_NAME = "FK_NAME"; + + /** + * The {@value} key for the foreign key table schema. + * Values in this column may be null. + */ + public static final String FKTABLE_SCHEM = "FKTABLE_SCHEM"; + + /** + * The {@value} key for the foreign key table name. + */ + public static final String FKTABLE_NAME = "FKTABLE_NAME"; + + /** + * The {@value} key for the foreign key column name. + */ + public static final String FKCOLUMN_NAME = "FKCOLUMN_NAME"; + + /** + * The {@value} key for what happens to the foreign key when primary is deleted. + * Possible values are: + * <ul> + * <li>{@code importedKeyNoAction} — do not allow delete of primary key if it has been imported.</li> + * <li>{@code importedKeyCascade} — delete rows that import a deleted key.</li> + * <li>{@code importedKeySetNull} — change imported key to NULL if its primary key has been deleted.</li> + * <li>{@code importedKeySetDefault} — change imported key to default if its primary key has been deleted.</li> + * </ul> + * + * <p>Values in this column are short integers ({@code short}) rather than {@code String}.</p> + */ + public static final String DELETE_RULE = "DELETE_RULE"; + + /** + * The {@value} key for the name of the index. + * Values in this column may be null. + */ + public static final String INDEX_NAME = "INDEX_NAME"; + + /** + * Do not allow instantiation of this class. + */ + private Reflection() { + } +} diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/SQLBuilder.java b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/SQLBuilder.java index 56661c5..adc0352 100644 --- a/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/SQLBuilder.java +++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/sql/SQLBuilder.java @@ -46,6 +46,14 @@ public class SQLBuilder { private final String quote; /** + * Whether the schema name should be written between quotes. If {@code false}, + * we will let the database engine uses its default lower case / upper case policy. + * + * @see #appendIdentifier(String, String) + */ + private final boolean quoteSchema; + + /** * The string that can be used to escape wildcard characters. * This is the value returned by {@link DatabaseMetaData#getSearchStringEscape()}. */ @@ -57,14 +65,6 @@ public class SQLBuilder { private final StringBuilder buffer = new StringBuilder(); /** - * Whether the schema name should be written between quotes. If {@code false}, - * we will let the database engine uses its default lower case / upper case policy. - * - * @see #appendIdentifier(String, String) - */ - private final boolean quoteSchema; - - /** * Creates a new {@code SQLBuilder} initialized from the given database metadata. * * @param metadata the database metadata. diff --git a/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/ColumnMetaModel.java b/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/ColumnMetaModel.java index 6ff5453..9b86ac3 100644 --- a/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/ColumnMetaModel.java +++ b/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/ColumnMetaModel.java @@ -25,6 +25,7 @@ import java.util.UUID; import org.opengis.feature.AttributeType; import org.apache.sis.feature.DefaultAttributeType; import org.apache.sis.storage.DataStoreException; +import org.apache.sis.internal.metadata.sql.SQLBuilder; /** @@ -161,13 +162,14 @@ public final class ColumnMetaModel { // Generate value if possible. if (Number.class.isAssignableFrom(clazz)) { // Get the maximum value in the database and increment it - final StringBuilder sql = new StringBuilder(); - sql.append("SELECT 1 + MAX("); - dialect.encodeColumnName(sql, name); - sql.append(") FROM "); - dialect.encodeSchemaAndTableName(sql, schema, table); + final String sql = new SQLBuilder(cx.getMetaData(), true) + .append("SELECT 1 + MAX(") + .appendIdentifier(name) + .append(") FROM ") + .appendIdentifier(schema, table) + .toString(); try (Statement st = cx.createStatement(); - ResultSet rs = st.executeQuery(sql.toString())) { + ResultSet rs = st.executeQuery(sql)) { rs.next(); next = rs.getObject(1); } diff --git a/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/DataBaseModel.java b/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/DataBaseModel.java index 5f73ec4..4e3d86d 100644 --- a/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/DataBaseModel.java +++ b/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/DataBaseModel.java @@ -44,8 +44,8 @@ import org.apache.sis.feature.builder.AttributeRole; import org.apache.sis.feature.builder.AttributeTypeBuilder; import org.apache.sis.feature.builder.FeatureTypeBuilder; import org.apache.sis.feature.builder.PropertyTypeBuilder; +import org.apache.sis.internal.metadata.sql.Reflection; import org.apache.sis.internal.feature.Geometries; -import org.apache.sis.internal.sql.feature.MetaDataConstants.*; import org.apache.sis.storage.sql.SQLStore; import org.apache.sis.storage.DataStore; import org.apache.sis.storage.DataStoreException; @@ -63,17 +63,11 @@ import org.apache.sis.util.logging.WarningListeners; * @module */ public final class DataBaseModel { - /** - * @todo Does not seem to be used yet. - */ - private static final String ASSOCIATION_SEPARATOR = "→"; - /** - * The native SRID associated to a certain descriptor - * - * @todo Does not seem to be used yet. - */ - private static final String JDBC_NATIVE_SRID = "nativeSRID"; + private static final String TYPE_TABLE = "TABLE"; + private static final String TYPE_VIEW = "VIEW"; + private static final String VALUE_YES = "YES"; + private static final String VALUE_NO = "NO"; /** * Feature type used to mark types which are sub-types of others. @@ -99,7 +93,6 @@ public final class DataBaseModel { //various cache while analyzing model private DatabaseMetaData metadata; - private CachedResultSet cacheSchemas; private CachedResultSet cacheTables; private CachedResultSet cacheColumns; private CachedResultSet cachePrimaryKeys; @@ -135,8 +128,7 @@ public final class DataBaseModel { } /** - * Clear the model cache. A new database analyze will be made the next time - * it is needed. + * Clear the model cache. A new database analyze will be made the next time it is needed. */ private synchronized void clearCache() { pkIndex = new FeatureNaming<>(); @@ -191,87 +183,59 @@ public final class DataBaseModel { requieredSchemas = new HashSet<>(); try (Connection cx = store.getDataSource().getConnection()) { - metadata = cx.getMetaData(); - - // Cache all metadata informations, we will loop on them plenty of times //////// - cacheSchemas = new CachedResultSet(metadata.getSchemas(), - Schema.TABLE_SCHEM); + /* + * Schema names available in the database: + * 1. TABLE_SCHEM : String => schema name + * 2. TABLE_CATALOG : String => catalog name (may be null) + */ + final CachedResultSet cacheSchemas = new CachedResultSet(metadata.getSchemas(), + Reflection.TABLE_SCHEM); + /* + * Description of the tables available: + * 1. TABLE_SCHEM : String => table schema (may be null) + * 2. TABLE_NAME : String => table name + * 3. TABLE_TYPE : String => table type (typically "TABLE" or "VIEW"). + */ cacheTables = new CachedResultSet( - metadata.getTables(null,null,null,new String[]{Table.VALUE_TYPE_TABLE, Table.VALUE_TYPE_VIEW}), - Table.TABLE_SCHEM, - Table.TABLE_NAME, - Table.TABLE_TYPE); - cacheColumns = new CachedResultSet(metadata.getColumns(null, null, null, "%"), - Column.TABLE_SCHEM, - Column.TABLE_NAME, - Column.COLUMN_NAME, - Column.COLUMN_SIZE, - Column.DATA_TYPE, - Column.TYPE_NAME, - Column.IS_NULLABLE, - Column.IS_AUTOINCREMENT, - Column.REMARKS); - if (dialect.supportGlobalMetadata()) { - cachePrimaryKeys = new CachedResultSet(metadata.getPrimaryKeys(null, null, null), - Column.TABLE_SCHEM, - Column.TABLE_NAME, - Column.COLUMN_NAME); - cacheImportedKeys = new CachedResultSet(metadata.getImportedKeys(null, null, null), - ImportedKey.PK_NAME, - ImportedKey.FK_NAME, - ImportedKey.FKTABLE_SCHEM, - ImportedKey.FKTABLE_NAME, - ImportedKey.FKCOLUMN_NAME, - ImportedKey.PKTABLE_SCHEM, - ImportedKey.PKTABLE_NAME, - ImportedKey.PKCOLUMN_NAME, - ImportedKey.DELETE_RULE); - cacheExportedKeys = new CachedResultSet(metadata.getExportedKeys(null, null, null), - ExportedKey.PK_NAME, - ExportedKey.FK_NAME, - ExportedKey.PKTABLE_SCHEM, - ExportedKey.PKTABLE_NAME, - ExportedKey.PKCOLUMN_NAME, - ExportedKey.FKTABLE_SCHEM, - ExportedKey.FKTABLE_NAME, - ExportedKey.FKCOLUMN_NAME, - ExportedKey.DELETE_RULE); - } else { - //we have to loop ourself on all schema and tables to collect informations - cachePrimaryKeys = new CachedResultSet(); - cacheImportedKeys = new CachedResultSet(); - cacheExportedKeys = new CachedResultSet(); - - final Iterator<Map<String,Object>> ite = cacheSchemas.records.iterator(); - while (ite.hasNext()) { - final String schemaName = (String) ite.next().get(Schema.TABLE_SCHEM); - for (Map<String,Object> info : cacheTables.records) { - if (!Objects.equals(info.get(Table.TABLE_SCHEM),schemaName)) continue; - cachePrimaryKeys.append(metadata.getPrimaryKeys(null, schemaName, (String) info.get(Table.TABLE_NAME)), - Column.TABLE_SCHEM, - Column.TABLE_NAME, - Column.COLUMN_NAME); - cacheImportedKeys.append(metadata.getImportedKeys(null, schemaName, (String) info.get(Table.TABLE_NAME)), - ImportedKey.FKTABLE_SCHEM, - ImportedKey.FKTABLE_NAME, - ImportedKey.FKCOLUMN_NAME, - ImportedKey.PKTABLE_SCHEM, - ImportedKey.PKTABLE_NAME, - ImportedKey.PKCOLUMN_NAME, - ImportedKey.DELETE_RULE); - cacheExportedKeys.append(metadata.getExportedKeys(null, schemaName, (String) info.get(Table.TABLE_NAME)), - ImportedKey.PKTABLE_SCHEM, - ImportedKey.PKTABLE_NAME, - ExportedKey.PKCOLUMN_NAME, - ExportedKey.FKTABLE_SCHEM, - ExportedKey.FKTABLE_NAME, - ExportedKey.FKCOLUMN_NAME, - ImportedKey.DELETE_RULE); - } - } - } - + metadata.getTables(null, null, null, new String[] {TYPE_TABLE, TYPE_VIEW}), // TODO: use metadata.getTableTypes() + Reflection.TABLE_SCHEM, + Reflection.TABLE_NAME, + Reflection.TABLE_TYPE); + cacheColumns = new CachedResultSet(metadata.getColumns(null, null, null, null), + Reflection.TABLE_SCHEM, + Reflection.TABLE_NAME, + Reflection.COLUMN_NAME, + Reflection.COLUMN_SIZE, + Reflection.DATA_TYPE, + Reflection.TYPE_NAME, + Reflection.IS_NULLABLE, + Reflection.IS_AUTOINCREMENT, + Reflection.REMARKS); + cachePrimaryKeys = new CachedResultSet(metadata.getPrimaryKeys(null, null, null), + Reflection.TABLE_SCHEM, + Reflection.TABLE_NAME, + Reflection.COLUMN_NAME); + cacheImportedKeys = new CachedResultSet(metadata.getImportedKeys(null, null, null), + Reflection.PK_NAME, + Reflection.FK_NAME, + Reflection.FKTABLE_SCHEM, + Reflection.FKTABLE_NAME, + Reflection.FKCOLUMN_NAME, + Reflection.PKTABLE_SCHEM, + Reflection.PKTABLE_NAME, + Reflection.PKCOLUMN_NAME, + Reflection.DELETE_RULE); + cacheExportedKeys = new CachedResultSet(metadata.getExportedKeys(null, null, null), + Reflection.PK_NAME, + Reflection.FK_NAME, + Reflection.PKTABLE_SCHEM, + Reflection.PKTABLE_NAME, + Reflection.PKCOLUMN_NAME, + Reflection.FKTABLE_SCHEM, + Reflection.FKTABLE_NAME, + Reflection.FKCOLUMN_NAME, + Reflection.DELETE_RULE); //////////////////////////////////////////////////////////////////////////////// @@ -280,7 +244,7 @@ public final class DataBaseModel { } else { final Iterator<Map<String,Object>> ite = cacheSchemas.records.iterator(); while (ite.hasNext()) { - requieredSchemas.add((String) ite.next().get(Schema.TABLE_SCHEM)); + requieredSchemas.add((String) ite.next().get(Reflection.TABLE_SCHEM)); } } @@ -298,7 +262,6 @@ public final class DataBaseModel { } catch (SQLException e) { throw new DataStoreException("Error occurred analyzing database model.\n" + e.getMessage(), e); } finally { - cacheSchemas = null; cacheTables = null; cacheColumns = null; cachePrimaryKeys = null; @@ -343,279 +306,267 @@ public final class DataBaseModel { } - private SchemaMetaModel analyzeSchema(final String schemaName, final Connection cx) throws DataStoreException { + private SchemaMetaModel analyzeSchema(final String schemaName, final Connection cx) throws SQLException { final SchemaMetaModel schema = new SchemaMetaModel(schemaName); - try { - for (Map<String,Object> info : cacheTables.records) { - if (!Objects.equals(info.get(Table.TABLE_SCHEM), schemaName)) continue; - if (databaseTable != null && !databaseTable.isEmpty() && !Objects.equals(info.get(Table.TABLE_NAME),databaseTable)) continue; - final TableMetaModel table = analyzeTable(info,cx); - schema.tables.put(table.name, table); - } - } catch (SQLException e) { - throw new DataStoreException("Error occurred analyzing database model.", e); + for (Map<String,Object> info : cacheTables.records) { + if (!Objects.equals(info.get(Reflection.TABLE_SCHEM), schemaName)) continue; + if (databaseTable != null && !databaseTable.isEmpty() && !Objects.equals(info.get(Reflection.TABLE_NAME), databaseTable)) continue; + final TableMetaModel table = analyzeTable(info, cx); + schema.tables.put(table.name, table); } return schema; } - private TableMetaModel analyzeTable(final Map tableSet, final Connection cx) throws DataStoreException, SQLException { - final String schemaName = (String) tableSet.get(Table.TABLE_SCHEM); - final String tableName = (String) tableSet.get(Table.TABLE_NAME); - final String tableType = (String) tableSet.get(Table.TABLE_TYPE); - + private TableMetaModel analyzeTable(final Map<String,Object> tableSet, final Connection cx) throws SQLException { + final String schemaName = (String) tableSet.get(Reflection.TABLE_SCHEM); + final String tableName = (String) tableSet.get(Reflection.TABLE_NAME); + final String tableType = (String) tableSet.get(Reflection.TABLE_TYPE); final TableMetaModel table = new TableMetaModel(tableName,tableType); - final FeatureTypeBuilder ftb = new FeatureTypeBuilder(); - try { + // Explore all columns ---------------------------------------------- + final Predicate<Map<String,Object>> tableFilter = (Map<String,Object> info) -> { + return Objects.equals(info.get(Reflection.TABLE_SCHEM), schemaName) + && Objects.equals(info.get(Reflection.TABLE_NAME), tableName); + }; + + final Iterator<Map<String,Object>> ite1 = cacheColumns.records.stream().filter(tableFilter).iterator(); + while (ite1.hasNext()) { + analyzeColumn(ite1.next(), cx, ftb.addAttribute(Object.class)); + } + + // Find primary key ------------------------------------------------- + final List<ColumnMetaModel> cols = new ArrayList<>(); + final Iterator<Map<String,Object>> pkIte = cachePrimaryKeys.records.stream().filter(tableFilter).iterator(); + while (pkIte.hasNext()) { + final Map<String,Object> result = pkIte.next(); + final String columnName = (String) result.get(Reflection.COLUMN_NAME); - // Explore all columns ---------------------------------------------- - final Predicate<Map<String,Object>> tableFilter = (Map<String,Object> info) -> { - return Objects.equals(info.get(Table.TABLE_SCHEM), schemaName) - && Objects.equals(info.get(Table.TABLE_NAME), tableName); + final Predicate<Map<String,Object>> colFilter = (Map<String,Object> info) -> { + return Objects.equals(info.get(Reflection.COLUMN_NAME), columnName); }; + final Iterator<Map<String,Object>> cite = cacheColumns.records.stream().filter(tableFilter.and(colFilter)).iterator(); + final Map<String,Object> column = cite.next(); - final Iterator<Map<String,Object>> ite1 = cacheColumns.records.stream().filter(tableFilter).iterator(); - while (ite1.hasNext()) { - ftb.addAttribute(analyzeColumn(ite1.next(),cx)); - } + final int sqlType = ((Number) column.get(Reflection.DATA_TYPE)).intValue(); + final String sqlTypeName = (String) column.get(Reflection.TYPE_NAME); + Class<?> columnType = dialect.getJavaType(sqlType, sqlTypeName); - // Find primary key ------------------------------------------------- - final List<ColumnMetaModel> cols = new ArrayList<>(); - final Iterator<Map<String,Object>> pkIte = cachePrimaryKeys.records.stream().filter(tableFilter).iterator(); - while (pkIte.hasNext()) { - final Map<String,Object> result = pkIte.next(); - final String columnName = (String) result.get(Column.COLUMN_NAME); - - final Predicate<Map<String,Object>> colFilter = (Map<String,Object> info) -> { - return Objects.equals(info.get(Column.COLUMN_NAME), columnName); - }; - final Iterator<Map<String,Object>> cite = cacheColumns.records.stream().filter(tableFilter.and(colFilter)).iterator(); - final Map<String,Object> column = cite.next(); - - final int sqlType = ((Number) column.get(Column.DATA_TYPE)).intValue(); - final String sqlTypeName = (String) column.get(Column.TYPE_NAME); - Class<?> columnType = dialect.getJavaType(sqlType, sqlTypeName); - - if (columnType == null) { - listeners.warning("No class for SQL type " + sqlType, null); - columnType = Object.class; - } + if (columnType == null) { + listeners.warning("No class for SQL type " + sqlType, null); + columnType = Object.class; + } - ColumnMetaModel col = null; + ColumnMetaModel col = null; - final String str = (String) column.get(Column.IS_AUTOINCREMENT); - if (Column.VALUE_YES.equalsIgnoreCase(str)) { - col = new ColumnMetaModel(schemaName, tableName, columnName, sqlType, sqlTypeName, columnType, ColumnMetaModel.Type.AUTO, null); + final String str = (String) column.get(Reflection.IS_AUTOINCREMENT); + if (VALUE_YES.equalsIgnoreCase(str)) { + col = new ColumnMetaModel(schemaName, tableName, columnName, sqlType, sqlTypeName, columnType, ColumnMetaModel.Type.AUTO, null); + } else { + // TODO: need to distinguish "NO" and empty string. + final String sequenceName = dialect.getColumnSequence(cx,schemaName, tableName, columnName); + if (sequenceName != null) { + col = new ColumnMetaModel(schemaName, tableName, columnName, sqlType, + sqlTypeName, columnType, ColumnMetaModel.Type.SEQUENCED,sequenceName); } else { - final String sequenceName = dialect.getColumnSequence(cx,schemaName, tableName, columnName); - if (sequenceName != null) { - col = new ColumnMetaModel(schemaName, tableName, columnName, sqlType, - sqlTypeName, columnType, ColumnMetaModel.Type.SEQUENCED,sequenceName); - } else { - col = new ColumnMetaModel(schemaName, tableName, columnName, sqlType, - sqlTypeName, columnType, ColumnMetaModel.Type.PROVIDED, null); - } + col = new ColumnMetaModel(schemaName, tableName, columnName, sqlType, + sqlTypeName, columnType, ColumnMetaModel.Type.PROVIDED, null); } - cols.add(col); } - /* - * Search indexes, they provide informations such as: - * - Unique indexes may indicate 1:1 relations in complexe features - * - Unique indexes can be used as primary key if no primary key are defined - */ - final boolean pkEmpty = cols.isEmpty(); - final List<String> names = new ArrayList<>(); - final Map<String,List<String>> uniqueIndexes = new HashMap<>(); - String indexname = null; - // We can't cache this one, seems to be a bug in the driver, it won't find anything for table name like '%' - cacheIndexInfos = new CachedResultSet(metadata.getIndexInfo(null, schemaName, tableName, true, false), - Index.TABLE_SCHEM, - Index.TABLE_NAME, - Index.COLUMN_NAME, - Index.INDEX_NAME); - final Iterator<Map<String,Object>> indexIte = cacheIndexInfos.records.stream().filter(tableFilter).iterator(); - while (indexIte.hasNext()) { - final Map<String,Object> result = indexIte.next(); - final String columnName = (String) result.get(Index.COLUMN_NAME); - final String idxName = (String) result.get(Index.INDEX_NAME); - - List<String> lst = uniqueIndexes.get(idxName); - if (lst == null) { - lst = new ArrayList<>(); - uniqueIndexes.put(idxName, lst); - } - lst.add(columnName); - - if (pkEmpty) { - // We use a single index columns set as primary key - // We must not mix with other potential indexes. - if (indexname == null) { - indexname = idxName; - } else if (!indexname.equals(idxName)) { - continue; - } - names.add(columnName); - } + cols.add(col); + } + /* + * Search indexes, they provide informations such as: + * - Unique indexes may indicate 1:1 relations in complexe features + * - Unique indexes can be used as primary key if no primary key are defined + */ + final boolean pkEmpty = cols.isEmpty(); + final List<String> names = new ArrayList<>(); + final Map<String,List<String>> uniqueIndexes = new HashMap<>(); + String indexname = null; + // We can't cache this one, seems to be a bug in the driver, it won't find anything for table name like '%' + cacheIndexInfos = new CachedResultSet(metadata.getIndexInfo(null, schemaName, tableName, true, false), + Reflection.TABLE_SCHEM, + Reflection.TABLE_NAME, + Reflection.COLUMN_NAME, + Reflection.INDEX_NAME); + final Iterator<Map<String,Object>> indexIte = cacheIndexInfos.records.stream().filter(tableFilter).iterator(); + while (indexIte.hasNext()) { + final Map<String,Object> result = indexIte.next(); + final String columnName = (String) result.get(Reflection.COLUMN_NAME); + final String idxName = (String) result.get(Reflection.INDEX_NAME); + + List<String> lst = uniqueIndexes.get(idxName); + if (lst == null) { + lst = new ArrayList<>(); + uniqueIndexes.put(idxName, lst); } - - // For each unique index composed of one column add a flag on the property descriptor - for (Entry<String,List<String>> entry : uniqueIndexes.entrySet()) { - final List<String> columns = entry.getValue(); - if (columns.size() == 1) { - String columnName = columns.get(0); - for (PropertyTypeBuilder desc : ftb.properties()) { - if (desc.getName().tip().toString().equals(columnName)) { - final AttributeTypeBuilder<?> atb = (AttributeTypeBuilder) desc; - atb.addCharacteristic(ColumnMetaModel.JDBC_PROPERTY_UNIQUE).setDefaultValue(Boolean.TRUE); - } - } + lst.add(columnName); + + if (pkEmpty) { + // We use a single index columns set as primary key + // We must not mix with other potential indexes. + if (indexname == null) { + indexname = idxName; + } else if (!indexname.equals(idxName)) { + continue; } + names.add(columnName); } + } - if (pkEmpty && !names.isEmpty()) { - // Build a primary key from unique index - final Iterator<Map<String,Object>> ite = cacheColumns.records.stream().filter(tableFilter).iterator(); - while (ite.hasNext()) { - final Map<String,Object> result = ite.next(); - final String columnName = (String) result.get(Column.COLUMN_NAME); - if (!names.contains(columnName)) { - continue; - } - - final int sqlType = ((Number) result.get(Column.DATA_TYPE)).intValue(); - final String sqlTypeName = (String) result.get(Column.TYPE_NAME); - final Class<?> columnType = dialect.getJavaType(sqlType, sqlTypeName); - final ColumnMetaModel col = new ColumnMetaModel(schemaName, tableName, columnName, - sqlType, sqlTypeName, columnType, ColumnMetaModel.Type.PROVIDED, null); - cols.add(col); - - // Set as identifier - for (PropertyTypeBuilder desc : ftb.properties()) { - if (desc.getName().tip().toString().equals(columnName)) { - final AttributeTypeBuilder<?> atb = (AttributeTypeBuilder) desc; - atb.addRole(AttributeRole.IDENTIFIER_COMPONENT); - break; - } + // For each unique index composed of one column add a flag on the property descriptor + for (Entry<String,List<String>> entry : uniqueIndexes.entrySet()) { + final List<String> columns = entry.getValue(); + if (columns.size() == 1) { + String columnName = columns.get(0); + for (PropertyTypeBuilder desc : ftb.properties()) { + if (desc.getName().tip().toString().equals(columnName)) { + final AttributeTypeBuilder<?> atb = (AttributeTypeBuilder) desc; + atb.addCharacteristic(ColumnMetaModel.JDBC_PROPERTY_UNIQUE).setDefaultValue(Boolean.TRUE); } } } + } - - if (cols.isEmpty()) { - if (Table.VALUE_TYPE_TABLE.equals(tableType)) { - listeners.warning("No primary key found for " + tableName, null); + if (pkEmpty && !names.isEmpty()) { + // Build a primary key from unique index + final Iterator<Map<String,Object>> ite = cacheColumns.records.stream().filter(tableFilter).iterator(); + while (ite.hasNext()) { + final Map<String,Object> result = ite.next(); + final String columnName = (String) result.get(Reflection.COLUMN_NAME); + if (!names.contains(columnName)) { + continue; } - } - table.key = new PrimaryKey(tableName, cols); - // Mark primary key columns - for (PropertyTypeBuilder desc : ftb.properties()) { - for (ColumnMetaModel col : cols) { - if (desc.getName().tip().toString().equals(col.name)) { + final int sqlType = ((Number) result.get(Reflection.DATA_TYPE)).intValue(); + final String sqlTypeName = (String) result.get(Reflection.TYPE_NAME); + final Class<?> columnType = dialect.getJavaType(sqlType, sqlTypeName); + final ColumnMetaModel col = new ColumnMetaModel(schemaName, tableName, columnName, + sqlType, sqlTypeName, columnType, ColumnMetaModel.Type.PROVIDED, null); + cols.add(col); + + // Set as identifier + for (PropertyTypeBuilder desc : ftb.properties()) { + if (desc.getName().tip().toString().equals(columnName)) { final AttributeTypeBuilder<?> atb = (AttributeTypeBuilder) desc; atb.addRole(AttributeRole.IDENTIFIER_COMPONENT); break; } } } + } - // Find imported keys ----------------------------------------------- - final Predicate<Map<String,Object>> fkFilter = (Map<String,Object> info) -> { - return Objects.equals(info.get(ImportedKey.FKTABLE_SCHEM), schemaName) - && Objects.equals(info.get(ImportedKey.FKTABLE_NAME), tableName); - }; - Iterator<Map<String,Object>> ite = cacheImportedKeys.records.stream().filter(fkFilter).iterator(); - while (ite.hasNext()) { - final Map<String,Object> result = ite.next(); - String relationName = (String) result.get(ImportedKey.PK_NAME); - if (relationName == null) relationName = (String) result.get(ImportedKey.FK_NAME); - final String localColumn = (String) result.get(ImportedKey.FKCOLUMN_NAME); - final String refSchemaName = (String) result.get(ImportedKey.PKTABLE_SCHEM); - final String refTableName = (String) result.get(ImportedKey.PKTABLE_NAME); - final String refColumnName = (String) result.get(ImportedKey.PKCOLUMN_NAME); - final int deleteRule = ((Number) result.get(ImportedKey.DELETE_RULE)).intValue(); - final boolean deleteCascade = DatabaseMetaData.importedKeyCascade == deleteRule; - final RelationMetaModel relation = new RelationMetaModel(relationName,localColumn, - refSchemaName, refTableName, refColumnName, true, deleteCascade); - table.importedKeys.add(relation); - - if (refSchemaName!=null && !visitedSchemas.contains(refSchemaName)) requieredSchemas.add(refSchemaName); - - // Set the information - for (PropertyTypeBuilder desc : ftb.properties()) { - if (desc.getName().tip().toString().equals(localColumn)) { - final AttributeTypeBuilder<?> atb = (AttributeTypeBuilder) desc; - atb.addCharacteristic(ColumnMetaModel.JDBC_PROPERTY_RELATION).setDefaultValue(relation); - break; - } + if (cols.isEmpty()) { + if (TYPE_TABLE.equals(tableType)) { + listeners.warning("No primary key found for " + tableName, null); + } + } + table.key = new PrimaryKey(tableName, cols); + + // Mark primary key columns + for (PropertyTypeBuilder desc : ftb.properties()) { + for (ColumnMetaModel col : cols) { + if (desc.getName().tip().toString().equals(col.name)) { + final AttributeTypeBuilder<?> atb = (AttributeTypeBuilder) desc; + atb.addRole(AttributeRole.IDENTIFIER_COMPONENT); + break; } } + } - // Find exported keys ----------------------------------------------- - final Predicate<Map<String,Object>> ekFilter = (Map<String,Object> info) -> { - return Objects.equals(info.get(ImportedKey.PKTABLE_SCHEM), schemaName) - && Objects.equals(info.get(ImportedKey.PKTABLE_NAME), tableName); - }; - ite = cacheExportedKeys.records.stream().filter(ekFilter).iterator(); - while (ite.hasNext()) { - final Map<String,Object> result = ite.next(); - String relationName = (String) result.get(ExportedKey.FKCOLUMN_NAME); - if (relationName == null) relationName = (String) result.get(ExportedKey.FK_NAME); - final String localColumn = (String) result.get(ExportedKey.PKCOLUMN_NAME); - final String refSchemaName = (String) result.get(ExportedKey.FKTABLE_SCHEM); - final String refTableName = (String) result.get(ExportedKey.FKTABLE_NAME); - final String refColumnName = (String) result.get(ExportedKey.FKCOLUMN_NAME); - final int deleteRule = ((Number) result.get(ImportedKey.DELETE_RULE)).intValue(); - final boolean deleteCascade = DatabaseMetaData.importedKeyCascade == deleteRule; - table.exportedKeys.add(new RelationMetaModel(relationName, localColumn, - refSchemaName, refTableName, refColumnName, false, deleteCascade)); - - if (refSchemaName != null && !visitedSchemas.contains(refSchemaName)) requieredSchemas.add(refSchemaName); + + // Find imported keys ----------------------------------------------- + final Predicate<Map<String,Object>> fkFilter = (Map<String,Object> info) -> { + return Objects.equals(info.get(Reflection.FKTABLE_SCHEM), schemaName) + && Objects.equals(info.get(Reflection.FKTABLE_NAME), tableName); + }; + Iterator<Map<String,Object>> ite = cacheImportedKeys.records.stream().filter(fkFilter).iterator(); + while (ite.hasNext()) { + final Map<String,Object> result = ite.next(); + String relationName = (String) result.get(Reflection.PK_NAME); + if (relationName == null) relationName = (String) result.get(Reflection.FK_NAME); + final String localColumn = (String) result.get(Reflection.FKCOLUMN_NAME); + final String refSchemaName = (String) result.get(Reflection.PKTABLE_SCHEM); + final String refTableName = (String) result.get(Reflection.PKTABLE_NAME); + final String refColumnName = (String) result.get(Reflection.PKCOLUMN_NAME); + final int deleteRule = ((Number) result.get(Reflection.DELETE_RULE)).intValue(); + final boolean deleteCascade = DatabaseMetaData.importedKeyCascade == deleteRule; + final RelationMetaModel relation = new RelationMetaModel(relationName,localColumn, + refSchemaName, refTableName, refColumnName, true, deleteCascade); + table.importedKeys.add(relation); + + if (refSchemaName!=null && !visitedSchemas.contains(refSchemaName)) requieredSchemas.add(refSchemaName); + + // Set the information + for (PropertyTypeBuilder desc : ftb.properties()) { + if (desc.getName().tip().toString().equals(localColumn)) { + final AttributeTypeBuilder<?> atb = (AttributeTypeBuilder) desc; + atb.addCharacteristic(ColumnMetaModel.JDBC_PROPERTY_RELATION).setDefaultValue(relation); + break; + } } + } - // Find parent table if any ----------------------------------------- -// if(handleSuperTableMetadata == null || handleSuperTableMetadata){ -// try{ -// result = metadata.getSuperTables(null, schemaName, tableName); -// while (result.next()) { -// final String parentTable = result.getString(SuperTable.SUPERTABLE_NAME); -// table.parents.add(parentTable); -// } -// }catch(final SQLException ex){ -// //not implemented by database -// handleSuperTableMetadata = Boolean.FALSE; -// store.getLogger().log(Level.INFO, "Database does not handle getSuperTable, feature type hierarchy will be ignored."); -// }finally{ -// closeSafe(store.getLogger(),result); + // Find exported keys ----------------------------------------------- + final Predicate<Map<String,Object>> ekFilter = (Map<String,Object> info) -> { + return Objects.equals(info.get(Reflection.PKTABLE_SCHEM), schemaName) + && Objects.equals(info.get(Reflection.PKTABLE_NAME), tableName); + }; + ite = cacheExportedKeys.records.stream().filter(ekFilter).iterator(); + while (ite.hasNext()) { + final Map<String,Object> result = ite.next(); + String relationName = (String) result.get(Reflection.FKCOLUMN_NAME); + if (relationName == null) relationName = (String) result.get(Reflection.FK_NAME); + final String localColumn = (String) result.get(Reflection.PKCOLUMN_NAME); + final String refSchemaName = (String) result.get(Reflection.FKTABLE_SCHEM); + final String refTableName = (String) result.get(Reflection.FKTABLE_NAME); + final String refColumnName = (String) result.get(Reflection.FKCOLUMN_NAME); + final int deleteRule = ((Number) result.get(Reflection.DELETE_RULE)).intValue(); + final boolean deleteCascade = DatabaseMetaData.importedKeyCascade == deleteRule; + table.exportedKeys.add(new RelationMetaModel(relationName, localColumn, + refSchemaName, refTableName, refColumnName, false, deleteCascade)); + + if (refSchemaName != null && !visitedSchemas.contains(refSchemaName)) requieredSchemas.add(refSchemaName); + } + + // Find parent table if any ----------------------------------------- +// if (handleSuperTableMetadata == null || handleSuperTableMetadata) { +// try { +// result = metadata.getSuperTables(null, schemaName, tableName); +// while (result.next()) { +// final String parentTable = result.getString(SuperTable.SUPERTABLE_NAME); +// table.parents.add(parentTable); // } +// } catch (SQLException ex) { +// //not implemented by database +// handleSuperTableMetadata = Boolean.FALSE; +// store.getLogger().log(Level.INFO, "Database does not handle getSuperTable, feature type hierarchy will be ignored."); +// } finally { +// closeSafe(store.getLogger(),result); // } +// } - } catch (SQLException e) { - throw new DataStoreException("Error occurred analyzing table: " + tableName, e); - } ftb.setName(tableName); table.tableType = ftb; return table; } - private AttributeType<?> analyzeColumn(final Map<String,Object> columnSet, final Connection cx) throws SQLException, DataStoreException{ - final String schemaName = (String) columnSet.get(Column.TABLE_SCHEM); - final String tableName = (String) columnSet.get(Column.TABLE_NAME); - final String columnName = (String) columnSet.get(Column.COLUMN_NAME); - final int columnSize = ((Number) columnSet.get(Column.COLUMN_SIZE)).intValue(); - final int columnDataType = ((Number) columnSet.get(Column.DATA_TYPE)).intValue(); - final String columnTypeName = (String) columnSet.get(Column.TYPE_NAME); - final String columnNullable = (String) columnSet.get(Column.IS_NULLABLE); - final SingleAttributeTypeBuilder atb = new SingleAttributeTypeBuilder(); + private AttributeType<?> analyzeColumn(final Map<String,Object> columnSet, final Connection cx, final AttributeTypeBuilder<?> atb) + throws SQLException + { + final String schemaName = (String) columnSet.get(Reflection.TABLE_SCHEM); + final String tableName = (String) columnSet.get(Reflection.TABLE_NAME); + final String columnName = (String) columnSet.get(Reflection.COLUMN_NAME); + final int columnSize = ((Number) columnSet.get(Reflection.COLUMN_SIZE)).intValue(); + final int columnDataType = ((Number) columnSet.get(Reflection.DATA_TYPE)).intValue(); + final String columnTypeName = (String) columnSet.get(Reflection.TYPE_NAME); + final String columnNullable = (String) columnSet.get(Reflection.IS_NULLABLE); atb.setName(columnName); - atb.setLength(columnSize); - try { - dialect.decodeColumnType(atb, cx, columnTypeName, columnDataType, schemaName, tableName, columnName); - } catch (SQLException e) { - throw new DataStoreException("Error occurred analyzing column: " + columnName, e); - } - atb.setMinimumOccurs(Column.VALUE_NO.equalsIgnoreCase(columnNullable) ? 1 : 0); + atb.setMaximalLength(columnSize); + dialect.decodeColumnType(atb, cx, columnTypeName, columnDataType, schemaName, tableName, columnName); + // TODO: need to distinguish "YES" and empty string? + atb.setMinimumOccurs(VALUE_NO.equalsIgnoreCase(columnNullable) ? 1 : 0); atb.setMaximumOccurs(1); return atb.build(); } @@ -623,7 +574,7 @@ public final class DataBaseModel { /** * Analyze the metadata of the ResultSet to rebuild a feature type. */ - public FeatureType analyzeResult(final ResultSet result, final String name) throws SQLException, DataStoreException{ + final FeatureType analyzeResult(final ResultSet result, final String name) throws SQLException, DataStoreException { final FeatureTypeBuilder ftb = new FeatureTypeBuilder(); ftb.setName(name); @@ -652,15 +603,16 @@ public final class DataBaseModel { } } } - - if (desc == null) { - //could not find the original type - //this column must be calculated - final SingleAttributeTypeBuilder atb = new SingleAttributeTypeBuilder(); + if (desc != null) { + ftb.addProperty(desc); + } else { + // could not find the original type + // this column must be calculated + final AttributeTypeBuilder<?> atb = ftb.addAttribute(Object.class); final int nullable = metadata.isNullable(i); atb.setName(columnLabel); - atb.setMinimumOccurs(nullable == metadata.columnNullable ? 0 : 1); + atb.setMinimumOccurs(nullable == ResultSetMetaData.columnNullable ? 0 : 1); atb.setMaximumOccurs(1); atb.setName(columnLabel); @@ -676,9 +628,7 @@ public final class DataBaseModel { } catch (SQLException e) { throw new DataStoreException("Error occurred analyzing column : " + columnName, e); } - desc = atb.build(); } - ftb.addProperty(desc); } return ftb.build(); } @@ -709,9 +659,9 @@ public final class DataBaseModel { if (Geometries.isKnownType(binding)) { final Predicate<Map<?,?>> colFilter = (Map<?,?> info) -> { - return Objects.equals(info.get(Table.TABLE_SCHEM), schema.name) - && Objects.equals(info.get(Table.TABLE_NAME), tableName) - && Objects.equals(info.get(Column.COLUMN_NAME), name); + return Objects.equals(info.get(Reflection.TABLE_SCHEM), schema.name) + && Objects.equals(info.get(Reflection.TABLE_NAME), tableName) + && Objects.equals(info.get(Reflection.COLUMN_NAME), name); }; final Map<String,Object> metas = cacheColumns.records.stream().filter(colFilter).findFirst().get(); @@ -730,9 +680,9 @@ public final class DataBaseModel { } } else if (Coverage.class.isAssignableFrom(binding)) { final Predicate<Map<String,Object>> colFilter = (Map<String,Object> info) -> { - return Objects.equals(info.get(Table.TABLE_SCHEM), schema.name) - && Objects.equals(info.get(Table.TABLE_NAME), tableName) - && Objects.equals(info.get(Column.COLUMN_NAME), name); + return Objects.equals(info.get(Reflection.TABLE_SCHEM), schema.name) + && Objects.equals(info.get(Reflection.TABLE_NAME), tableName) + && Objects.equals(info.get(Reflection.COLUMN_NAME), name); }; final Map<String,Object> metas = cacheColumns.records.stream().filter(colFilter).findFirst().get(); diff --git a/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/Dialect.java b/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/Dialect.java index 17f9547..ad9930f 100644 --- a/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/Dialect.java +++ b/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/Dialect.java @@ -21,6 +21,7 @@ import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.apache.sis.feature.builder.AttributeTypeBuilder; import org.apache.sis.storage.DataStoreException; @@ -42,16 +43,6 @@ public abstract class Dialect { } /** - * Indicates if the JDBC driver support global metadata. - * Some drivers force specifying a schema or table to return results. - * This prevent us from loading all metadata in one request and - * makes us loop on all tables. - * - * @return whether global JDBC metadata are available. - */ - public abstract boolean supportGlobalMetadata(); - - /** * Indicates whether a table will be used as a {@code FeatureType}. * * @param name database table name. @@ -71,23 +62,6 @@ public abstract class Dialect { public abstract Class<?> getJavaType(int sqlType, String sqlTypeName); /** - * Encodes the column name part of a SQL query. - * - * @param sql where to write the SQL statement. - * @param name column name to write, not null. - */ - public abstract void encodeColumnName(StringBuilder sql, String name); - - /** - * Encodes the schema and table name parts of a SQL query. - * - * @param sql where to write the SQL statement. - * @param schema database schema to write, or null if none. - * @param table database table to write, not null. - */ - public abstract void encodeSchemaAndTableName(StringBuilder sql, String schema, String table); - - /** * If a column is an auto-increment or has a sequence, tries to extract next value. * * @param column description of the database column for which to get the next value. @@ -122,7 +96,7 @@ public abstract class Dialect { * @param column name of the database column. * @throws SQLException if a JDBC error occurred while executing a statement. */ - public abstract void decodeColumnType(final SingleAttributeTypeBuilder atb, final Connection cx, + public abstract void decodeColumnType(final AttributeTypeBuilder<?> atb, final Connection cx, final String typeName, final int datatype, final String schema, final String table, final String column) throws SQLException; @@ -136,7 +110,7 @@ public abstract class Dialect { * @param customquery {@code true} if the request is a custom query. * @throws SQLException if a JDBC error occurred while executing a statement. */ - public abstract void decodeGeometryColumnType(final SingleAttributeTypeBuilder atb, final Connection cx, + public abstract void decodeGeometryColumnType(final AttributeTypeBuilder<?> atb, final Connection cx, final ResultSet rs, final int columnIndex, boolean customquery) throws SQLException; /** diff --git a/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/MetaDataConstants.java b/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/MetaDataConstants.java deleted file mode 100644 index 393785b..0000000 --- a/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/MetaDataConstants.java +++ /dev/null @@ -1,515 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ -package org.apache.sis.internal.sql.feature; - - -/** - * Constants defined by JDBC to retrieve a database meta-model. - * - * @author Johann Sorel (Geomatys) - * @version 1.0 - * @since 1.0 - * @module - */ -final class MetaDataConstants { - - static final class Schema { - /** schema name. */ - public static final String TABLE_SCHEM = "TABLE_SCHEM"; - - /** catalog name (values in this column may be null). */ - public static final String TABLE_CATALOG = "TABLE_CATALOG"; - - private Schema() { - } - } - - static final class Table { - /** Table catalog (values in this column may be null). */ - public static final String TABLE_CAT = "TABLE_CAT"; - - /** Table schema (values in this column may be null). */ - - public static final String TABLE_SCHEM = "TABLE_SCHEM"; - - /** Table name. */ - public static final String TABLE_NAME = "TABLE_NAME"; - - /** - * Table type. Typical types are: - * "TABLE", "VIEW", "SYSTEM TABLE", - * "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS", "SYNONYM". - */ - public static final String TABLE_TYPE = "TABLE_TYPE"; - - /** Explanatory comment on the table. */ - public static final String REMARKS = "REMARKS"; - - /** The types catalog (values in this column may be null). */ - public static final String TYPE_CAT = "TYPE_CAT"; - - /** The types schema (values in this column may be null). */ - public static final String TYPE_SCHEM = "TYPE_SCHEM"; - - /** Type name (values in this column may be null). */ - public static final String TYPE_NAME = "TYPE_NAME"; - - /** Name of the designated "identifier" column of a typed table (values in this column may be null). */ - public static final String SELF_REFERENCING_COL_NAME = "SELF_REFERENCING_COL_NAME"; - - /** - * Specifies how values in SELF_REFERENCING_COL_NAME are created. - * Values are "SYSTEM", "USER", "DERIVED". values in this column may be null. - */ - public static final String REF_GENERATION = "REF_GENERATION"; - - public static final String VALUE_TYPE_TABLE = "TABLE"; - public static final String VALUE_TYPE_VIEW = "VIEW"; - public static final String VALUE_TYPE_SYSTEMTABLE = "SYSTEM TABLE"; - public static final String VALUE_TYPE_GLOBALTEMPORARY = "GLOBAL TEMPORARY"; - public static final String VALUE_TYPE_LOCALTEMPORARY = "LOCAL TEMPORARY"; - public static final String VALUE_TYPE_ALIAS = "ALIAS"; - public static final String VALUE_TYPE_SYNONYM = "SYNONYM"; - - public static final String VALUE_REFGEN_SYSTEM = "SYSTEM"; - public static final String VALUE_REFGEN_USER = "USER"; - public static final String VALUE_REFGEN_DERIVED = "DERIVED"; - - private Table() { - } - } - - public static final class SuperTable { - /** The type's catalog (values in this column may be null). */ - public static final String TABLE_CAT = "TABLE_CAT"; - - /** Type's schema (values in this column may be null). */ - public static final String TABLE_SCHEM = "TABLE_SCHEM"; - - /** Type name. */ - public static final String TABLE_NAME = "TABLE_NAME"; - - /** The direct super type's name. */ - public static final String SUPERTABLE_NAME = "SUPERTABLE_NAME"; - - private SuperTable() { - } - } - - public static final class Column { - /** Table catalog (values in this column may be null). */ - public static final String TABLE_CAT = "TABLE_CAT"; - - /** Table schema (values in this column may be null). */ - public static final String TABLE_SCHEM = "TABLE_SCHEM"; - - /** Table name. */ - public static final String TABLE_NAME = "TABLE_NAME"; - - /** Column name. */ - public static final String COLUMN_NAME = "COLUMN_NAME"; - - /** SQL type from {@link java.sql.Types}. */ - public static final String DATA_TYPE = "DATA_TYPE"; - - /** Data source dependent type name, for a UDT the type name is fully qualified. */ - public static final String TYPE_NAME = "TYPE_NAME"; - - /** Column size ({@code int} values). */ - public static final String COLUMN_SIZE = "COLUMN_SIZE"; - - /** Not used. */ - public static final String BUFFER_LENGTH = "BUFFER_LENGTH"; - - /** - * The number of fractional digits as {@code int} values. - * Null is returned for data types where DECIMAL_DIGITS is not applicable. - */ - public static final String DECIMAL_DIGITS = "DECIMAL_DIGITS"; - - /** Radix (typically either 10 or 2) as {@code int} values. */ - public static final String NUM_PREC_RADIX = "NUM_PREC_RADIX"; - - /** - * Whether NULL allowed as a {@code int} code. - * <ul> - * <li>{@code columnNoNulls} - might not allow NULL values</li> - * <li>{@code columnNullable} - definitely allows NULL values</li> - * <li>{@code columnNullableUnknown} - nullability unknown</li> - * </ul> - */ - public static final String NULLABLE = "NULLABLE"; - - /** Comment describing column (values in this column may be null). */ - public static final String REMARKS = "REMARKS"; - - /** - * Default value for the column, which should be interpreted as a - * string when the value is enclosed in single quotes (values in this column may be null) - */ - public static final String COLUMN_DEF = "COLUMN_DEF"; - - /** Unused {@code int} values. */ - public static final String SQL_DATA_TYPE = "SQL_DATA_TYPE"; - - /** Unused {@code int} values. */ - public static final String SQL_DATETIME_SUB = "SQL_DATETIME_SUB"; - - /** Maximum number (as a {@code int}) of bytes in the column of type {@code char}. */ - public static final String CHAR_OCTET_LENGTH = "CHAR_OCTET_LENGTH"; - - /** Index of column in table (starting at 1). */ - public static final String ORDINAL_POSITION = "ORDINAL_POSITION"; - - /** - * ISO rules are used to determine the nullability for a column. - * YES if the parameter can include NULLs, - * NO if the parameter cannot include NULLs, - * empty string if the nullability for the parameter is unknown. - */ - public static final String IS_NULLABLE = "IS_NULLABLE"; - - /** - * Catalog of table that is the scope of a reference attribute - * (null if DATA_TYPE isn't REF). - */ - public static final String SCOPE_CATLOG = "SCOPE_CATLOG"; - - /** - * Schema of table that is the scope of a reference attribute - * (null if the DATA_TYPE isn't REF). - */ - public static final String SCOPE_SCHEMA = "SCOPE_SCHEMA"; - - /** - * Table name that this the scope of a reference attribute - * (null if the DATA_TYPE isn't REF). - */ - public static final String SCOPE_TABLE = "SCOPE_TABLE"; - - /** - * Source type of a distinct type or user-generated Ref type. - * This is a SQL type from {@link java.sql.Types} - * (null if DATA_TYPE isn't DISTINCT or user-generated REF). - */ - public static final String SOURCE_DATA_TYPE = "SOURCE_DATA_TYPE"; - - /** - * Indicates whether this column is auto incremented. - * YES if the column is auto incremented, - * NO if the column is not auto incremented, - * empty string if whether the column is auto incremented is unknown. - */ - public static final String IS_AUTOINCREMENT = "IS_AUTOINCREMENT"; - - public static final String VALUE_YES = "YES"; - public static final String VALUE_NO = "NO"; - - private Column() { - } - } - - public static final class PrimaryKey{ - /** Table catalog (values in this column may be null). */ - public static final String TABLE_CAT = "TABLE_CAT"; - - /** Table schema (values in this column may be null). */ - public static final String TABLE_SCHEM = "TABLE_SCHEM"; - - /** Table name. */ - public static final String TABLE_NAME = "TABLE_NAME"; - - /** Column name. */ - public static final String COLUMN_NAME = "COLUMN_NAME"; - - /** - * Sequence number within primary key as a {@code short} code. - * A value of 1 represents the first column of the primary key; - * a value of 2 would represent the second column within the primary key. - */ - public static final String KEY_SEQ = "KEY_SEQ"; - - /** Primary key name (values in this column may be null). */ - public static final String PK_NAME = "PK_NAME"; - - private PrimaryKey() { - } - } - - public static final class ImportedKey{ - /** Primary key table catalog being imported (values in this column may be null). */ - public static final String PKTABLE_CAT = "PKTABLE_CAT"; - - /** Primary key table schema being imported (values in this column may be null). */ - public static final String PKTABLE_SCHEM = "PKTABLE_SCHEM"; - - /** Primary key table name being imported. */ - public static final String PKTABLE_NAME = "PKTABLE_NAME"; - - /** Primary key column name being imported. */ - public static final String PKCOLUMN_NAME = "PKCOLUMN_NAME"; - - /** Foreign key table catalog (may be null). */ - public static final String FKTABLE_CAT = "FKTABLE_CAT"; - - /** Foreign key table schema (may be null). */ - public static final String FKTABLE_SCHEM = "FKTABLE_SCHEM"; - - /** Foreign key table name. */ - public static final String FKTABLE_NAME = "FKTABLE_NAME"; - - /** Foreign key column name. */ - public static final String FKCOLUMN_NAME = "FKCOLUMN_NAME"; - - /** - * Sequence number within a foreign key as a {@code short} code. - * A value of 1 represents the first column of the foreign key; - * a value of 2 would represent the second column within the foreign key. - */ - public static final String KEY_SEQ = "KEY_SEQ"; - - /** - * What happens to a foreign key when the primary key is updated, as a {@code short} code. - * <ul> - * <li>{@code importedNoAction} - do not allow update of primary key if it has been imported</li> - * <li>{@code importedKeyCascade} - change imported key to agree with primary key update</li> - * <li>{@code importedKeySetNull} - change imported key to NULL if its primary key has been updated</li> - * <li>{@code importedKeySetDefault} - change imported key to default values if its primary key has been updated</li> - * <li>{@code importedKeyRestrict} - same as importedKeyNoAction (for ODBC 2.x compatibility).</li> - * </ul> - */ - public static final String UPDATE_RULE = "UPDATE_RULE"; - - /** - * What happens to the foreign key when primary is deleted, as a {@code short} code. - * <ul> - * <li>{@code importedKeyNoAction} - do not allow delete of primary key if it has been imported</li> - * <li>{@code importedKeyCascade} - delete rows that import a deleted key</li> - * <li>{@code importedKeySetNull} - change imported key to NULL if its primary key has been deleted</li> - * <li>{@code importedKeyRestrict} - same as importedKeyNoAction (for ODBC 2.x compatibility)</li> - * <li>{@code importedKeySetDefault} - change imported key to default if its primary key has been deleted.</li> - * </ul> - */ - public static final String DELETE_RULE = "DELETE_RULE"; - - /** Foreign key name (values in this column may be null). */ - public static final String FK_NAME = "FK_NAME"; - - /** Primary key name (values in this column may be null). */ - public static final String PK_NAME = "PK_NAME"; - - /** - * Whether the evaluation of foreign key constraints can be deferred until commit (as a {@code short} code). - * <ul> - * <li>{@code importedKeyInitiallyDeferred} - see SQL92 for definition</li> - * <li>{@code importedKeyInitiallyImmediate} - see SQL92 for definition</li> - * <li>{@code importedKeyNotDeferrable} - see SQL92 for definition</li> - * </ul> - */ - public static final String DEFERRABILITY = "DEFERRABILITY"; - - private ImportedKey() { - } - } - - public static final class ExportedKey{ - /** Primary key table catalog (values in this column may be null). */ - public static final String PKTABLE_CAT = "PKTABLE_CAT"; - - /** Primary key table schema (values in this column may be null). */ - public static final String PKTABLE_SCHEM = "PKTABLE_SCHEM"; - - /** Primary key table name. */ - public static final String PKTABLE_NAME = "PKTABLE_NAME"; - - /** Primary key column name. */ - public static final String PKCOLUMN_NAME = "PKCOLUMN_NAME"; - - /** Foreign key table catalog (may be null) being exported (values in this column may be null). */ - public static final String FKTABLE_CAT = "FKTABLE_CAT"; - - /** Foreign key table schema (may be null) being exported (values in this column may be null). */ - public static final String FKTABLE_SCHEM = "FKTABLE_SCHEM"; - - /** Foreign key table name being exported. */ - public static final String FKTABLE_NAME = "FKTABLE_NAME"; - - /** Foreign key column name being exported. */ - public static final String FKCOLUMN_NAME = "FKCOLUMN_NAME"; - - /** - * Sequence number within foreign key as a {@code short} code. - * a value of 1 represents the first column of the foreign key; - * a value of 2 would represent the second column within the foreign key. - */ - public static final String KEY_SEQ = "KEY_SEQ"; - - /** - * What happens to foreign key when primary is updated, as a {@code short} code. - * <ul> - * <li>{@code importedNoAction} - do not allow update of primary key if it has been imported</li> - * <li>{@code importedKeyCascade} - change imported key to agree with primary key update</li> - * <li>{@code importedKeySetNull} - change imported key to NULL if its primary key has been updated</li> - * <li>{@code importedKeySetDefault} - change imported key to default values if its primary key has been updated</li> - * <li>{@code importedKeyRestrict} - same as importedKeyNoAction (for ODBC 2.x compatibility)</li> - * </ul> - */ - public static final String UPDATE_RULE = "UPDATE_RULE"; - - /** - * What happens to the foreign key when primary is deleted, as a {@code short} code. - * <ul> - * <li>{@code importedKeyNoAction} - do not allow delete of primary key if it has been imported</li> - * <li>{@code importedKeyCascade} - delete rows that import a deleted key</li> - * <li>{@code importedKeySetNull} - change imported key to NULL if its primary key has been deleted</li> - * <li>{@code importedKeyRestrict} - same as importedKeyNoAction (for ODBC 2.x compatibility)</li> - * <li>{@code importedKeySetDefault} - change imported key to default if its primary key has been deleted</li> - * </ul> - */ - public static final String DELETE_RULE = "DELETE_RULE"; - - /** Foreign key name (values in this column may be null). */ - public static final String FK_NAME = "FK_NAME"; - - /** Primary key name (values in this column may be null). */ - public static final String PK_NAME = "PK_NAME"; - - /** - * Whether the evaluation of foreign key constraints can be deferred until commit, as a {@code short} code. - * <ul> - * <li>{@code importedKeyInitiallyDeferred} - see SQL92 for definition</li> - * <li>{@code importedKeyInitiallyImmediate} - see SQL92 for definition</li> - * <li>{@code importedKeyNotDeferrable} - see SQL92 for definition</li> - * </ul> - */ - public static final String DEFERRABILITY = "DEFERRABILITY"; - - private ExportedKey() { - } - } - - public static final class BestRow{ - /** - * Actual scope of result as a {@code short} code. - * bestRowTemporary - very temporary, while using row - * bestRowTransaction - valid for remainder of current transaction - * bestRowSession - valid for remainder of current session. - */ - public static final String SCOPE = "SCOPE"; - - /** Column name. */ - public static final String COLUMN_NAME = "COLUMN_NAME"; - - /** SQL data type from {@link java.sql.Types}. */ - public static final String DATA_TYPE = "DATA_TYPE"; - - /** Data source dependent type name. For a UDT the type name is fully qualified. */ - public static final String TYPE_NAME = "TYPE_NAME"; - - /** Precision as an {@code int}. */ - public static final String COLUMN_SIZE = "COLUMN_SIZE"; - - /** Not used. */ - public static final String BUFFER_LENGTH = "BUFFER_LENGTH"; - - /** Scale - Null is returned for data types where DECIMAL_DIGITS is not applicable. */ - public static final String DECIMAL_DIGITS = "DECIMAL_DIGITS"; - - /** - * Whether this a pseudo column like an Oracle ROWID, as a {@code short} code. - * bestRowUnknown - may or may not be pseudo column - * bestRowNotPseudo - is NOT a pseudo column - * bestRowPseudo - is a pseudo column. - */ - public static final String PSEUDO_COLUMN = "PSEUDO_COLUMN"; - - private BestRow() { - } - } - - public static final class Index { - /** Table catalog (values in this column may be null). */ - public static final String TABLE_CAT = "TABLE_CAT"; - - /** Table schema (values in this column may be null). */ - public static final String TABLE_SCHEM = "TABLE_SCHEM"; - - /** Table name. */ - public static final String TABLE_NAME = "TABLE_NAME"; - - /** Whether index values can be non-unique. - * false when TYPE is tableIndexStatistic */ - public static final String NON_UNIQUE = "NON_UNIQUE"; - - /** - * Index catalog (values in this column may be null). - * Values are null when TYPE is tableIndexStatistic. - */ - public static final String INDEX_QUALIFIER = "INDEX_QUALIFIER"; - - /** Index name; null when TYPE is tableIndexStatistic. */ - public static final String INDEX_NAME = "INDEX_NAME"; - - /** - * Index type as a {@code short} code. - * tableIndexStatistic - this identifies table statistics that are - * returned in conjuction with a table's index descriptions - * tableIndexClustered - this is a clustered index - * tableIndexHashed - this is a hashed index - * tableIndexOther - this is some other style of index. - */ - public static final String TYPE = "TYPE"; - - /** - * Column sequence number within index as a {@code short}. - * Zero when TYPE is tableIndexStatistic. - */ - public static final String ORDINAL_POSITION = "ORDINAL_POSITION"; - - /** Column name. Values are null when TYPE is tableIndexStatistic. */ - public static final String COLUMN_NAME = "COLUMN_NAME"; - - /** - * Column sort sequence. - * "A" for ascending, - * "D" for descending, may be null if sort sequence is not supported; - * null when TYPE is tableIndexStatistic. - */ - public static final String ASC_OR_DESC = "ASC_OR_DESC"; - - /** - * When TYPE is tableIndexStatistic, then this is the number of rows in the table as an {@code int}. - * Otherwise, it is the number of unique values in the index. - */ - public static final String CARDINALITY = "CARDINALITY"; - - /** - * When TYPE is tableIndexStatisic then this is the number of pages used for the table as an {@code int}. - * Otherwise it is the number of pages used for the current index. - */ - public static final String PAGES = "PAGES"; - - /** Filter condition, if any. Values in this column may be null. */ - public static final String FILTER_CONDITION = "FILTER_CONDITION"; - - private Index() { - } - } - - private MetaDataConstants() { - } -} diff --git a/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/SingleAttributeTypeBuilder.java b/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/SingleAttributeTypeBuilder.java deleted file mode 100644 index ad22d65..0000000 --- a/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/feature/SingleAttributeTypeBuilder.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ -package org.apache.sis.internal.sql.feature; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.apache.sis.feature.DefaultAttributeType; -import org.opengis.feature.AttributeType; -import org.opengis.referencing.crs.CoordinateReferenceSystem; -import org.opengis.util.GenericName; -import org.apache.sis.internal.feature.AttributeConvention; -import org.apache.sis.internal.system.DefaultFactories; -import org.opengis.util.NameFactory; - -import static org.apache.sis.feature.AbstractIdentifiedType.*; - - -/** - * Builder for a single {@link AttributeType}. - * This is an alternative to {@link org.apache.sis.feature.builder.PropertyTypeBuilder}. - * - * @author Johann Sorel (Geomatys) - * @version 1.0 - * @since 1.0 - * @module - * - * @todo Is this class really needed? - */ -public final class SingleAttributeTypeBuilder { - /** - * Properties (name, description, …) to give to the attribute type constructor. - */ - private final Map<String,Object> properties; - - /** - * Optional characteristics of the attribute. - */ - private final List<AttributeType<?>> characteristics; - - /** - * The class of the attribute. - */ - private Class<?> valueClass; - - /** - * Minimum number of occurrences. - */ - private int minimumOccurs; - - /** - * Maximum number of occurrences. - */ - private int maximumOccurs; - - /** - * The default value, or {@code null} if none. - */ - private Object defaultValue; - - /** - * Creates a new attribute type builder. - */ - public SingleAttributeTypeBuilder() { - properties = new HashMap<>(); - characteristics = new ArrayList<>(); - minimumOccurs = 1; - maximumOccurs = 1; - } - - /** - * Restores this builder to its initial state. - * This method can be invoked before to build another attribute. - * - * @return {@code this} for method call chaining. - */ - public SingleAttributeTypeBuilder reset(){ - properties.clear(); - characteristics.clear(); - valueClass = null; - minimumOccurs = 1; - maximumOccurs = 1; - defaultValue = null; - return this; - } - - /** - * Sets this builder to the same properties then the given attribute type. - * - * @param template the attribute type to use as a template. - * @return {@code this} for method call chaining. - */ - public SingleAttributeTypeBuilder copy(AttributeType<?> template) { - reset(); - setName (template.getName()); - setDefinition (template.getDefinition()); - setDescription(template.getDescription()); - setDesignation(template.getDesignation()); - characteristics.addAll(template.characteristics().values()); - valueClass = template.getValueClass(); - minimumOccurs = template.getMinimumOccurs(); - maximumOccurs = template.getMaximumOccurs(); - defaultValue = template.getDefaultValue(); - return this; - } - - public SingleAttributeTypeBuilder setName(String localPart) { - return setName(null, localPart); - } - - public SingleAttributeTypeBuilder setName(String namespace, String localPart) { - final GenericName name; - if (namespace == null || namespace.isEmpty()) { - name = DefaultFactories.forBuildin(NameFactory.class).createGenericName(null, localPart); - } else { - name = DefaultFactories.forBuildin(NameFactory.class).createGenericName(null, namespace, localPart); - } - return setName(name); - } - - public SingleAttributeTypeBuilder setName(GenericName name) { - properties.put(DefaultAttributeType.NAME_KEY, name); - return this; - } - - public GenericName getName() { - return (GenericName) properties.get(DefaultAttributeType.NAME_KEY); - } - - public SingleAttributeTypeBuilder setDescription(CharSequence description) { - properties.put(DESCRIPTION_KEY, description); - return this; - } - - public CharSequence getDescription() { - return (CharSequence) properties.get(DESCRIPTION_KEY); - } - - public SingleAttributeTypeBuilder setDesignation(CharSequence designation){ - properties.put(DESIGNATION_KEY, designation); - return this; - } - - public CharSequence getDesignation(){ - return (CharSequence) properties.get(DESIGNATION_KEY); - } - - public SingleAttributeTypeBuilder setDefinition(CharSequence definition){ - properties.put(DEFINITION_KEY, definition); - return this; - } - - public CharSequence getDefinition(){ - return (CharSequence) properties.get(DEFINITION_KEY); - } - - public SingleAttributeTypeBuilder setValueClass(Class<?> valueClass) { - this.valueClass = valueClass; - return this; - } - - public Class<?> getValueClass(){ - return valueClass; - } - - public SingleAttributeTypeBuilder setDefaultValue(Object defaultValue) { - this.defaultValue = defaultValue; - return this; - } - - public Object getDefaultValue() { - return defaultValue; - } - - public SingleAttributeTypeBuilder setMinimumOccurs(int minimumOccurs) { - this.minimumOccurs = minimumOccurs; - return this; - } - - public int getMinimumOccurs() { - return minimumOccurs; - } - - public SingleAttributeTypeBuilder setMaximumOccurs(int maximumOccurs) { - this.maximumOccurs = maximumOccurs; - return this; - } - - public int getMaximumOccurs() { - return maximumOccurs; - } - - /** - * Set maximum string length. - * - * @param length maximal number of characters. - * @return {@code this} for method call chaining. - */ - public SingleAttributeTypeBuilder setLength(int length) { - return addCharacteristic(AttributeConvention.MAXIMAL_LENGTH_CHARACTERISTIC, Integer.class, 0, 1, length); - } - - public SingleAttributeTypeBuilder setCRS(CoordinateReferenceSystem crs) { - return addCharacteristic(AttributeConvention.CRS_CHARACTERISTIC, CoordinateReferenceSystem.class, 0, 1, crs); - } - - public SingleAttributeTypeBuilder setPossibleValues(Collection<?> values) { - return addCharacteristic(AttributeConvention.VALID_VALUES_CHARACTERISTIC, Object.class, 0, 1, values); - } - - public SingleAttributeTypeBuilder addCharacteristic(String localPart, Class<?> valueClass, int minimumOccurs, int maximumOccurs, Object defaultValue) { - final GenericName name = DefaultFactories.forBuildin(NameFactory.class).createGenericName(null, localPart); - return addCharacteristic(name,valueClass,minimumOccurs,maximumOccurs,defaultValue); - } - - public SingleAttributeTypeBuilder addCharacteristic(GenericName name, Class<?> valueClass, int minimumOccurs, int maximumOccurs, Object defaultValue) { - return addCharacteristic(new DefaultAttributeType( - Collections.singletonMap(NAME_KEY, name), - valueClass,minimumOccurs,maximumOccurs,defaultValue)); - } - - public SingleAttributeTypeBuilder addCharacteristic(AttributeType<?> characteristic) { - //search and remove previous characteristic with the same id if it exist - for (AttributeType<?> at : characteristics) { - if (at.getName().equals(characteristic.getName())) { - characteristics.remove(at); - break; - } - } - characteristics.add(characteristic); - return this; - } - - public AttributeType<?> build(){ - return new DefaultAttributeType(properties, valueClass, - minimumOccurs, maximumOccurs, - defaultValue, characteristics.toArray(new AttributeType[characteristics.size()])); - } - - public static AttributeType<?> create(GenericName name, Class<?> valueClass) { - return new DefaultAttributeType(Collections.singletonMap("name", name), valueClass, 1, 1, null); - } -} diff --git a/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/postgres/PostgresDialect.java b/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/postgres/PostgresDialect.java index 6bd82cd..2ec1041 100644 --- a/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/postgres/PostgresDialect.java +++ b/storage/sis-sql/src/main/java/org/apache/sis/internal/sql/postgres/PostgresDialect.java @@ -23,16 +23,16 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; import org.opengis.referencing.crs.CoordinateReferenceSystem; -import org.apache.sis.internal.sql.feature.SingleAttributeTypeBuilder; import org.apache.sis.internal.sql.feature.ColumnMetaModel; -import org.apache.sis.storage.DataStoreException; import org.apache.sis.internal.sql.feature.Dialect; +import org.apache.sis.feature.builder.AttributeTypeBuilder; +import org.apache.sis.storage.DataStoreException; /** * Implements PostgreSQL-specific functionalities. * - * @author Johann Sorel (Geomatys) + * @author Johann Sorel (Geomatys) * @version 1.0 * @since 1.0 * @module @@ -51,11 +51,6 @@ final class PostgresDialect extends Dialect { } @Override - public boolean supportGlobalMetadata() { - return true; - } - - @Override public boolean isTableIgnored(String name) { return IGNORE_TABLES.contains(name.toLowerCase()); } @@ -66,16 +61,6 @@ final class PostgresDialect extends Dialect { } @Override - public void encodeColumnName(StringBuilder sql, String name) { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - public void encodeSchemaAndTableName(StringBuilder sql, String databaseSchema, String tableName) { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override public Object nextValue(ColumnMetaModel column, Connection cx) throws SQLException, DataStoreException { throw new UnsupportedOperationException("Not supported yet."); } @@ -86,12 +71,12 @@ final class PostgresDialect extends Dialect { } @Override - public void decodeColumnType(SingleAttributeTypeBuilder atb, Connection cx, String typeName, int datatype, String schemaName, String tableName, String columnName) throws SQLException { + public void decodeColumnType(AttributeTypeBuilder<?> atb, Connection cx, String typeName, int datatype, String schemaName, String tableName, String columnName) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); } @Override - public void decodeGeometryColumnType(SingleAttributeTypeBuilder atb, Connection cx, ResultSet rs, int columnIndex, boolean customquery) throws SQLException { + public void decodeGeometryColumnType(AttributeTypeBuilder<?> atb, Connection cx, ResultSet rs, int columnIndex, boolean customquery) throws SQLException { throw new UnsupportedOperationException("Not supported yet."); }
