diff --git a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/AbstractWhereBuilder.java b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/AbstractWhereBuilder.java index a8a9bde54b..1f3e86384a 100644 --- a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/AbstractWhereBuilder.java +++ b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-commons/src/main/java/org/deegree/sqldialect/filter/AbstractWhereBuilder.java @@ -77,6 +77,7 @@ import org.deegree.filter.sort.SortProperty; import org.deegree.filter.spatial.BBOX; import org.deegree.filter.spatial.SpatialOperator; +import org.deegree.filter.temporal.TemporalOperator; import org.deegree.geometry.Geometry; import org.deegree.sqldialect.SQLDialect; import org.deegree.sqldialect.filter.expression.SQLArgument; @@ -313,6 +314,9 @@ protected SQLExpression toProtoSQL( Operator op ) sql = toProtoSQL( (SpatialOperator) op ); break; } + case TEMPORAL: { + sql = toProtoSQL( (TemporalOperator) op ); + } } return sql; } @@ -487,7 +491,7 @@ protected SQLExpression toProtoSQL( ComparisonOperator op ) return sqlOper; } - private void inferType( SQLExpression expr1, SQLExpression expr2 ) { + protected void inferType( SQLExpression expr1, SQLExpression expr2 ) { PrimitiveType pt1 = expr1.getPrimitiveType(); PrimitiveType pt2 = expr2.getPrimitiveType(); if ( pt1 == null && pt2 != null ) { @@ -501,7 +505,7 @@ private void inferType( SQLExpression expr1, SQLExpression expr2 ) { } } - private void inferType( SQLExpression expr1, SQLExpression expr2, SQLExpression expr3 ) { + protected void inferType( SQLExpression expr1, SQLExpression expr2, SQLExpression expr3 ) { PrimitiveType pt1 = expr1.getPrimitiveType(); PrimitiveType pt2 = expr2.getPrimitiveType(); PrimitiveType pt3 = expr3.getPrimitiveType(); @@ -537,6 +541,10 @@ private PropertyIsLike buildIsLike( Expression propName, Expression literal, boo matchCase, null ); } + protected void addExpression( SQLOperationBuilder builder, SQLExpression expr ) { + addExpression( builder, expr, true ); + } + protected void addExpression( SQLOperationBuilder builder, SQLExpression expr, Boolean matchCase ) { if ( matchCase == null || matchCase ) { builder.add( expr ); @@ -658,6 +666,23 @@ protected SQLOperation toProtoSQL( LogicalOperator op ) protected abstract SQLOperation toProtoSQL( SpatialOperator op ) throws UnmappableException, FilterEvaluationException; + /** + * Translates the given {@link TemporalOperator} into an {@link SQLOperation}. + * + * @param op + * temporal operator to be translated, must not be null + * @return corresponding SQL expression, may be null + * @throws UnmappableException + * if translation is not possible (usually due to unmappable property names) + * @throws FilterEvaluationException + * if the filter contains invalid {@link ValueReference}s + */ + protected SQLOperation toProtoSQL( TemporalOperator op ) + throws UnmappableException, FilterEvaluationException { + LOG.warn( "Mapping of temporal operators to SQL is not implemented (and probably not possible)." ); + return null; + } + /** * Translates the given {@link Expression} into an {@link SQLExpression}. * @@ -837,7 +862,8 @@ protected SQLExpression toProtoSQL( Literal literal ) throw new UnmappableException( "Only primitive valued literals are currently supported." ); } } - PrimitiveParticleConverter converter = new DefaultPrimitiveConverter( new PrimitiveType( STRING ), null, false ); + PrimitiveParticleConverter converter = new DefaultPrimitiveConverter( new PrimitiveType( STRING ), null, + false ); return new SQLArgument( null, converter ); } diff --git a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISWhereBuilder.java b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISWhereBuilder.java index 7dc08a62b8..dc99b8301d 100644 --- a/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISWhereBuilder.java +++ b/deegree-core/deegree-core-sqldialect/deegree-sqldialect-postgis/src/main/java/org/deegree/sqldialect/postgis/PostGISWhereBuilder.java @@ -36,12 +36,15 @@ package org.deegree.sqldialect.postgis; import static java.sql.Types.BOOLEAN; +import static org.deegree.commons.tom.primitive.BaseType.DATE_TIME; import static org.deegree.commons.tom.primitive.BaseType.DECIMAL; import java.util.ArrayList; +import java.util.Date; import java.util.List; import org.deegree.commons.tom.TypedObjectNode; +import org.deegree.commons.tom.datetime.ISO8601Converter; import org.deegree.commons.tom.primitive.PrimitiveType; import org.deegree.commons.tom.primitive.PrimitiveValue; import org.deegree.commons.tom.sql.DefaultPrimitiveConverter; @@ -68,6 +71,7 @@ import org.deegree.filter.spatial.SpatialOperator; import org.deegree.filter.spatial.Touches; import org.deegree.filter.spatial.Within; +import org.deegree.filter.temporal.TemporalOperator; import org.deegree.geometry.Geometry; import org.deegree.sqldialect.filter.AbstractWhereBuilder; import org.deegree.sqldialect.filter.PropertyNameMapper; @@ -77,6 +81,10 @@ import org.deegree.sqldialect.filter.expression.SQLOperation; import org.deegree.sqldialect.filter.expression.SQLOperationBuilder; import org.deegree.sqldialect.filter.islike.IsLikeString; +import org.deegree.time.position.IndeterminateValue; +import org.deegree.time.position.TimePosition; +import org.deegree.time.primitive.GenericTimeInstant; +import org.deegree.time.primitive.GenericTimePeriod; /** * {@link AbstractWhereBuilder} implementation for PostGIS databases. @@ -113,7 +121,7 @@ public class PostGISWhereBuilder extends AbstractWhereBuilder { */ public PostGISWhereBuilder( PostGISDialect dialect, PropertyNameMapper mapper, OperatorFilter filter, SortProperty[] sortCrit, boolean allowPartialMappings, boolean useLegacyPredicates ) - throws FilterEvaluationException, UnmappableException { + throws FilterEvaluationException, UnmappableException { super( dialect, mapper, filter, sortCrit ); this.useLegacyPredicates = useLegacyPredicates; build( allowPartialMappings ); @@ -157,8 +165,7 @@ protected String getStringValueFromFunction( Expression pattern ) appendParamsFromFunction( function, params ); TypedObjectNode value = evaluateFunction( function, params ); if ( !( value instanceof PrimitiveValue ) ) { - throw new UnsupportedOperationException( - "SQL IsLike request with a function evaluating to a non-primitive value is not supported!" ); + throw new UnsupportedOperationException( "SQL IsLike request with a function evaluating to a non-primitive value is not supported!" ); } String valueAsString = ( (PrimitiveValue) value ).getAsText(); return valueAsString; @@ -385,6 +392,96 @@ protected SQLOperation toProtoSQL( SpatialOperator op ) return builder.toOperation(); } + protected SQLOperation toProtoSQL( TemporalOperator op ) + throws UnmappableException, FilterEvaluationException { + SQLOperation sql = null; + switch ( op.getSubType() ) { + case AFTER: { + SQLExpression first = toProtoSQL( op.getParameter1() ); + Expression parameter2 = op.getParameter2(); + SQLExpression second; + if ( isTimeInstant( parameter2 ) ) { + TimePosition timePosition = ( (GenericTimeInstant) ( (Literal) parameter2 ).getValue() ).getPosition(); + second = createDateExpression( timePosition ); + } else if ( isTimePeriod( parameter2 ) ) { + TimePosition end = ( (GenericTimePeriod) ( (Literal) parameter2 ).getValue() ).getEndPosition(); + second = createDateExpression( end ); + } else { + second = toProtoSQL( parameter2 ); + } + inferType( first, second ); + sql = createSqlAfter( first, second ); + break; + } + case BEFORE: { + SQLExpression first = toProtoSQL( op.getParameter1() ); + Expression parameter2 = op.getParameter2(); + SQLExpression second; + if ( isTimeInstant( parameter2 ) ) { + TimePosition timePosition = ( (GenericTimeInstant) ( (Literal) parameter2 ).getValue() ).getPosition(); + second = createDateExpression( timePosition ); + } else if ( isTimePeriod( parameter2 ) ) { + TimePosition begin = ( (GenericTimePeriod) ( (Literal) parameter2 ).getValue() ).getBeginPosition(); + second = createDateExpression( begin ); + } else { + second = toProtoSQL( parameter2 ); + } + inferType( first, second ); + sql = createSqlBefore( first, second ); + break; + } + case TEQUALS: { + SQLExpression first = toProtoSQL( op.getParameter1() ); + Expression parameter2 = op.getParameter2(); + SQLExpression second; + if ( isTimeInstant( parameter2 ) ) { + TimePosition timePosition = ( (GenericTimeInstant) ( (Literal) parameter2 ).getValue() ).getPosition(); + second = createDateExpression( timePosition ); + } else { + second = toProtoSQL( parameter2 ); + } + inferType( first, second ); + sql = createSqlEquals( first, second ); + break; + } + case DURING: { + Expression parameter2 = op.getParameter2(); + if ( isTimePeriod( parameter2 ) ) { + TimePosition begin = ( (GenericTimePeriod) ( (Literal) parameter2 ).getValue() ).getBeginPosition(); + TimePosition end = ( (GenericTimePeriod) ( (Literal) parameter2 ).getValue() ).getEndPosition(); + SQLExpression valueReference = toProtoSQL( op.getParameter1() ); + SQLExpression beginExpr = createDateExpression( begin ); + SQLExpression endExpr = createDateExpression( end ); + sql = createSqlDuring( valueReference, beginExpr, endExpr ); + } + break; + } + default: + sql = super.toProtoSQL( op ); + } + return sql; + } + + private SQLExpression createDateExpression( TimePosition timePosition ) { + if ( timePosition.getIndeterminatePosition() != null ) { + if ( timePosition.getIndeterminatePosition().equals( IndeterminateValue.NOW ) ) { + String encodedTemporal = ISO8601Converter.formatDateTime( new Date() ); + TimePosition now = new TimePosition( null, null, null, encodedTemporal ); + return createDateExpressionFromTimePosition( now ); + } else { + return null; + } + } + return createDateExpressionFromTimePosition( timePosition ); + } + + private SQLExpression createDateExpressionFromTimePosition( TimePosition now ) { + PrimitiveValue value = new PrimitiveValue( now.getValue(), new PrimitiveType( DATE_TIME ) ); + DefaultPrimitiveConverter converter = new DefaultPrimitiveConverter( new PrimitiveType( DATE_TIME ), null, + false ); + return new SQLArgument( value, converter ); + } + @Override protected void addExpression( SQLOperationBuilder builder, SQLExpression expr, Boolean matchCase ) { if ( matchCase == null || matchCase ) { @@ -400,4 +497,79 @@ private SQLExpression toProtoSQL( Geometry geom, ICRS targetCRS, int srid ) throws FilterEvaluationException { return new SQLArgument( geom, new PostGISGeometryConverter( null, targetCRS, "" + srid, useLegacyPredicates ) ); } + + private SQLOperation createSqlDuring( SQLExpression valueReference, SQLExpression beginExpr, + SQLExpression endExpr ) { + if ( beginExpr == null && endExpr == null ) + return null; + if ( beginExpr != null && endExpr == null ) { + SQLOperationBuilder builder = new SQLOperationBuilder( BOOLEAN ); + builder.add( "(" ); + addExpression( builder, valueReference ); + builder.add( " >= " ); + addExpression( builder, beginExpr ); + builder.add( ")" ); + return builder.toOperation(); + } + if ( beginExpr == null && endExpr != null ) { + SQLOperationBuilder builder = new SQLOperationBuilder( BOOLEAN ); + builder.add( "(" ); + addExpression( builder, valueReference ); + builder.add( " <= " ); + addExpression( builder, endExpr ); + builder.add( ")" ); + return builder.toOperation(); + } + inferType( valueReference, beginExpr, endExpr ); + SQLOperationBuilder builder = new SQLOperationBuilder( BOOLEAN ); + builder.add( "(" ); + addExpression( builder, valueReference ); + builder.add( " >= " ); + addExpression( builder, beginExpr ); + builder.add( " AND " ); + addExpression( builder, valueReference ); + builder.add( " <= " ); + addExpression( builder, endExpr ); + builder.add( ")" ); + return builder.toOperation(); + } + + private SQLOperation createSqlEquals( SQLExpression first, SQLExpression second ) { + SQLOperationBuilder builder = new SQLOperationBuilder( BOOLEAN ); + builder.add( "(" ); + addExpression( builder, first ); + builder.add( " = " ); + addExpression( builder, second ); + builder.add( ")" ); + return builder.toOperation(); + } + + private SQLOperation createSqlBefore( SQLExpression first, SQLExpression second ) { + SQLOperationBuilder builder = new SQLOperationBuilder( BOOLEAN ); + builder.add( "(" ); + addExpression( builder, first ); + builder.add( " < " ); + addExpression( builder, second ); + builder.add( ")" ); + return builder.toOperation(); + } + + private SQLOperation createSqlAfter( SQLExpression first, SQLExpression second ) { + SQLOperationBuilder builder = new SQLOperationBuilder( BOOLEAN ); + builder.add( "(" ); + addExpression( builder, first ); + builder.add( " > " ); + addExpression( builder, second ); + builder.add( ")" ); + return builder.toOperation(); + } + + private boolean isTimePeriod( Expression parameter2 ) { + return parameter2 instanceof Literal && ( (Literal) parameter2 ).getValue() instanceof GenericTimePeriod; + } + + private boolean isTimeInstant( Expression parameter2 ) { + return parameter2 instanceof Literal && ( (Literal) parameter2 ).getValue() instanceof GenericTimeInstant; + } + } \ No newline at end of file