com.sap.cds.ql.impl.CqnAnalyzerImpl Maven / Gradle / Ivy
/************************************************************************
* © 2019-2023 SAP SE or an SAP affiliate company. All rights reserved. *
************************************************************************/
package com.sap.cds.ql.impl;
import static com.sap.cds.util.CdsModelUtils.entity;
import static com.sap.cds.util.CdsModelUtils.isContextElementRef;
import static com.sap.cds.util.CqnStatementUtils.resolveStar;
import static com.sap.cds.util.CqnStatementUtils.selectedRefs;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.stream.Collectors.toList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.IntStream;
import com.sap.cds.impl.builder.model.Conjunction;
import com.sap.cds.impl.util.Stack;
import com.sap.cds.ql.cqn.AnalysisResult;
import com.sap.cds.ql.cqn.CqnAnalyzer.CqnAnalyzerSPI;
import com.sap.cds.ql.cqn.CqnComparisonPredicate;
import com.sap.cds.ql.cqn.CqnConnectivePredicate;
import com.sap.cds.ql.cqn.CqnConnectivePredicate.Operator;
import com.sap.cds.ql.cqn.CqnContainmentTest;
import com.sap.cds.ql.cqn.CqnDelete;
import com.sap.cds.ql.cqn.CqnElementRef;
import com.sap.cds.ql.cqn.CqnInPredicate;
import com.sap.cds.ql.cqn.CqnListValue;
import com.sap.cds.ql.cqn.CqnNegation;
import com.sap.cds.ql.cqn.CqnPredicate;
import com.sap.cds.ql.cqn.CqnReference.Segment;
import com.sap.cds.ql.cqn.CqnSelect;
import com.sap.cds.ql.cqn.CqnSelectListItem;
import com.sap.cds.ql.cqn.CqnSelectListValue;
import com.sap.cds.ql.cqn.CqnSource;
import com.sap.cds.ql.cqn.CqnStatement;
import com.sap.cds.ql.cqn.CqnStructuredTypeRef;
import com.sap.cds.ql.cqn.CqnUpdate;
import com.sap.cds.ql.cqn.CqnValue;
import com.sap.cds.ql.cqn.CqnVisitor;
import com.sap.cds.ql.cqn.ResolvedRefItem;
import com.sap.cds.ql.cqn.ResolvedSegment;
import com.sap.cds.reflect.CdsElement;
import com.sap.cds.reflect.CdsEntity;
import com.sap.cds.reflect.CdsModel;
import com.sap.cds.util.CdsModelUtils;
import com.sap.cds.util.CqnStatementUtils;
public class CqnAnalyzerImpl implements CqnAnalyzerSPI {
private static final String COUNT = "COUNT";
private static final String COUNT_DISTINCT = "countDistinct";
private static final Object AMBIGUOUS = new Object();
private static final Map GIVE_UP = new HashMap<>();
@Override
public boolean isCountQuery(CqnStatement cqn) {
if (cqn.isSelect() && cqn.asSelect().items().size() == 1) {
CqnSelectListItem item = cqn.asSelect().items().get(0);
if (item.isValue()) {
CqnValue val = item.asValue().value();
if (val.isFunction()) {
String func = val.asFunction().func();
return COUNT.equals(func.toUpperCase(Locale.US)) || COUNT_DISTINCT.equals(func);
}
}
}
return false;
}
@Override
public AnalysisResult analyze(CdsModel model, CqnStructuredTypeRef ref) {
List segments = resolveSegments(model, ref.segments());
return new Result(segments, Optional.empty());
}
@Override
public AnalysisResult analyze(CdsModel model, CqnSelect select) {
List segments = resolveSegments(model, select.ref().segments());
return new Result(segments, select.where());
}
@Override
public AnalysisResult analyze(CdsModel model, CqnUpdate update) {
List segments = resolveSegments(model, update.ref().segments());
return new Result(segments, update.where());
}
@Override
public AnalysisResult analyze(CdsModel model, CqnDelete delete) {
List segments = resolveSegments(model, delete.ref().segments());
return new Result(segments, delete.where());
}
@Override
public Iterable resolveRefItems(CdsModel model, CqnSelect query) {
return resolveRefItems(model, query, i -> true);
}
private static List resolveRefItems(CdsModel model, CqnSelect query,
Predicate filter) {
CqnSource from = query.from();
if (from.isRef()) {
CdsEntity target = entity(model, from.asRef());
query = resolveStar(query, target);
return selectedRefs(query).filter(filter).map(r -> new ResolvedRef(model, target, r)).collect(toList());
}
return emptyList();
}
private List resolveSegments(CdsModel model, List extends Segment> segments) {
List entities = CdsModelUtils.entities(model, segments);
return IntStream.range(0, segments.size())
.mapToObj(i -> new ResolvedSeg(segments.get(i), entities.get(i), Optional.empty())).collect(toList());
}
public static Map extractKeys(Segment seg, CdsEntity entity) {
Optional filter = seg.filter();
if (filter.isPresent()) {
return new ResolvedSeg(seg, entity, filter).keys();
}
return emptyMap();
}
private static class ResolvedRef implements ResolvedRefItem {
private final CdsModel model;
private final CdsEntity entity;
private final CqnSelectListValue value;
private final CqnElementRef ref;
private ResolvedRef(CdsModel model, CdsEntity entity, CqnSelectListValue value) {
this.model = model;
this.entity = entity;
this.value = value;
this.ref = value.value().asRef();
}
@Override
public String displayName() {
return value.displayName();
}
@Override
public CqnElementRef ref() {
return ref;
}
@Override
public CdsElement element() {
return CdsModelUtils.element(entity, ref);
}
@Override
public Optional origin() {
Optional projection = entity.query();
if (projection.isPresent()) {
List refs = resolveRefItems(model, projection.get(),
i -> i.displayName().equals(ref.lastSegment()));
if (!refs.isEmpty()) {
return Optional.of(refs.get(0));
}
}
return Optional.empty();
}
}
private static class Result extends PathImpl implements AnalysisResult {
private final Optional where;
public Result(List segments, Optional where) {
super(new LinkedList<>(segments));
this.where = where;
}
@Override
public CdsEntity rootEntity() {
return root().entity();
}
@Override
public Map rootKeys() {
return root().keys();
}
@Override
public CdsEntity targetEntity() {
return target().entity();
}
@Override
public Map targetKeys() {
return target(ResolvedSegment::keys);
}
@Override
public Map targetKeyValues() {
return target(ResolvedSegment::keyValues);
}
@Override
public Map targetValues() {
return target(ResolvedSegment::values);
}
private Map target(Function> filter) {
ResolvedSegment target = target();
if (where.isPresent()) {
target = new ResolvedSeg(target.segment(), target.entity(), where);
}
return filter.apply(target);
}
}
private static class ResolvedSeg extends PathImpl.ResolvedSegImpl {
private final CqnPredicate filter;
private ResolvedSeg(Segment segment, CdsEntity entity, Optional where) {
super(segment, entity);
CqnPredicate combined = Conjunction.and(segment.filter(), where).orElse(null);
filter = CqnStatementUtils.simplifyPredicate(combined);
}
@Override
public Map values() {
if (filter == null || filter.isLiteral() && filter.asLiteral().isBoolean()) {
return new HashMap<>();
}
ValueExtractor extractor = new ValueExtractor();
filter.accept(extractor);
return extractor.values();
}
}
private static final class ValueExtractor implements CqnVisitor {
private Stack
© 2015 - 2025 Weber Informatics LLC | Privacy Policy