diff --git a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/AbstractSQLDialect.java b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/AbstractSQLDialect.java index 80072b9bc2..0f2aabf0c1 100644 --- a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/AbstractSQLDialect.java +++ b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/AbstractSQLDialect.java @@ -34,6 +34,13 @@ ----------------------------------------------------------------------------*/ package org.deegree.sqldialect; +import org.deegree.commons.jdbc.TableName; +import org.slf4j.Logger; + +import java.util.List; + +import static org.slf4j.LoggerFactory.getLogger; + /** * Implementations provide the vendor-specific behavior for a spatial DBMS so it can be * accessed by deegree. @@ -43,6 +50,8 @@ */ public abstract class AbstractSQLDialect implements SQLDialect { + private static final Logger LOG = getLogger(AbstractSQLDialect.class); + private char defaultEscapeChar = Character.UNASSIGNED; @Override @@ -60,4 +69,15 @@ public boolean isRowLimitingCapable() { return true; } + @Override + public String getSelectBBox(List columns, List tables) { + if (columns.size() > 1 || tables.size() > 1) + LOG.warn("Multiple geometry columns are currently not supported. Using first."); + StringBuilder sql = new StringBuilder("SELECT "); + sql.append(getBBoxAggregateSnippet(columns.get(0))); + sql.append(" FROM "); + sql.append(tables.get(0)); + return sql.toString(); + } + } diff --git a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/SQLDialect.java b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/SQLDialect.java index 854e3ebc4e..aeb733477f 100644 --- a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/SQLDialect.java +++ b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/SQLDialect.java @@ -139,11 +139,22 @@ AbstractWhereBuilder getWhereBuilder(PropertyNameMapper mapper, OperatorFilter f /** * Returns an SQL snippet for SELECTing the aggregate bounding box of the given * column. - * @param colummn name of the column that stores the bounding box, never + * @param column name of the column that stores the bounding box, never * null * @return SQL snippet, never null */ - String getBBoxAggregateSnippet(String colummn); + String getBBoxAggregateSnippet(String column); + + /** + * Returns an SQL snippet for SELECTing the aggregate bounding box of the given + * columns. + * @param columns list of column names that stores the bounding box, never + * null + * @param tables list of table names that stores the bounding box, never + * null + * @return SQL snippet, never null + */ + String getSelectBBox(List columns, List tables); /** * Converts the value that has been SELECTed via diff --git a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-mssql/src/main/java/org/deegree/sqldialect/mssql/MSSQLDialect.java b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-mssql/src/main/java/org/deegree/sqldialect/mssql/MSSQLDialect.java index 41773f9319..83fa95d97b 100644 --- a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-mssql/src/main/java/org/deegree/sqldialect/mssql/MSSQLDialect.java +++ b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-mssql/src/main/java/org/deegree/sqldialect/mssql/MSSQLDialect.java @@ -121,7 +121,7 @@ public String getUndefinedSrid() { } @Override - public String getBBoxAggregateSnippet(String colummn) { + public String getBBoxAggregateSnippet(String column) { return "1"; } diff --git a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISDialect.java b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISDialect.java index 014a5f9c21..97436c7f31 100644 --- a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISDialect.java +++ b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISDialect.java @@ -40,6 +40,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.List; +import java.util.stream.Collectors; import org.deegree.commons.jdbc.SQLIdentifier; import org.deegree.commons.jdbc.TableName; @@ -195,6 +196,29 @@ public String getBBoxAggregateSnippet(String column) { return sql.toString(); } + @Override + public String getSelectBBox(List columns, List tables) { + if (columns.size() <= 1) { + return super.getSelectBBox(columns, tables); + } + StringBuilder sql = new StringBuilder("SELECT "); + sql.append("ST_Extent( u.allbboxes )::BOX2D FROM ("); + // subquery start + sql.append("SELECT ST_Collect( ARRAY[ "); + boolean isFirst = true; + for (String column : columns) { + if (!isFirst) + sql.append(", "); + sql.append(getBBoxAggregateSnippet(column)); + isFirst = false; + } + sql.append(" ] ) as allbboxes FROM "); + sql.append(tables.stream().map(SQLIdentifier::toString).collect(Collectors.joining(", "))); + // subquery end + sql.append(") u"); + return sql.toString(); + } + @Override public Envelope getBBoxAggregateValue(ResultSet rs, int colIdx, ICRS crs) throws SQLException { Envelope env = null; diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/FeatureTypeMapping.java b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/FeatureTypeMapping.java index 037bdf9b55..5ddf7fc23d 100644 --- a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/FeatureTypeMapping.java +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/FeatureTypeMapping.java @@ -157,6 +157,24 @@ public List getDefaultSortCriteria() { * defined) */ public Pair getDefaultGeometryMapping() { + List> defaultGeometryMappings = getAllGeometryMappings(); + if (defaultGeometryMappings.isEmpty()) + return null; + return defaultGeometryMappings.get(0); + } + + /** + * Returns all {@link GeometryMapping}. + * @return all geometry mappings, may be empty (no geometry mapping + * defined) but never null + */ + public List> getAllGeometryMappings() { + List> defaultGeometryMappings = new ArrayList<>(); + addGeometryMappings(defaultGeometryMappings); + return defaultGeometryMappings; + } + + private void addGeometryMappings(List> defaultGeometryMappings) { TableName table = getFtTable(); for (Mapping particle : particles) { if (particle instanceof GeometryMapping) { @@ -164,33 +182,28 @@ public Pair getDefaultGeometryMapping() { if (joins != null && !joins.isEmpty()) { table = joins.get(joins.size() - 1).getToTable(); } - return new Pair(table, (GeometryMapping) particle); + defaultGeometryMappings.add(new Pair<>(table, (GeometryMapping) particle)); } - } - for (Mapping particle : particles) { - TableName propTable = table; if (particle instanceof CompoundMapping) { + TableName propTable = table; List joins = particle.getJoinedTable(); if (joins != null && !joins.isEmpty()) { propTable = joins.get(joins.size() - 1).getToTable(); } - Pair gm = getDefaultGeometryMapping(propTable, (CompoundMapping) particle); - if (gm != null) { - return gm; - } + addGeometryMappings(defaultGeometryMappings, propTable, (CompoundMapping) particle); } } - return null; } - private Pair getDefaultGeometryMapping(TableName table, CompoundMapping complex) { + private void addGeometryMappings(List> defaultGeometryMappings, TableName table, + CompoundMapping complex) { for (Mapping particle : complex.getParticles()) { if (particle instanceof GeometryMapping) { List joins = particle.getJoinedTable(); if (joins != null && !joins.isEmpty()) { table = joins.get(joins.size() - 1).getToTable(); } - return new Pair(table, (GeometryMapping) particle); + defaultGeometryMappings.add(new Pair<>(table, (GeometryMapping) particle)); } } for (Mapping particle : complex.getParticles()) { @@ -200,13 +213,9 @@ private Pair getDefaultGeometryMapping(TableName tab if (joins != null && !joins.isEmpty()) { propTable = joins.get(joins.size() - 1).getToTable(); } - Pair gm = getDefaultGeometryMapping(propTable, (CompoundMapping) particle); - if (gm != null) { - return gm; - } + addGeometryMappings(defaultGeometryMappings, propTable, (CompoundMapping) particle); } } - return null; } } \ No newline at end of file diff --git a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java index b3dec90fc6..ae4eb760c6 100644 --- a/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java +++ b/deegree-datastores/deegree-featurestores/deegree-featurestore-sql/src/main/java/org/deegree/feature/persistence/sql/SQLFeatureStore.java @@ -56,6 +56,7 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; +import java.util.stream.Collectors; import javax.xml.namespace.QName; @@ -488,25 +489,36 @@ private Envelope calcEnvelope(FeatureTypeMapping ftMapping, Connection conn) thr LOG.trace("Determining BBOX for feature type '{}' (relational mode)", ftMapping.getFeatureType()); - String column = null; - Pair propMapping = ftMapping.getDefaultGeometryMapping(); - if (propMapping == null) { + List> geometryMappings = ftMapping.getAllGeometryMappings(); + if (geometryMappings.isEmpty()) { return null; } - MappingExpression me = propMapping.second.getMapping(); - if (me == null || !(me instanceof DBField)) { - String msg = "Cannot determine BBOX for feature type '" + ftMapping.getFeatureType() - + "' (relational mode)."; - LOG.warn(msg); + + boolean containsUnsupportedMapping = geometryMappings.stream().anyMatch(geometryMapping -> { + MappingExpression mapping = geometryMapping.second.getMapping(); + return mapping == null || !(mapping instanceof DBField); + }); + if (containsUnsupportedMapping) { + LOG.warn("Cannot determine BBOX for feature type '{}' (relational mode).", ftMapping.getFeatureType()); + return null; + } + long noOfDifferentCrs = geometryMappings.stream() + .map(geometryMapping -> geometryMapping.second.getCRS()) + .distinct() + .count(); + if (noOfDifferentCrs > 1) { + LOG.warn("Multiple geometries with different CRS are currently not supported"); return null; } - column = ((DBField) me).getColumn(); - Envelope env = null; - StringBuilder sql = new StringBuilder("SELECT "); - sql.append(dialect.getBBoxAggregateSnippet(column)); - sql.append(" FROM "); - sql.append(propMapping.first); + List columns = geometryMappings.stream() + .map(geometryMapping -> ((DBField) geometryMapping.second.getMapping()).getColumn()) + .collect(Collectors.toList()); + List uniqueTableNames = geometryMappings.stream() + .map(geometryMapping -> geometryMapping.first) + .distinct() + .collect(Collectors.toUnmodifiableList()); + String sql = dialect.getSelectBBox(columns, uniqueTableNames); Statement stmt = null; ResultSet rs = null; @@ -515,8 +527,8 @@ private Envelope calcEnvelope(FeatureTypeMapping ftMapping, Connection conn) thr LOG.debug("Executing envelope SELECT: " + sql); rs = stmt.executeQuery(sql.toString()); rs.next(); - ICRS crs = propMapping.second.getCRS(); - env = dialect.getBBoxAggregateValue(rs, 1, crs); + ICRS crs = geometryMappings.get(0).second.getCRS(); + return dialect.getBBoxAggregateValue(rs, 1, crs); } catch (SQLException e) { LOG.debug(e.getMessage(), e); @@ -525,7 +537,6 @@ private Envelope calcEnvelope(FeatureTypeMapping ftMapping, Connection conn) thr finally { release(rs, stmt, null); } - return env; } private Envelope calcEnvelope(QName ftName, BlobMapping blobMapping, Connection conn) throws FeatureStoreException {