use of com.blazebit.persistence.FinalSetOperationCTECriteriaBuilder in project blaze-persistence by Blazebit.
the class BlazeCriteriaBuilderRenderer method renderJoins.
private <X extends FromBuilder<X>> X renderJoins(QueryMetadata metadata, FromBaseBuilder<X> fromBuilder) {
X criteriaBuilder = null;
for (final JoinExpression joinExpression : metadata.getJoins()) {
boolean fetch = joinExpression.hasFlag(JPAQueryMixin.FETCH);
boolean hasCondition = joinExpression.getCondition() != null;
Expression<?> target = joinExpression.getTarget();
String alias = null;
if (target instanceof Operation<?>) {
Operation<?> operation = (Operation<?>) target;
if (operation.getOperator() == Ops.ALIAS) {
target = operation.getArg(0);
alias = ((Path<?>) operation.getArg(1)).getMetadata().getName();
}
}
if (target instanceof ValuesExpression<?>) {
ValuesExpression<?> valuesExpression = (ValuesExpression<?>) target;
Class type = valuesExpression.getRoot().getType();
String name = valuesExpression.getAlias().getMetadata().getName();
Collection<?> elements = valuesExpression.getElements();
if (!valuesExpression.getMetadata().isRoot()) {
String attribute = relativePathString(valuesExpression.getRoot(), valuesExpression);
if (valuesExpression.isIdentifiable()) {
criteriaBuilder = (X) fromBuilder.fromIdentifiableValues(type, attribute, name, elements);
} else {
criteriaBuilder = (X) fromBuilder.fromValues(type, attribute, name, elements);
}
} else if (valuesExpression.isIdentifiable()) {
criteriaBuilder = (X) fromBuilder.fromIdentifiableValues(type, name, elements);
} else {
criteriaBuilder = (X) fromBuilder.fromValues(type, name, elements);
}
} else if (target instanceof Path<?>) {
Path<?> entityPath = (Path<?>) target;
boolean entityJoin = entityPath.getMetadata().isRoot();
String renderedExpression = renderExpression(entityPath);
// While this looks suspicious, this is actually in line with Querydsl's default behaviour in JPQLSerializer.handleJoinTarget
if (alias == null && entityJoin) {
alias = renderedExpression;
}
switch(joinExpression.getType()) {
case DEFAULT:
if (fromBuilder instanceof FromBuilder) {
criteriaBuilder = (X) fromBuilder;
From from = criteriaBuilder.getFrom(alias);
// TODO find a clean way to detect FROM clauses that are already set by fromEntitySubquery...
if (from != null) {
if (entityPath instanceof CollectionExpressionBase<?, ?> && ((CollectionExpressionBase<?, ?>) entityPath).getElementType().equals(from.getJavaType()) || from.getJavaType().equals(entityPath.getType())) {
break;
}
}
}
if (entityJoin) {
criteriaBuilder = fromBuilder.from(entityPath.getType(), alias);
} else {
if (fromBuilder instanceof BaseSubqueryBuilder) {
criteriaBuilder = (X) ((BaseSubqueryBuilder<?>) fromBuilder).from(renderedExpression, alias);
} else if (fromBuilder instanceof SubqueryInitiator<?>) {
criteriaBuilder = (X) ((SubqueryInitiator<?>) fromBuilder).from(renderedExpression, alias);
} else {
throw new IllegalArgumentException(renderedExpression + " join not supported here");
}
}
break;
default:
JoinType joinType = getJoinType(joinExpression);
if (hasCondition && fetch) {
LOG.warning("Fetch is ignored due to on-clause");
}
if (entityJoin) {
if (!hasCondition) {
throw new IllegalStateException("No on-clause for entity join!");
}
final JoinOnBuilder<X> xJoinOnBuilder = criteriaBuilder.joinOn(entityPath.getType(), alias, joinType);
setExpressionSubqueries(joinExpression.getCondition(), null, xJoinOnBuilder, JoinOnBuilderExpressionSetter.INSTANCE);
} else if (!hasCondition) {
// If there is no alias, assume a default join
boolean defaultJoin = alias == null || joinExpression.hasFlag(AbstractBlazeJPAQuery.DEFAULT);
if (fetch) {
if (defaultJoin) {
((FullQueryBuilder<?, ?>) criteriaBuilder).joinDefault(renderedExpression, alias, joinType, fetch);
} else {
((FullQueryBuilder<?, ?>) criteriaBuilder).join(renderedExpression, alias, joinType, fetch);
}
} else {
if (defaultJoin) {
criteriaBuilder.joinDefault(renderedExpression, alias, joinType);
} else {
criteriaBuilder.join(renderedExpression, alias, joinType);
}
}
} else {
if (alias == null) {
throw new IllegalArgumentException("This association join requires an alias, like so: .join(" + renderedExpression + ", " + entityPath.getClass().getSimpleName() + "." + entityPath.getMetadata().getName() + ")");
}
final JoinOnBuilder<X> xJoinOnBuilder = criteriaBuilder.joinOn(renderedExpression, alias, joinType);
setExpressionSubqueries(joinExpression.getCondition(), null, xJoinOnBuilder, JoinOnBuilderExpressionSetter.INSTANCE);
}
break;
}
} else if (target instanceof SubQueryExpression) {
switch(joinExpression.getType()) {
case DEFAULT:
{
FullSelectCTECriteriaBuilder<X> xFullSelectCTECriteriaBuilder = fromBuilder.fromSubquery(target.getType(), alias);
Object o = serializeSubQuery(xFullSelectCTECriteriaBuilder, target);
criteriaBuilder = o instanceof FinalSetOperationCTECriteriaBuilder ? ((FinalSetOperationCTECriteriaBuilder<X>) o).end() : ((FullSelectCTECriteriaBuilder<X>) o).end();
break;
}
default:
{
JoinType joinType = getJoinType(joinExpression);
boolean isLateral = joinExpression.hasFlag(AbstractBlazeJPAQuery.LATERAL);
if (fetch) {
LOG.warning("Fetch is ignored due to subquery entity join!");
}
SubQueryExpression<?> subQueryExpression = target.accept(new FirstSubqueryResolver(), null);
Path<?> fromPath = subQueryExpression.getMetadata().getJoins().get(0).getTarget().accept(new FirstSubqueryTargetPathResolver(), null);
boolean entityJoin = fromPath.getMetadata().isRoot();
if (hasCondition) {
FullSelectCTECriteriaBuilder<JoinOnBuilder<X>> joinOnBuilderFullSelectCTECriteriaBuilder;
if (isLateral) {
String subqueryAlias = subQueryExpression.getMetadata().getJoins().get(0).getTarget().accept(new JoinTargetAliasPathResolver(), null).getMetadata().getName();
if (entityJoin) {
joinOnBuilderFullSelectCTECriteriaBuilder = criteriaBuilder.joinLateralOnSubquery(target.getType(), alias, joinType);
} else {
joinOnBuilderFullSelectCTECriteriaBuilder = criteriaBuilder.joinLateralOnSubquery(renderExpression(fromPath), alias, subqueryAlias, joinType);
}
} else {
if (!entityJoin) {
throw new IllegalStateException("Entity join to association");
}
joinOnBuilderFullSelectCTECriteriaBuilder = criteriaBuilder.joinOnSubquery(target.getType(), alias, joinType);
}
Object o = serializeSubQuery(joinOnBuilderFullSelectCTECriteriaBuilder, target);
final JoinOnBuilder<X> joinOnBuilder = o instanceof FinalSetOperationCTECriteriaBuilder ? ((FinalSetOperationCTECriteriaBuilder<JoinOnBuilder<X>>) o).end() : ((FullSelectCTECriteriaBuilder<JoinOnBuilder<X>>) o).end();
criteriaBuilder = (X) setExpressionSubqueries(joinExpression.getCondition(), null, joinOnBuilder, JoinOnBuilderExpressionSetter.INSTANCE);
} else {
if (isLateral) {
FullSelectCTECriteriaBuilder<X> xFullSelectCTECriteriaBuilder;
String subqueryAlias = subQueryExpression.getMetadata().getJoins().get(0).getTarget().accept(new JoinTargetAliasPathResolver(), null).getMetadata().getName();
if (entityJoin) {
xFullSelectCTECriteriaBuilder = criteriaBuilder.joinLateralSubquery(target.getType(), alias, joinType);
} else {
xFullSelectCTECriteriaBuilder = criteriaBuilder.joinLateralSubquery(renderExpression(fromPath), alias, subqueryAlias, joinType);
}
Object o = serializeSubQuery(xFullSelectCTECriteriaBuilder, target);
criteriaBuilder = o instanceof FinalSetOperationCTECriteriaBuilder ? ((FinalSetOperationCTECriteriaBuilder<X>) o).end() : ((FullSelectCTECriteriaBuilder<X>) o).end();
} else {
throw new IllegalStateException("No on-clause for subquery entity join!");
}
}
break;
}
}
} else {
throw new UnsupportedOperationException("Joins for " + target + " is not yet implemented");
}
}
return criteriaBuilder;
}
use of com.blazebit.persistence.FinalSetOperationCTECriteriaBuilder in project blaze-persistence by Blazebit.
the class BlazeCriteriaBuilderRenderer method serializeSubQuery.
private Object serializeSubQuery(Object criteriaBuilder, Expression<?> expression) {
Object result = expression.accept(new Visitor<Object, Object>() {
@Override
public Object visit(Constant<?> constant, Object criteriaBuilder) {
throw new UnsupportedOperationException();
}
@Override
public Object visit(FactoryExpression<?> factoryExpression, Object criteriaBuilder) {
throw new UnsupportedOperationException();
}
@Override
public Object visit(Operation<?> setOperation, Object criteriaBuilder) {
Expression<?> lhs = setOperation.getArg(0);
SubQueryExpression<?> lhsSubquery = lhs.accept(GetSubQueryVisitor.INSTANCE, null);
SetOperationFlag setOperationFlag = lhsSubquery != null ? getSetOperationFlag(lhsSubquery.getMetadata()) : null;
boolean lhsNestedSet = setOperationFlag != null && LEFT_NESTED_SET_OPERATIONS.contains(setOperation.getOperator());
if (lhsNestedSet) {
if (criteriaBuilder instanceof StartOngoingSetOperationBuilder) {
StartOngoingSetOperationBuilder<?, ?, ?> ob = (StartOngoingSetOperationBuilder<?, ?, ?>) criteriaBuilder;
criteriaBuilder = ob.startSet();
} else if (criteriaBuilder instanceof SubqueryInitiator) {
SubqueryInitiator<?> subqueryInitiator = (SubqueryInitiator<?>) criteriaBuilder;
criteriaBuilder = subqueryInitiator.startSet();
} else {
criteriaBuilder = criteriaBuilderFactory.startSet(entityManager, Object.class);
}
criteriaBuilder = setOperationFlag.getFlag().accept(this, criteriaBuilder);
if (criteriaBuilder instanceof OngoingSetOperationBuilder) {
criteriaBuilder = ((OngoingSetOperationBuilder<?, ?, ?>) criteriaBuilder).endSetWith();
renderOrderBy(lhsSubquery.getMetadata(), (OrderByBuilder<?>) criteriaBuilder);
renderModifiers(lhsSubquery.getMetadata().getModifiers(), (LimitBuilder<?>) criteriaBuilder);
criteriaBuilder = ((BaseOngoingFinalSetOperationBuilder) criteriaBuilder).endSet();
} else {
throw new UnsupportedOperationException();
}
} else {
criteriaBuilder = lhs.accept(this, criteriaBuilder);
}
Expression<?> rhs = setOperation.getArg(1);
SubQueryExpression<?> rhsSubquery = rhs.accept(GetSubQueryVisitor.INSTANCE, null);
setOperationFlag = rhsSubquery != null ? getSetOperationFlag(rhsSubquery.getMetadata()) : null;
boolean isNestedSet = setOperationFlag != null;
SetOperationBuilder<?, ?> setOperationBuilder = (SetOperationBuilder<?, ?>) criteriaBuilder;
switch((JPQLNextOps) setOperation.getOperator()) {
// CHECKSTYLE:OFF: FallThrough
case SET_UNION:
case LEFT_NESTED_SET_UNION:
criteriaBuilder = isNestedSet ? setOperationBuilder.startUnion() : setOperationBuilder.union();
break;
case SET_UNION_ALL:
case LEFT_NESTED_SET_UNION_ALL:
criteriaBuilder = isNestedSet ? setOperationBuilder.startUnionAll() : setOperationBuilder.unionAll();
break;
case SET_EXCEPT:
case LEFT_NESTED_SET_EXCEPT:
criteriaBuilder = isNestedSet ? setOperationBuilder.startExcept() : setOperationBuilder.except();
break;
case SET_EXCEPT_ALL:
case LEFT_NESTED_SET_EXCEPT_ALL:
criteriaBuilder = isNestedSet ? setOperationBuilder.startExceptAll() : setOperationBuilder.exceptAll();
break;
case SET_INTERSECT:
case LEFT_NESTED_SET_INTERSECT:
criteriaBuilder = isNestedSet ? setOperationBuilder.startIntersect() : setOperationBuilder.intersect();
break;
case SET_INTERSECT_ALL:
case LEFT_NESTED_SET_INTERSECT_ALL:
criteriaBuilder = isNestedSet ? setOperationBuilder.startIntersectAll() : setOperationBuilder.intersectAll();
break;
default:
throw new UnsupportedOperationException("No support for set operation " + setOperation.getOperator());
}
if (isNestedSet) {
criteriaBuilder = setOperationFlag.getFlag().accept(this, criteriaBuilder);
if (criteriaBuilder instanceof OngoingSetOperationBuilder) {
criteriaBuilder = ((OngoingSetOperationBuilder<?, ?, ?>) criteriaBuilder).endSetWith();
renderOrderBy(rhsSubquery.getMetadata(), (OrderByBuilder<?>) criteriaBuilder);
renderModifiers(rhsSubquery.getMetadata().getModifiers(), (LimitBuilder<?>) criteriaBuilder);
criteriaBuilder = ((BaseOngoingFinalSetOperationBuilder) criteriaBuilder).endSet();
} else {
throw new UnsupportedOperationException();
}
} else {
criteriaBuilder = rhs.accept(this, criteriaBuilder);
}
return criteriaBuilder;
}
@Override
public Object visit(ParamExpression<?> paramExpression, Object criteriaBuilder) {
throw new UnsupportedOperationException();
}
@Override
public Object visit(Path<?> path, Object criteriaBuilder) {
throw new UnsupportedOperationException();
}
@Override
public Object visit(SubQueryExpression<?> subQuery, Object criteriaBuilder) {
QueryMetadata subQueryMetadata = subQuery.getMetadata();
SetOperationFlag setOperationFlag = getSetOperationFlag(subQueryMetadata);
if (setOperationFlag != null) {
return setOperationFlag.getFlag().accept(this, criteriaBuilder);
}
renderCTEs(subQueryMetadata);
criteriaBuilder = renderJoins(subQueryMetadata, (FromBaseBuilder) criteriaBuilder);
criteriaBuilder = renderNamedWindows(subQueryMetadata, (WindowContainerBuilder) criteriaBuilder);
renderDistinct(subQueryMetadata, (DistinctBuilder<?>) criteriaBuilder);
renderWhere(subQueryMetadata, (WhereBuilder<?>) criteriaBuilder);
renderGroupBy(subQueryMetadata, (GroupByBuilder<?>) criteriaBuilder);
renderHaving(subQueryMetadata, (HavingBuilder<?>) criteriaBuilder);
Expression<?> select = subQueryMetadata.getProjection();
if (select instanceof FactoryExpression<?> && criteriaBuilder instanceof FullQueryBuilder<?, ?>) {
FactoryExpression<T> factoryExpression = (FactoryExpression<T>) select;
FullQueryBuilder<?, ?> fullQueryBuilder = (FullQueryBuilder<?, ?>) criteriaBuilder;
criteriaBuilder = fullQueryBuilder.selectNew(new FactoryExpressionObjectBuilder(factoryExpression));
} else {
List<? extends Expression<?>> projection = expandProjection(subQueryMetadata.getProjection());
if (criteriaBuilder instanceof SelectBaseCTECriteriaBuilder) {
SelectBaseCTECriteriaBuilder<?> selectBaseCriteriaBuilder = (SelectBaseCTECriteriaBuilder<?>) criteriaBuilder;
boolean bindEntity = projection.size() == 1 && subQueryMetadata.getJoins().get(0).getTarget().accept(new JoinTargetAliasPathResolver(), null).equals(projection.get(0));
if (bindEntity) {
EntityMetamodel metamodel = criteriaBuilderFactory.getService(EntityMetamodel.class);
Path<?> pathExpression = (Path<?>) projection.get(0);
ExtendedManagedType<?> managedType = metamodel.getManagedType(ExtendedManagedType.class, pathExpression.getType());
Map<String, ? extends ExtendedAttribute<?, ?>> ownedSingularAttributes = managedType.getOwnedSingularAttributes();
for (Map.Entry<String, ? extends ExtendedAttribute<?, ?>> ownedSingularAttribute : ownedSingularAttributes.entrySet()) {
String attributeName = ownedSingularAttribute.getKey();
ExtendedAttribute<?, ?> attribute = ownedSingularAttribute.getValue();
if (!JpaMetamodelUtils.isAssociation(attribute.getAttribute())) {
final SelectBuilder<?> bindBuilder = selectBaseCriteriaBuilder.bind(attributeName);
BeanPath<?> beanPath = new BeanPath<Object>(attribute.getElementClass(), pathExpression, attributeName);
setExpressionSubqueries(beanPath, null, bindBuilder, SelectBuilderExpressionSetter.INSTANCE);
}
}
} else {
for (int i = 0; i < projection.size(); i++) {
Expression<?> projExpression = projection.get(i);
BindResolverContext bindResolverContext = new BindResolverContext();
projExpression = projExpression.accept(BindResolver.INSTANCE, bindResolverContext);
Path<?> cteAttribute = bindResolverContext.getCteAttribute();
String alias = bindResolverContext.getAliasString();
if (cteAttribute == null && cteAliases != null) {
cteAttribute = cteAliases.get(i);
}
if (cteAttribute != null) {
Path<?> cteEntityPath = cteAttribute.getRoot();
String relativeCteAttributePath = relativePathString(cteEntityPath, cteAttribute);
final SelectBuilder<?> bindBuilder = selectBaseCriteriaBuilder.bind(relativeCteAttributePath);
setExpressionSubqueries(projExpression, alias, bindBuilder, SelectBuilderExpressionSetter.INSTANCE);
} else {
throw new UnsupportedOperationException("Select statement should be bound to any CTE attribute");
}
}
}
} else {
for (Expression<?> selection : projection) {
renderSingleSelect(selection, (SelectBuilder<?>) criteriaBuilder);
}
}
}
renderOrderBy(subQueryMetadata, (OrderByBuilder<?>) criteriaBuilder);
renderParameters(subQueryMetadata, (ParameterHolder<?>) criteriaBuilder);
// Limit / offset on full query is set outside of the renderer, based on whether we're rendering a full count query or not
if (!(criteriaBuilder instanceof Queryable)) {
renderModifiers(subQueryMetadata.getModifiers(), (LimitBuilder<?>) criteriaBuilder);
}
return criteriaBuilder;
}
@Override
public Object visit(TemplateExpression<?> templateExpression, Object criteriaBuilder) {
throw new UnsupportedOperationException();
}
}, criteriaBuilder);
if (result instanceof BaseOngoingSetOperationBuilder) {
result = ((BaseOngoingSetOperationBuilder<?, ?, ?>) result).endSet();
}
if ((result instanceof FinalSetOperationCriteriaBuilder || result instanceof FinalSetOperationCTECriteriaBuilder || result instanceof FinalSetOperationSubqueryBuilder) && expression instanceof SubQueryExpression<?>) {
QueryMetadata metadata = ((SubQueryExpression<?>) expression).getMetadata();
renderOrderBy(metadata, (OrderByBuilder<?>) result);
// Limit / offset on full query is set outside of the renderer, based on whether we're rendering a full count query or not
if (!(criteriaBuilder instanceof Queryable)) {
renderModifiers(metadata.getModifiers(), (LimitBuilder<?>) result);
}
}
return result;
}
Aggregations