de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.PgExpressionHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of FROST-Server.SQLjooq Show documentation
Show all versions of FROST-Server.SQLjooq Show documentation
SQL bindings for the FROST-Server.
/*
* Copyright (C) 2024 Fraunhofer Institut IOSB, Fraunhoferstr. 1, D 76131
* Karlsruhe, Germany.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*/
package de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq;
import static de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.utils.QueryState.ALIAS_ROOT;
import static de.fraunhofer.iosb.ilt.frostserver.property.SpecialNames.AT_IOT_ID;
import static de.fraunhofer.iosb.ilt.frostserver.util.Constants.NOT_IMPLEMENTED_MULTI_VALUE_PK;
import de.fraunhofer.iosb.ilt.frostserver.model.EntityType;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.bindings.PostGisGeometryBinding;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.fieldwrapper.ArrayConstandFieldWrapper;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.fieldwrapper.FieldListWrapper;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.fieldwrapper.FieldWrapper;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.fieldwrapper.JsonFieldFactory;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.fieldwrapper.JsonFieldFactory.JsonFieldWrapper;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.fieldwrapper.NullWrapper;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.fieldwrapper.SimpleFieldWrapper;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.fieldwrapper.StaDateTimeWrapper;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.fieldwrapper.StaDurationWrapper;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.fieldwrapper.StaTimeIntervalWrapper;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.fieldwrapper.TimeFieldWrapper;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.fieldwrapper.WrapperHelper;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.tables.StaMainTable;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.tables.TableCollection;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.utils.QueryState;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.utils.TableRef;
import de.fraunhofer.iosb.ilt.frostserver.persistence.pgjooq.utils.Utils;
import de.fraunhofer.iosb.ilt.frostserver.property.EntityPropertyCustom;
import de.fraunhofer.iosb.ilt.frostserver.property.EntityPropertyCustomLink;
import de.fraunhofer.iosb.ilt.frostserver.property.EntityPropertyMain;
import de.fraunhofer.iosb.ilt.frostserver.property.NavigationProperty;
import de.fraunhofer.iosb.ilt.frostserver.property.NavigationPropertyMain;
import de.fraunhofer.iosb.ilt.frostserver.property.NavigationPropertyMain.NavigationPropertyEntitySet;
import de.fraunhofer.iosb.ilt.frostserver.property.Property;
import de.fraunhofer.iosb.ilt.frostserver.property.PropertyReference;
import de.fraunhofer.iosb.ilt.frostserver.query.OrderBy;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.Expression;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.ExpressionVisitor;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.Path;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.constant.BooleanConstant;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.constant.ConstantList;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.constant.DateConstant;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.constant.DateTimeConstant;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.constant.DoubleConstant;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.constant.DurationConstant;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.constant.GeoJsonConstant;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.constant.IntegerConstant;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.constant.IntervalConstant;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.constant.LineStringConstant;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.constant.NullConstant;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.constant.PointConstant;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.constant.PolygonConstant;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.constant.StringConstant;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.constant.TimeConstant;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.Function;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.arithmetic.Add;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.arithmetic.Divide;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.arithmetic.Modulo;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.arithmetic.Multiply;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.arithmetic.Subtract;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.comparison.Equal;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.comparison.GreaterEqual;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.comparison.GreaterThan;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.comparison.In;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.comparison.LessEqual;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.comparison.LessThan;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.comparison.NotEqual;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.context.ContextEntityProperty;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.context.PrincipalName;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.date.Date;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.date.Day;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.date.FractionalSeconds;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.date.Hour;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.date.MaxDateTime;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.date.MinDateTime;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.date.Minute;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.date.Month;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.date.Now;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.date.Second;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.date.Time;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.date.TotalOffsetMinutes;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.date.Year;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.logical.And;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.logical.Any;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.logical.Not;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.logical.Or;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.math.Ceiling;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.math.Floor;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.math.Round;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.spatialrelation.GeoDistance;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.spatialrelation.GeoIntersects;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.spatialrelation.GeoLength;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.spatialrelation.STContains;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.spatialrelation.STCrosses;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.spatialrelation.STDisjoint;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.spatialrelation.STEquals;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.spatialrelation.STIntersects;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.spatialrelation.STOverlaps;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.spatialrelation.STRelate;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.spatialrelation.STTouches;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.spatialrelation.STWithin;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.string.Concat;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.string.EndsWith;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.string.IndexOf;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.string.Length;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.string.StartsWith;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.string.Substring;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.string.SubstringOf;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.string.ToLower;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.string.ToUpper;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.string.Trim;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.temporal.After;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.temporal.Before;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.temporal.During;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.temporal.Finishes;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.temporal.Meets;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.temporal.Overlaps;
import de.fraunhofer.iosb.ilt.frostserver.query.expression.function.temporal.Starts;
import de.fraunhofer.iosb.ilt.frostserver.settings.CoreSettings;
import de.fraunhofer.iosb.ilt.frostserver.settings.Settings;
import java.util.Calendar;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import net.time4j.Moment;
import net.time4j.PlainDate;
import net.time4j.PlainTime;
import net.time4j.ZonalDateTime;
import net.time4j.range.MomentInterval;
import org.apache.commons.lang3.NotImplementedException;
import org.geojson.GeoJsonObject;
import org.geolatte.geom.Geometry;
import org.geolatte.geom.codec.Wkt;
import org.jooq.Condition;
import org.jooq.DatePart;
import org.jooq.Field;
import org.jooq.impl.DSL;
import org.jooq.impl.SQLDataType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Hylke van der Schaaf
*/
public class PgExpressionHandler implements ExpressionVisitor {
/**
* The logger for this class.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(PgExpressionHandler.class);
private static final String ST_GEOM_FROM_EWKT = "ST_GeomFromEWKT(?)";
private final QueryBuilder queryBuilder;
private QueryState queryState;
private int maxCustomLinkDepth = -1;
public PgExpressionHandler(CoreSettings settings, QueryBuilder queryBuilder) {
this.queryBuilder = queryBuilder;
this.queryState = queryBuilder.getQueryState();
final Settings experimentalSettings = settings.getExtensionSettings();
if (experimentalSettings.getBoolean(CoreSettings.TAG_CUSTOM_LINKS_ENABLE, CoreSettings.class)) {
maxCustomLinkDepth = experimentalSettings.getInt(CoreSettings.TAG_CUSTOM_LINKS_RECURSE_DEPTH, CoreSettings.class);
}
}
public Condition addFilterToWhere(Expression filter, Condition sqlWhere) {
FieldWrapper filterField = filter.accept(this);
if (filterField.isCondition()) {
return sqlWhere.and(filterField.getCondition());
}
if (filterField instanceof FieldListWrapper listExpression) {
for (Field expression : listExpression.getExpressions().values()) {
if (Boolean.class.isAssignableFrom(expression.getType())) {
Field predicate = expression;
return sqlWhere.and(predicate);
}
}
}
LOGGER.error("Filter is not a predicate but a {}.", filterField.getClass().getName());
throw new IllegalArgumentException("Filter is not a predicate but a " + filterField.getClass().getName());
}
public void addOrderbyToQuery(OrderBy orderBy, Utils.SortSelectFields orderFields) {
FieldWrapper resultExpression = orderBy.getExpression().accept(this);
if (resultExpression instanceof StaTimeIntervalWrapper timeInterval) {
addToQuery(orderBy, timeInterval.getStart(), orderFields);
addToQuery(orderBy, timeInterval.getEnd(), orderFields);
return;
}
if (resultExpression instanceof StaDurationWrapper duration) {
addToQuery(orderBy, duration.getDuration(), orderFields);
return;
}
if (resultExpression instanceof StaDateTimeWrapper dateTime) {
addToQuery(orderBy, dateTime.getDateTime(), orderFields);
return;
}
if (resultExpression instanceof FieldListWrapper fieldListWrapper) {
for (Field sqlExpression : fieldListWrapper.getExpressionsForOrder().values()) {
addToQuery(orderBy, sqlExpression, orderFields);
}
return;
}
Field field = resultExpression.getDefaultField();
addToQuery(orderBy, field, orderFields);
}
public void addToQuery(OrderBy orderBy, Field field, Utils.SortSelectFields orderFields) {
orderFields.add(field, orderBy.getType());
}
@Override
public FieldWrapper visit(Path path) {
PathState state = new PathState();
state.elements = path.getElements();
var storedQueryState = queryState;
try {
Property firstElement = state.elements.get(0);
int startIdx = 0;
if (firstElement instanceof PropertyReference pr) {
startIdx = 1;
queryState = queryState.findStateForAlias(pr.getName());
} else {
queryState = queryState.findStateForAlias(ALIAS_ROOT);
}
state.pathTableRef = queryState.getTableRef();
walkPath(state, startIdx, path);
} finally {
queryState = storedQueryState;
}
return state.finalExpression;
}
private void walkPath(PathState state, int startIdx, Path path) throws IllegalArgumentException {
for (state.curIndex = startIdx; state.curIndex < state.elements.size() && !state.finished; state.curIndex++) {
Property element = state.elements.get(state.curIndex);
if (element instanceof EntityPropertyCustom) {
handleCustomProperty(state, path);
} else if (element instanceof EntityPropertyCustomLink) {
handleCustomProperty(state, path);
} else if (element instanceof EntityPropertyMain entityPropertyMain) {
handleEntityProperty(state, path, entityPropertyMain);
} else if (element instanceof NavigationPropertyMain navigationPropertyMain) {
handleNavigationProperty(state, path, navigationPropertyMain);
}
}
if (state.finalExpression == null) {
throw new IllegalArgumentException("Path does not end in an EntityProperty: " + path);
}
if (state.finalExpression instanceof Field field && Moment.class.isAssignableFrom(field.getType())) {
Field dateTimePath = (Field) state.finalExpression;
state.finalExpression = new StaDateTimeWrapper(dateTimePath);
}
}
private void handleCustomProperty(PathState state, Path path) {
if (state.finalExpression == null) {
throw new IllegalArgumentException("CustomProperty must follow an EntityProperty: " + path);
}
// generate finalExpression::jsonb#>>'{x,y,z}'
JsonFieldWrapper jsonFactory;
if (state.finalExpression instanceof JsonFieldWrapper jsonFieldWrapper) {
jsonFactory = jsonFieldWrapper;
} else {
jsonFactory = new JsonFieldWrapper(state.finalExpression);
}
for (; state.curIndex < state.elements.size(); state.curIndex++) {
final Property property = state.elements.get(state.curIndex);
String name = property.getName();
if (property instanceof EntityPropertyCustomLink epcl) {
int maxDepth = state.curIndex + maxCustomLinkDepth;
if (state.curIndex <= maxDepth) {
handleCustomLink(epcl, jsonFactory, name, state);
return;
} else {
jsonFactory.addToPath(name);
}
} else {
jsonFactory.addToPath(name);
}
}
state.finalExpression = jsonFactory.materialise();
state.finished = true;
}
private void handleCustomLink(final EntityPropertyCustomLink epcl, JsonFieldWrapper jsonFactory, String name, PathState state) {
JsonFieldFactory.JsonFieldWrapper sourceIdFieldWrapper = jsonFactory.addToPath(name + AT_IOT_ID).materialise();
Field sourceIdField = sourceIdFieldWrapper.getFieldAsType(Number.class, true);
state.pathTableRef = queryEntityType(epcl, state.pathTableRef, sourceIdField);
state.finalExpression = null;
}
private void handleEntityProperty(PathState state, Path path, EntityPropertyMain element) {
if (state.finalExpression != null) {
throw new IllegalArgumentException("EntityProperty can not follow an other EntityProperty: " + path);
}
Map pathExpressions = state.pathTableRef.getTable()
.getPropertyFieldRegistry()
.getAllFieldsForProperty(element, new LinkedHashMap<>());
if (pathExpressions.size() == 1) {
final Field field = pathExpressions.values().stream().iterator().next();
Field optimisedField = state.pathTableRef.getJoinEqual(field);
state.finalExpression = WrapperHelper.wrapField(optimisedField);
} else {
state.finalExpression = getSubExpression(state, pathExpressions);
}
}
private void handleNavigationProperty(PathState state, Path path, NavigationPropertyMain np) {
if (state.finalExpression != null) {
throw new IllegalArgumentException("NavigationProperty can not follow an EntityProperty: " + path);
}
state.pathTableRef = queryEntityType(np, state.pathTableRef);
}
private FieldWrapper getSubExpression(PathState state, Map pathExpressions) {
int nextIdx = state.curIndex + 1;
if (state.elements.size() > nextIdx) {
Property subProperty = state.elements.get(nextIdx);
// If the subProperty is unknown, and the expression can be of type JSON,
// then we assume JSON.
if (!pathExpressions.containsKey(subProperty.getName()) && pathExpressions.containsKey("j")) {
return new SimpleFieldWrapper(pathExpressions.get("j"));
}
// We can not accept json, so the subProperty must be a known direction.
state.finished = true;
return WrapperHelper.wrapField(pathExpressions.get(subProperty.getName()));
}
if (pathExpressions.containsKey(StaTimeIntervalWrapper.KEY_TIME_INTERVAL_START)
&& pathExpressions.containsKey(StaTimeIntervalWrapper.KEY_TIME_INTERVAL_END)) {
return new StaTimeIntervalWrapper(pathExpressions);
}
return new FieldListWrapper(pathExpressions);
}
/**
* Queries the given entity type, as relation to the given table reference
* and returns a new table reference. Effectively, this generates a join.
*
* @param np The NavigationProperty to query
* @param last The table the requested entity is related to.
* @return The table reference of the requested entity.
*/
public TableRef queryEntityType(NavigationProperty np, TableRef last) {
if (queryState == null) {
throw new IllegalStateException("QueryState should not be null");
}
if (last == null) {
throw new IllegalStateException("last result should not be null");
}
TableRef existingJoin = last.getJoin(np);
if (existingJoin != null) {
return existingJoin;
}
return last.createJoin(np.getName(), queryState);
}
/**
* Directly query an entity type. Used for custom linking.
*
* @param epcl the custom link.
* @param sourceRef The source table ref.
* @param sourceIdField The source ID field.
* @return A new table ref with the target entity type table joined.
*/
public TableRef queryEntityType(EntityPropertyCustomLink epcl, TableRef sourceRef, Field sourceIdField) {
final EntityType targetEntityType = epcl.getEntityType();
final StaMainTable> target = queryBuilder.getTableCollection().getTablesByType().get(targetEntityType);
final StaMainTable> targetAliased = target.asSecure(queryState.getNextAlias(), queryBuilder.getPersistenceManager());
final List targetField = targetAliased.getPkFields();
if (targetField.size() > 1) {
throw new NotImplementedException(NOT_IMPLEMENTED_MULTI_VALUE_PK);
}
queryState.setSqlFrom(queryState.getSqlFrom().leftJoin(targetAliased).on(targetField.get(0).eq(sourceIdField)));
TableRef newRef = new TableRef(targetAliased);
sourceRef.addJoin(epcl, newRef);
return newRef;
}
public Field[] findPair(FieldWrapper p1, FieldWrapper p2) {
Field[] result = new Field[2];
result[0] = p1.getFieldAsType(Number.class, true);
result[1] = p2.getFieldAsType(Number.class, true);
if (result[0] != null && result[1] != null) {
return result;
}
result[0] = p1.getFieldAsType(Boolean.class, true);
result[1] = p2.getFieldAsType(Boolean.class, true);
if (result[0] != null && result[1] != null) {
return result;
}
// If both are strings, use strings.
result[0] = p1.getFieldAsType(String.class, true);
result[1] = p2.getFieldAsType(String.class, true);
if (result[0] != null && result[1] != null) {
return result;
}
LOGGER.warn("Could not match types for {} and {}", p1, p2);
result[0] = p1.getDefaultField();
result[1] = p2.getDefaultField();
return result;
}
@Override
public FieldWrapper visit(Any node) {
final TableCollection tc = queryBuilder.getTableCollection();
final QueryState> parentQueryState = queryState;
QueryState existsQueryState = walkAnyPath(parentQueryState, node, tc);
if (existsQueryState == null) {
throw new IllegalStateException("Failed to parse any().");
}
// Set our subQuery state to be the active one.
queryState = existsQueryState;
try {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
if (!p1.isCondition()) {
throw new IllegalArgumentException("Any() requires a condition, got " + p1);
}
Condition exists = DSL.exists(DSL.selectOne().from(existsQueryState.getSqlFrom()).where(existsQueryState.getSqlWhere().and(p1.getCondition())));
return new SimpleFieldWrapper(exists);
} finally {
// Set the query state back to what it was.
queryState = parentQueryState;
}
}
private QueryState walkAnyPath(final QueryState> parentQueryState, Any node, final TableCollection tc) throws IllegalArgumentException {
final StaMainTable parentMainTable = parentQueryState.getMainTable();
final EntityType parentEntityType = parentMainTable.getEntityType();
final Path path = node.getCollection();
final List elements = path.getElements();
QueryState existsQueryState = null;
TableRef lastJoin = null;
Property firstElement = elements.get(0);
int startIdx = 0;
if (firstElement instanceof PropertyReference) {
startIdx = 1;
}
for (int idx = elements.size() - 1; idx >= startIdx; idx--) {
Property element = elements.get(idx);
if ((lastJoin == null)) {
if (element instanceof NavigationPropertyEntitySet npes) {
// Last entry in the path: the target collection.
EntityType finalType = npes.getEntityType();
final StaMainTable> tableForType = tc.getTableForType(finalType).asSecure(parentQueryState.getNextAlias(), parentQueryState.getPersistenceManager());
existsQueryState = new QueryState(tableForType, parentQueryState, node.getLambdaName());
lastJoin = existsQueryState.getTableRef();
} else {
throw new IllegalArgumentException("Path before any() MUST end in an EntitySet. Found: " + element);
}
}
if (element instanceof NavigationPropertyMain npm) {
var inverse = npm.getInverse();
if (idx == startIdx) {
// First entry in the path: Link to the main table!
if (inverse.getEntityType() != parentEntityType) {
throw new IllegalArgumentException("path of any() did not track back to main entity type. Expected " + parentEntityType + " got " + inverse.getEntityType());
}
lastJoin.createSemiJoin(inverse.getName(), parentMainTable, existsQueryState);
} else {
TableRef existingJoin = lastJoin.getJoin(inverse);
if (existingJoin != null) {
lastJoin = existingJoin;
}
lastJoin = lastJoin.createJoin(inverse.getName(), existsQueryState);
}
} else if (element instanceof EntityPropertyCustomLink) {
throw new IllegalArgumentException("Path before any() should not contain Custom Links. Found: " + element);
} else if (element instanceof EntityPropertyCustom) {
throw new IllegalArgumentException("Path before any() should not contain EntityProperties. Found: " + element);
} else if (element instanceof EntityPropertyMain) {
throw new IllegalArgumentException("Path before any() should not contain EntityProperties. Found: " + element);
} else {
throw new IllegalArgumentException("Path before any() contains unknown element. Found: " + element);
}
}
return existsQueryState;
}
@Override
public FieldWrapper visit(BooleanConstant node) {
return new SimpleFieldWrapper(Boolean.TRUE.equals(node.getValue()) ? DSL.condition("TRUE") : DSL.condition("FALSE"));
}
@Override
public FieldWrapper visit(DateConstant node) {
PlainDate date = node.getValue();
Calendar instance = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
instance.set(date.getYear(), date.getMonth() - 1, date.getDayOfMonth());
return new SimpleFieldWrapper(DSL.inline(new java.sql.Date(instance.getTimeInMillis())));
}
@Override
public FieldWrapper visit(DateTimeConstant node) {
ZonalDateTime value = node.getValue();
return new StaDateTimeWrapper(value.toMoment(), true);
}
@Override
public FieldWrapper visit(DoubleConstant node) {
return new SimpleFieldWrapper(DSL.val(node.getValue()));
}
@Override
public FieldWrapper visit(DurationConstant node) {
return new StaDurationWrapper(node);
}
@Override
public FieldWrapper visit(IntervalConstant node) {
MomentInterval value = node.getValue();
return new StaTimeIntervalWrapper(
value.getStartAsMoment(),
value.getEndAsMoment());
}
@Override
public FieldWrapper visit(IntegerConstant node) {
return new SimpleFieldWrapper(DSL.val(node.getValue()));
}
@Override
public FieldWrapper visit(LineStringConstant node) {
Geometry geom = fromGeoJsonConstant(node);
return new SimpleFieldWrapper(DSL.field(ST_GEOM_FROM_EWKT, PostGisGeometryBinding.dataType(), geom.asText()));
}
@Override
public FieldWrapper visit(NullConstant node) {
return new NullWrapper();
}
@Override
public FieldWrapper visit(PointConstant node) {
Geometry geom = fromGeoJsonConstant(node);
return new SimpleFieldWrapper(DSL.field(ST_GEOM_FROM_EWKT, PostGisGeometryBinding.dataType(), geom.asText()));
}
@Override
public FieldWrapper visit(PolygonConstant node) {
Geometry geom = fromGeoJsonConstant(node);
return new SimpleFieldWrapper(DSL.field(ST_GEOM_FROM_EWKT, PostGisGeometryBinding.dataType(), geom.asText()));
}
private static Geometry fromGeoJsonConstant(GeoJsonConstant extends GeoJsonObject> node) {
if (node.getValue().getCrs() == null) {
return Wkt.fromWkt("SRID=4326;" + node.getSource());
}
return Wkt.fromWkt(node.getSource());
}
@Override
public FieldWrapper visit(StringConstant node) {
return new SimpleFieldWrapper(DSL.value(node.getValue()));
}
@Override
public FieldWrapper visit(TimeConstant node) {
PlainTime time = node.getValue();
Calendar instance = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
instance.set(1970, 1, 1, time.getHour(), time.getMinute(), time.getSecond());
return new SimpleFieldWrapper(DSL.inline(new java.sql.Time(instance.getTimeInMillis())));
}
@Override
public FieldWrapper visit(ConstantList node) {
return new ArrayConstandFieldWrapper(node);
}
@Override
public FieldWrapper visit(Before node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof TimeFieldWrapper timeExpression) {
return timeExpression.before(p2);
}
throw new IllegalArgumentException("Before can only be used on times, not on " + p1.getClass().getName());
}
@Override
public FieldWrapper visit(After node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof TimeFieldWrapper timeExpression) {
return timeExpression.after(p2);
}
throw new IllegalArgumentException("After can only be used on times, not on " + p1.getClass().getName());
}
@Override
public FieldWrapper visit(Meets node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof TimeFieldWrapper timeExpression) {
return timeExpression.meets(p2);
}
throw new IllegalArgumentException("Meets can only be used on times, not on " + p1.getClass().getName());
}
@Override
public FieldWrapper visit(During node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p2 instanceof StaTimeIntervalWrapper ti2) {
return ti2.contains(p1);
}
throw new IllegalArgumentException("Second parameter of 'during' has to be an interval.");
}
@Override
public FieldWrapper visit(Overlaps node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof TimeFieldWrapper timeExpression) {
return timeExpression.overlaps(p2);
}
throw new IllegalArgumentException("Overlaps can only be used on times, not on " + p1.getClass().getName());
}
@Override
public FieldWrapper visit(Starts node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof TimeFieldWrapper timeExpression) {
return timeExpression.starts(p2);
}
throw new IllegalArgumentException("Starts can only be used on times, not on " + p1.getClass().getName());
}
@Override
public FieldWrapper visit(Finishes node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof TimeFieldWrapper timeExpression) {
return timeExpression.finishes(p2);
}
throw new IllegalArgumentException("Finishes can only be used on times, not on " + p1.getClass().getName());
}
@Override
public FieldWrapper visit(Add node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof TimeFieldWrapper ti1) {
return ti1.add(p2);
}
if (p2 instanceof TimeFieldWrapper ti2) {
return ti2.add(p1);
}
Field n1 = p1.getFieldAsType(Number.class, true);
Field n2 = p2.getFieldAsType(Number.class, true);
return new SimpleFieldWrapper(n1.add(n2));
}
@Override
public FieldWrapper visit(Divide node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof TimeFieldWrapper ti1) {
return ti1.div(p2);
}
if (p2 instanceof TimeFieldWrapper) {
throw new IllegalArgumentException("Can not devide by a TimeExpression.");
}
Field n1 = p1.getFieldAsType(Number.class, true);
Field n2 = p2.getFieldAsType(Number.class, true);
return new SimpleFieldWrapper(n1.divide(n2).coerce(SQLDataType.DOUBLE));
}
@Override
public FieldWrapper visit(Modulo node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
Field extends Number> n1 = p1.getFieldAsType(Number.class, true);
Field extends Number> n2 = p2.getFieldAsType(Number.class, true);
if (n1.getType().equals(Double.class)) {
n1 = n1.cast(SQLDataType.NUMERIC);
}
if (n2.getType().equals(Double.class)) {
n2 = n2.cast(SQLDataType.NUMERIC);
}
return new SimpleFieldWrapper(n1.mod(n2));
}
@Override
public FieldWrapper visit(Multiply node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof TimeFieldWrapper ti1) {
return ti1.mul(p2);
}
if (p2 instanceof TimeFieldWrapper ti2) {
return ti2.mul(p1);
}
Field n1 = p1.getFieldAsType(Number.class, true);
Field n2 = p2.getFieldAsType(Number.class, true);
return new SimpleFieldWrapper(n1.multiply(n2));
}
@Override
public FieldWrapper visit(Subtract node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof TimeFieldWrapper ti1) {
return ti1.sub(p2);
}
if (p2 instanceof TimeFieldWrapper) {
throw new IllegalArgumentException("Can not sub a time expression from a " + p1.getClass().getName());
}
Field n1 = p1.getFieldAsType(Number.class, true);
Field n2 = p2.getFieldAsType(Number.class, true);
return new SimpleFieldWrapper(n1.subtract(n2));
}
@Override
public FieldWrapper visit(Equal node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof NullWrapper) {
return new SimpleFieldWrapper(p2.getDefaultField().isNull());
}
if (p2 instanceof NullWrapper) {
return new SimpleFieldWrapper(p1.getDefaultField().isNull());
}
if (p1 instanceof TimeFieldWrapper ti1) {
return ti1.eq(p2);
}
if (p2 instanceof TimeFieldWrapper ti2) {
return ti2.eq(p1);
}
if (p1 instanceof JsonFieldFactory.JsonFieldWrapper l1) {
return l1.eq(p2);
}
if (p2 instanceof JsonFieldFactory.JsonFieldWrapper l2) {
return l2.eq(p1);
}
Field[] pair = findPair(p1, p2);
return new SimpleFieldWrapper(pair[0].eq(pair[1]));
}
@Override
public FieldWrapper visit(GreaterEqual node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof TimeFieldWrapper ti1) {
return ti1.goe(p2);
}
if (p2 instanceof TimeFieldWrapper ti2) {
return ti2.loe(p1);
}
if (p1 instanceof JsonFieldFactory.JsonFieldWrapper l1) {
return l1.goe(p2);
}
if (p2 instanceof JsonFieldFactory.JsonFieldWrapper l2) {
return l2.loe(p1);
}
Field[] pair = findPair(p1, p2);
return new SimpleFieldWrapper(pair[0].greaterOrEqual(pair[1]));
}
@Override
public FieldWrapper visit(GreaterThan node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof TimeFieldWrapper ti1) {
return ti1.gt(p2);
}
if (p2 instanceof TimeFieldWrapper ti2) {
return ti2.lt(p1);
}
if (p1 instanceof JsonFieldFactory.JsonFieldWrapper l1) {
return l1.gt(p2);
}
if (p2 instanceof JsonFieldFactory.JsonFieldWrapper l2) {
return l2.lt(p1);
}
Field[] pair = findPair(p1, p2);
return new SimpleFieldWrapper(pair[0].greaterThan(pair[1]));
}
@Override
public FieldWrapper visit(LessEqual node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof TimeFieldWrapper ti1) {
return ti1.loe(p2);
}
if (p2 instanceof TimeFieldWrapper ti2) {
return ti2.goe(p1);
}
if (p1 instanceof JsonFieldFactory.JsonFieldWrapper l1) {
return l1.loe(p2);
}
if (p2 instanceof JsonFieldFactory.JsonFieldWrapper l2) {
return l2.goe(p1);
}
Field[] pair = findPair(p1, p2);
return new SimpleFieldWrapper(pair[0].lessOrEqual(pair[1]));
}
@Override
public FieldWrapper visit(LessThan node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof TimeFieldWrapper ti1) {
return ti1.lt(p2);
}
if (p2 instanceof TimeFieldWrapper ti2) {
return ti2.gt(p1);
}
if (p1 instanceof JsonFieldFactory.JsonFieldWrapper l1) {
return l1.lt(p2);
}
if (p2 instanceof JsonFieldFactory.JsonFieldWrapper l2) {
return l2.gt(p1);
}
Field[] pair = findPair(p1, p2);
return new SimpleFieldWrapper(pair[0].lt(pair[1]));
}
@Override
public FieldWrapper visit(NotEqual node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1 instanceof NullWrapper) {
return new SimpleFieldWrapper(p2.getDefaultField().isNotNull());
}
if (p2 instanceof NullWrapper) {
return new SimpleFieldWrapper(p1.getDefaultField().isNotNull());
}
if (p1 instanceof TimeFieldWrapper ti1) {
return ti1.neq(p2);
}
if (p2 instanceof TimeFieldWrapper ti2) {
return ti2.neq(p1);
}
if (p1 instanceof JsonFieldFactory.JsonFieldWrapper l1) {
return l1.ne(p2);
}
if (p2 instanceof JsonFieldFactory.JsonFieldWrapper l2) {
return l2.ne(p1);
}
Field[] pair = findPair(p1, p2);
return new SimpleFieldWrapper(pair[0].ne(pair[1]));
}
@Override
public FieldWrapper visit(In node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p2 instanceof ArrayConstandFieldWrapper clP2) {
return new SimpleFieldWrapper(p1.getDefaultField().in(clP2.getValueList()));
}
Field[] pair = findPair(p1, p2);
if (p2 instanceof JsonFieldWrapper jP2) {
return jP2.contains(pair[0]);
} else {
return new SimpleFieldWrapper(pair[0].in(pair[1]));
}
}
@Override
public FieldWrapper visit(Date node) {
Expression param = node.getParameters().get(0);
FieldWrapper input = param.accept(this);
if (input instanceof TimeFieldWrapper timeExpression) {
return new SimpleFieldWrapper(DSL.function("date", java.sql.Date.class, timeExpression.getDateTime()));
}
Field fieldAsDate = input.getFieldAsType(java.sql.Date.class, true);
if (fieldAsDate != null) {
return new SimpleFieldWrapper(fieldAsDate);
}
throw new IllegalArgumentException("Date can only be used on times, not on " + input.getClass().getName());
}
@Override
public FieldWrapper visit(Day node) {
Expression param = node.getParameters().get(0);
FieldWrapper input = param.accept(this);
if (input instanceof TimeFieldWrapper timeExpression) {
return new SimpleFieldWrapper(DSL.extract(timeExpression.getDateTime(), DatePart.DAY));
}
throw new IllegalArgumentException("Day can only be used on times, not on " + input.getClass().getName());
}
@Override
public FieldWrapper visit(FractionalSeconds node) {
Expression param = node.getParameters().get(0);
FieldWrapper input = param.accept(this);
if (input instanceof TimeFieldWrapper timeExpression) {
return new SimpleFieldWrapper(DSL.field("(date_part('SECONDS', TIMESTAMPTZ ?) - floor(date_part('SECONDS', TIMESTAMPTZ ?)))", Double.class, timeExpression.getDateTime(), timeExpression.getDateTime()));
}
throw new IllegalArgumentException("FractionalSeconds can only be used on times, not on " + input.getClass().getName());
}
@Override
public FieldWrapper visit(Hour node) {
Expression param = node.getParameters().get(0);
FieldWrapper input = param.accept(this);
if (input instanceof TimeFieldWrapper timeExpression) {
return new SimpleFieldWrapper(DSL.extract(timeExpression.getDateTime(), DatePart.HOUR));
}
throw new IllegalArgumentException("Hour can only be used on times, not on " + input.getClass().getName());
}
@Override
public FieldWrapper visit(MaxDateTime node) {
return new StaDateTimeWrapper(PostgresPersistenceManager.DATETIME_MAX, true);
}
@Override
public FieldWrapper visit(MinDateTime node) {
return new StaDateTimeWrapper(PostgresPersistenceManager.DATETIME_MIN, true);
}
@Override
public FieldWrapper visit(Minute node) {
Expression param = node.getParameters().get(0);
FieldWrapper input = param.accept(this);
if (input instanceof TimeFieldWrapper timeExpression) {
return new SimpleFieldWrapper(DSL.extract(timeExpression.getDateTime(), DatePart.MINUTE));
}
throw new IllegalArgumentException("Minute can only be used on times, not on " + input.getClass().getName());
}
@Override
public FieldWrapper visit(Month node) {
Expression param = node.getParameters().get(0);
FieldWrapper input = param.accept(this);
if (input instanceof TimeFieldWrapper timeExpression) {
return new SimpleFieldWrapper(DSL.extract(timeExpression.getDateTime(), DatePart.MONTH));
}
throw new IllegalArgumentException("Month can only be used on times, not on " + input.getClass().getName());
}
@Override
public FieldWrapper visit(Now node) {
return new StaDateTimeWrapper(DSL.field("now()", Moment.class));
}
@Override
public FieldWrapper visit(Second node) {
Expression param = node.getParameters().get(0);
FieldWrapper input = param.accept(this);
if (input instanceof TimeFieldWrapper timeExpression) {
return new SimpleFieldWrapper(DSL.extract(timeExpression.getDateTime(), DatePart.SECOND));
}
throw new IllegalArgumentException("Second can only be used on times, not on " + input.getClass().getName());
}
@Override
public FieldWrapper visit(Time node) {
Expression param = node.getParameters().get(0);
FieldWrapper input = param.accept(this);
if (input instanceof TimeFieldWrapper timeExpression) {
return new SimpleFieldWrapper(timeExpression.getDateTime().cast(SQLDataType.TIME));
}
throw new IllegalArgumentException("Time can only be used on times, not on " + input.getClass().getName());
}
@Override
public FieldWrapper visit(TotalOffsetMinutes node) {
Expression param = node.getParameters().get(0);
FieldWrapper input = param.accept(this);
if (input instanceof TimeFieldWrapper timeExpression) {
return new SimpleFieldWrapper(DSL.extract(timeExpression.getDateTime(), DatePart.TIMEZONE).div(60));
}
throw new IllegalArgumentException("TotalOffsetMinutes can only be used on times, not on " + input.getClass().getName());
}
@Override
public FieldWrapper visit(Year node) {
Expression param = node.getParameters().get(0);
FieldWrapper input = param.accept(this);
if (input instanceof TimeFieldWrapper timeExpression) {
return new SimpleFieldWrapper(DSL.extract(timeExpression.getDateTime(), DatePart.YEAR));
}
throw new IllegalArgumentException("Year can only be used on times, not on " + input.getClass().getName());
}
@Override
public FieldWrapper visit(GeoDistance node) {
Expression p1 = node.getParameters().get(0);
Expression p2 = node.getParameters().get(1);
FieldWrapper e1 = p1.accept(this);
FieldWrapper e2 = p2.accept(this);
Field g1 = e1.getFieldAsType(Geometry.class, true);
Field g2 = e2.getFieldAsType(Geometry.class, true);
if (g1 == null || g2 == null) {
throw new IllegalArgumentException("GeoDistance requires two geometries, got " + e1 + " & " + e2);
}
return new SimpleFieldWrapper(DSL.function("ST_Distance", SQLDataType.NUMERIC, g1, g2));
}
@Override
public FieldWrapper visit(GeoIntersects node) {
return stCompare(node, "ST_Intersects");
}
@Override
public FieldWrapper visit(GeoLength node) {
Expression p1 = node.getParameters().get(0);
FieldWrapper e1 = p1.accept(this);
Field g1 = e1.getFieldAsType(Geometry.class, true);
if (g1 == null) {
throw new IllegalArgumentException("GeoLength requires a geometry, got " + e1);
}
return new SimpleFieldWrapper(DSL.function("ST_Length", SQLDataType.NUMERIC, g1));
}
@Override
public FieldWrapper visit(And node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1.isCondition() && p2.isCondition()) {
return new SimpleFieldWrapper(p1.getCondition().and(p2.getCondition()));
}
throw new IllegalArgumentException("And requires two conditions, got " + p1 + " & " + p2);
}
@Override
public FieldWrapper visit(Not node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
if (p1.isCondition()) {
return new SimpleFieldWrapper(p1.getCondition().not());
}
throw new IllegalArgumentException("Not requires a condition, got " + p1);
}
@Override
public FieldWrapper visit(Or node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
FieldWrapper p2 = params.get(1).accept(this);
if (p1.isCondition() && p2.isCondition()) {
return new SimpleFieldWrapper(p1.getCondition().or(p2.getCondition()));
}
throw new IllegalArgumentException("Or requires two conditions, got " + p1 + " & " + p2);
}
@Override
public FieldWrapper visit(Ceiling node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
Field n1 = p1.getFieldAsType(Number.class, true);
return new SimpleFieldWrapper(DSL.ceil(n1));
}
@Override
public FieldWrapper visit(Floor node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
Field n1 = p1.getFieldAsType(Number.class, true);
return new SimpleFieldWrapper(DSL.floor(n1));
}
@Override
public FieldWrapper visit(Round node) {
List params = node.getParameters();
FieldWrapper p1 = params.get(0).accept(this);
Field n1 = p1.getFieldAsType(Number.class, true);
return new SimpleFieldWrapper(DSL.round(n1));
}
private FieldWrapper stCompare(Function node, String functionName) {
Expression p1 = node.getParameters().get(0);
Expression p2 = node.getParameters().get(1);
FieldWrapper e1 = p1.accept(this);
FieldWrapper e2 = p2.accept(this);
Field g1 = e1.getFieldAsType(Geometry.class, true);
Field g2 = e2.getFieldAsType(Geometry.class, true);
if (g1 == null || g2 == null) {
throw new IllegalArgumentException(functionName + " requires two geometries, got " + e1 + " & " + e2);
}
return new SimpleFieldWrapper(DSL.condition(DSL.function(functionName, SQLDataType.BOOLEAN, g1, g2)));
}
@Override
public FieldWrapper visit(STContains node) {
return stCompare(node, "ST_Contains");
}
@Override
public FieldWrapper visit(STCrosses node) {
return stCompare(node, "ST_Crosses");
}
@Override
public FieldWrapper visit(STDisjoint node) {
return stCompare(node, "ST_Disjoint");
}
@Override
public FieldWrapper visit(STEquals node) {
return stCompare(node, "ST_Equals");
}
@Override
public FieldWrapper visit(STIntersects node) {
return stCompare(node, "ST_Intersects");
}
@Override
public FieldWrapper visit(STOverlaps node) {
return stCompare(node, "ST_Overlaps");
}
@Override
public FieldWrapper visit(STRelate node) {
Expression p1 = node.getParameters().get(0);
Expression p2 = node.getParameters().get(1);
Expression p3 = node.getParameters().get(2);
FieldWrapper e1 = p1.accept(this);
FieldWrapper e2 = p2.accept(this);
FieldWrapper e3 = p3.accept(this);
Field g1 = e1.getFieldAsType(Geometry.class, true);
Field g2 = e2.getFieldAsType(Geometry.class, true);
Field g3 = e3.getFieldAsType(String.class, true);
if (g1 == null || g2 == null || g3 == null) {
throw new IllegalArgumentException("STRelate requires two geometries and a string, got " + e1 + ", " + e2 + " & " + e3);
}
return new SimpleFieldWrapper(DSL.condition(DSL.function("ST_Relate", SQLDataType.BOOLEAN, g1, g2, g3)));
}
@Override
public FieldWrapper visit(STTouches node) {
return stCompare(node, "ST_Touches");
}
@Override
public FieldWrapper visit(STWithin node) {
return stCompare(node, "ST_Within");
}
@Override
public FieldWrapper visit(Concat node) {
Expression p1 = node.getParameters().get(0);
Expression p2 = node.getParameters().get(1);
FieldWrapper e1 = p1.accept(this);
FieldWrapper e2 = p2.accept(this);
Field s1 = e1.getFieldAsType(String.class, true);
Field s2 = e2.getFieldAsType(String.class, true);
return new SimpleFieldWrapper(s1.concat(s2));
}
@Override
public FieldWrapper visit(EndsWith node) {
Expression p1 = node.getParameters().get(0);
Expression p2 = node.getParameters().get(1);
FieldWrapper e1 = p1.accept(this);
FieldWrapper e2 = p2.accept(this);
Field s1 = e1.getFieldAsType(String.class, true);
Field s2 = e2.getFieldAsType(String.class, true);
return new SimpleFieldWrapper(s1.endsWith(s2));
}
@Override
public FieldWrapper visit(IndexOf node) {
Expression p1 = node.getParameters().get(0);
Expression p2 = node.getParameters().get(1);
FieldWrapper e1 = p1.accept(this);
FieldWrapper e2 = p2.accept(this);
Field s1 = e1.getFieldAsType(String.class, true);
Field s2 = e2.getFieldAsType(String.class, true);
return new SimpleFieldWrapper(DSL.position(s1, s2));
}
@Override
public FieldWrapper visit(Length node) {
Expression param = node.getParameters().get(0);
FieldWrapper e1 = param.accept(this);
Field s1 = e1.getFieldAsType(String.class, true);
return new SimpleFieldWrapper(DSL.length(s1));
}
@Override
public FieldWrapper visit(StartsWith node) {
Expression p1 = node.getParameters().get(0);
Expression p2 = node.getParameters().get(1);
FieldWrapper e1 = p1.accept(this);
FieldWrapper e2 = p2.accept(this);
Field s1 = e1.getFieldAsType(String.class, true);
Field s2 = e2.getFieldAsType(String.class, true);
return new SimpleFieldWrapper(s1.startsWith(s2));
}
@Override
public FieldWrapper visit(Substring node) {
List params = node.getParameters();
Expression p1 = node.getParameters().get(0);
Expression p2 = node.getParameters().get(1);
FieldWrapper e1 = p1.accept(this);
FieldWrapper e2 = p2.accept(this);
Field s1 = e1.getFieldAsType(String.class, true);
Field n2 = e2.getFieldAsType(Integer.class, true);
if (params.size() > 2) {
Expression p3 = node.getParameters().get(2);
FieldWrapper e3 = p3.accept(this);
Field n3 = e3.getFieldAsType(Integer.class, true);
return new SimpleFieldWrapper(DSL.substring(s1, n2.add(1), n3));
}
return new SimpleFieldWrapper(DSL.substring(s1, n2.add(1)));
}
@Override
public FieldWrapper visit(SubstringOf node) {
Expression p1 = node.getParameters().get(0);
Expression p2 = node.getParameters().get(1);
FieldWrapper e1 = p1.accept(this);
FieldWrapper e2 = p2.accept(this);
Field s1 = e1.getFieldAsType(String.class, true);
Field s2 = e2.getFieldAsType(String.class, true);
return new SimpleFieldWrapper(s2.contains(s1));
}
@Override
public FieldWrapper visit(ToLower node) {
Expression param = node.getParameters().get(0);
FieldWrapper input = param.accept(this);
Field field = input.getFieldAsType(String.class, true);
return new SimpleFieldWrapper(DSL.lower(field));
}
@Override
public FieldWrapper visit(ToUpper node) {
Expression param = node.getParameters().get(0);
FieldWrapper input = param.accept(this);
Field field = input.getFieldAsType(String.class, true);
return new SimpleFieldWrapper(DSL.upper(field));
}
@Override
public FieldWrapper visit(Trim node) {
Expression param = node.getParameters().get(0);
FieldWrapper input = param.accept(this);
Field field = input.getFieldAsType(String.class, true);
return new SimpleFieldWrapper(DSL.trim(field));
}
@Override
public FieldWrapper visit(PrincipalName node) {
return new SimpleFieldWrapper(DSL.value(node.getValue()));
}
@Override
public FieldWrapper visit(ContextEntityProperty node) {
return new SimpleFieldWrapper(DSL.value(node.getValue()));
}
private static class PathState {
private TableRef pathTableRef;
private List elements;
private FieldWrapper finalExpression = null;
private int curIndex;
private boolean finished = false;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy