use of com.blazebit.persistence.parser.expression.Expression in project blaze-persistence by Blazebit.
the class OrderByManager method applyFrom.
String[] applyFrom(OrderByManager orderByManager, Map<String, Integer> identifierExpressionStringMap) {
String[] identifierToUseSelectAliases = new String[identifierExpressionStringMap.size()];
for (int i = 0; i < orderByManager.orderByInfos.size(); i++) {
OrderByInfo info = orderByManager.orderByInfos.get(i);
String potentialSelectAlias = info.getExpressionString();
AliasInfo aliasInfo = orderByManager.aliasManager.getAliasInfo(potentialSelectAlias);
Expression expression;
if (aliasInfo instanceof SelectInfo) {
SelectInfo selectInfo = (SelectInfo) aliasInfo;
Integer selectItemIndex = identifierExpressionStringMap.get(selectInfo.getExpression().toString());
if (selectItemIndex != null) {
// We need to use the same alias as in the SQL because Hibernate for some reason does not resolve aliases in the order by clause of subqueries
String alias = ColumnTruncFunction.SYNTHETIC_COLUMN_PREFIX + selectItemIndex;
identifierToUseSelectAliases[selectItemIndex] = alias;
expression = new PathExpression(new PropertyExpression(alias));
} else {
// We have an order by item with an alias that is not part of the identifier expression map
Expression copiedSelectExpression = selectInfo.getExpression().copy(ExpressionCopyContext.EMPTY);
if (selectInfo.getExpression() instanceof PathExpression) {
expression = copiedSelectExpression;
} else {
String alias = aliasManager.generateRootAlias("_generated_alias");
selectManager.select(copiedSelectExpression, alias);
List<Expression> args = new ArrayList<>(2);
args.add(new PathExpression(new PropertyExpression(alias)));
args.add(new StringLiteral(alias));
expression = new FunctionExpression(AliasFunction.FUNCTION_NAME, args);
}
}
} else {
expression = info.getExpression().copy(ExpressionCopyContext.EMPTY);
}
orderBy(subqueryInitFactory.reattachSubqueries(expression, ClauseType.ORDER_BY), info.ascending, info.nullFirst);
}
return identifierToUseSelectAliases;
}
use of com.blazebit.persistence.parser.expression.Expression in project blaze-persistence by Blazebit.
the class OrderByManager method splitEmbeddables.
void splitEmbeddables(JoinVisitor joinVisitor) {
List<OrderByInfo> infos = orderByInfos;
int size = infos.size();
for (int i = 0; i < size; i++) {
final OrderByInfo orderByInfo = infos.get(i);
String potentialSelectAlias = orderByInfo.getExpressionString();
AliasInfo aliasInfo = aliasManager.getAliasInfo(potentialSelectAlias);
Expression expr;
if (aliasInfo instanceof SelectInfo) {
SelectInfo selectInfo = (SelectInfo) aliasInfo;
expr = selectInfo.getExpression();
} else {
expr = orderByInfo.getExpression();
}
List<Expression> splittedOffExpressions = embeddableSplittingVisitor.splitOff(expr, false);
if (splittedOffExpressions != null && !splittedOffExpressions.isEmpty()) {
if (!jpaProvider.supportsSingleValuedAssociationIdExpressions() && joinVisitor != null) {
ClauseType fromClause = joinVisitor.getFromClause();
try {
joinVisitor.setFromClause(ClauseType.ORDER_BY);
for (int j = 0; j < splittedOffExpressions.size(); j++) {
splittedOffExpressions.get(j).accept(joinVisitor);
}
} finally {
joinVisitor.setFromClause(fromClause);
}
}
infos.set(i, new OrderByInfo(splittedOffExpressions.get(0), orderByInfo.ascending, orderByInfo.nullFirst));
List<OrderByInfo> newOrderByInfos = new ArrayList<>(splittedOffExpressions.size() - 1);
for (int j = 1; j < splittedOffExpressions.size(); j++) {
newOrderByInfos.add(new OrderByInfo(splittedOffExpressions.get(j), orderByInfo.ascending, orderByInfo.nullFirst));
}
infos.addAll(i + 1, newOrderByInfos);
size += newOrderByInfos.size();
}
}
}
use of com.blazebit.persistence.parser.expression.Expression in project blaze-persistence by Blazebit.
the class OrderByManager method getOrderByExpressions.
List<OrderByExpression> getOrderByExpressions(boolean hasCollections, CompoundPredicate rootPredicate, Collection<ResolvedExpression> groupByClauses, JoinVisitor joinVisitor) {
if (orderByInfos.isEmpty()) {
return Collections.emptyList();
}
Set<String> clausesRequiredForResultUniqueness;
if (groupByClauses.isEmpty()) {
clausesRequiredForResultUniqueness = null;
} else {
clausesRequiredForResultUniqueness = new HashSet<>(groupByClauses.size());
for (ResolvedExpression groupByClause : groupByClauses) {
clausesRequiredForResultUniqueness.add(groupByClause.getExpressionString());
}
}
List<OrderByExpression> realExpressions = new ArrayList<>(orderByInfos.size());
List<OrderByInfo> infos = orderByInfos;
int size = infos.size();
boolean resultUnique = false;
StringBuilder expressionStringBuilder = new StringBuilder();
queryGenerator.setQueryBuffer(expressionStringBuilder);
functionalDependencyAnalyzerVisitor.clear(rootPredicate, joinManager.getRoots().get(0), true);
for (int i = 0; i < size; i++) {
final OrderByInfo orderByInfo = infos.get(i);
String expressionString = orderByInfo.getExpressionString();
AliasInfo aliasInfo = aliasManager.getAliasInfo(expressionString);
Expression expr;
if (aliasInfo instanceof SelectInfo) {
SelectInfo selectInfo = (SelectInfo) aliasInfo;
expr = selectInfo.getExpression();
if (clausesRequiredForResultUniqueness != null && !clausesRequiredForResultUniqueness.isEmpty()) {
expressionStringBuilder.setLength(0);
expr.accept(queryGenerator);
clausesRequiredForResultUniqueness.remove(expressionStringBuilder.toString());
}
} else {
expr = orderByInfo.getExpression();
if (clausesRequiredForResultUniqueness != null && !clausesRequiredForResultUniqueness.isEmpty()) {
expressionStringBuilder.setLength(0);
expr.accept(queryGenerator);
clausesRequiredForResultUniqueness.remove(expressionStringBuilder.toString());
}
}
// We analyze the model and join node structure and also detect top-level EQ predicates that constantify attributes which makes them non-null
boolean nullable = joinManager.hasFullJoin() || ExpressionUtils.isNullable(metamodel, functionalDependencyAnalyzerVisitor.getConstantifiedJoinNodeAttributeCollector(), expr);
// Since we generate the null precedence emulation expressions, we must also generate them in the uniqueness determination code
if (nullable && clausesRequiredForResultUniqueness != null && !clausesRequiredForResultUniqueness.isEmpty() && !jpaProvider.supportsNullPrecedenceExpression()) {
expressionStringBuilder.insert(0, "CASE WHEN ");
expressionStringBuilder.append(" IS NULL THEN ");
if (orderByInfo.nullFirst) {
expressionStringBuilder.append("0 ELSE 1 END");
} else {
expressionStringBuilder.append("1 ELSE 0 END");
}
clausesRequiredForResultUniqueness.remove(expressionStringBuilder.toString());
}
// Note that there are actually two notions of uniqueness that we have to check for
// There is a result uniqueness which is relevant for the safety checks we do
// and there is a also the general uniqueness which is what is relevant for keyset pagination
//
// The general uniqueness can be inferred, when a path expression refers to a unique attribute and parent joins are "uniqueness preserving"
// A join node is uniqueness preserving when it is a join of a one-to-one or more generally, when there is a top-level equality predicate between unique keys
// Detecting top-level equality predicates is out of scope right now and will be done as part of #610
// Normally, when there are multiple query roots, we can only determine uniqueness when query roots are somehow joined by a unique attributes
// Since that is out of scope now, we require that there must be a single root in order for us to detect uniqueness properly
boolean unique;
List<Expression> splitOffExpressions;
// Determining general uniqueness requires that no collection joins are involved in a query which is kind of guaranteed by design by the PaginatedCriteriaBuilder
if (!joinManager.hasFullJoin() && joinManager.getRoots().size() == 1 && !hasCollections) {
unique = functionalDependencyAnalyzerVisitor.analyzeFormsUniqueTuple(expr);
splitOffExpressions = functionalDependencyAnalyzerVisitor.getSplittedOffExpressions();
} else {
unique = false;
splitOffExpressions = embeddableSplittingVisitor.splitOff(expr, false);
}
resultUnique = !joinManager.hasFullJoin() && (resultUnique || unique || clausesRequiredForResultUniqueness != null && clausesRequiredForResultUniqueness.isEmpty());
boolean resUnique = resultUnique || (i + 1) == size && functionalDependencyAnalyzerVisitor.isResultUnique();
if (splitOffExpressions == null || splitOffExpressions.isEmpty()) {
realExpressions.add(new OrderByExpression(orderByInfo.ascending, orderByInfo.nullFirst, expr, nullable, unique, resUnique));
} else {
if (jpaProvider.supportsSingleValuedAssociationIdExpressions() || joinVisitor == null) {
for (Expression splitOffExpression : splitOffExpressions) {
realExpressions.add(new OrderByExpression(orderByInfo.ascending, orderByInfo.nullFirst, splitOffExpression, nullable, unique, resUnique));
}
} else {
for (Expression splitOffExpression : splitOffExpressions) {
splitOffExpression.accept(joinVisitor);
realExpressions.add(new OrderByExpression(orderByInfo.ascending, orderByInfo.nullFirst, splitOffExpression, nullable, unique, resUnique));
}
}
}
}
queryGenerator.setQueryBuffer(null);
return realExpressions;
}
use of com.blazebit.persistence.parser.expression.Expression in project blaze-persistence by Blazebit.
the class OrderByManager method buildImplicitGroupByClauses.
/**
* Builds the clauses needed for the group by clause for a query that uses aggregate functions to work.
*
* @return
*/
void buildImplicitGroupByClauses(GroupByManager groupByManager, boolean hasGroupBy, JoinVisitor joinVisitor) {
if (orderByInfos.isEmpty()) {
return;
}
SimpleQueryGenerator.BooleanLiteralRenderingContext oldBooleanLiteralRenderingContext = queryGenerator.setBooleanLiteralRenderingContext(SimpleQueryGenerator.BooleanLiteralRenderingContext.CASE_WHEN);
StringBuilder sb = new StringBuilder();
List<OrderByInfo> infos = orderByInfos;
boolean hasFullJoin = !jpaProvider.supportsNullPrecedenceExpression() && joinManager.hasFullJoin();
int size = infos.size();
for (int i = 0; i < size; i++) {
final OrderByInfo orderByInfo = infos.get(i);
String potentialSelectAlias = orderByInfo.getExpressionString();
AliasInfo aliasInfo = aliasManager.getAliasInfo(potentialSelectAlias);
Expression expr;
if (aliasInfo instanceof SelectInfo) {
SelectInfo selectInfo = (SelectInfo) aliasInfo;
expr = selectInfo.getExpression();
} else {
expr = orderByInfo.getExpression();
}
Set<Expression> extractedGroupByExpressions = groupByExpressionGatheringVisitor.extractGroupByExpressions(expr, getClauseType());
if (!extractedGroupByExpressions.isEmpty()) {
queryGenerator.setClauseType(ClauseType.GROUP_BY);
queryGenerator.setQueryBuffer(sb);
for (Expression extractedExpression : extractedGroupByExpressions) {
sb.setLength(0);
queryGenerator.generate(extractedExpression);
String expressionString = sb.toString();
if (!jpaProvider.supportsNullPrecedenceExpression()) {
boolean nullable = hasFullJoin || ExpressionUtils.isNullable(metamodel, functionalDependencyAnalyzerVisitor.getConstantifiedJoinNodeAttributeCollector(), extractedExpression);
Expression resultExpression;
Expression defaultExpression;
if (nullable) {
sb.insert(0, "CASE WHEN ");
sb.append(" IS NULL THEN ");
if (orderByInfo.nullFirst) {
resultExpression = new NumericLiteral("0", NumericType.INTEGER);
defaultExpression = new NumericLiteral("1", NumericType.INTEGER);
sb.append("0 ELSE 1 END");
} else {
resultExpression = new NumericLiteral("1", NumericType.INTEGER);
defaultExpression = new NumericLiteral("0", NumericType.INTEGER);
sb.append("1 ELSE 0 END");
}
List<WhenClauseExpression> whenClauses = new ArrayList<>(1);
whenClauses.add(new WhenClauseExpression(new IsNullPredicate(extractedExpression.copy(ExpressionCopyContext.CLONE)), resultExpression));
Expression nullEmulationExpression = new GeneralCaseExpression(whenClauses, defaultExpression);
String nullPrecedenceEmulationExpression = sb.toString();
groupByManager.collect(new ResolvedExpression(nullPrecedenceEmulationExpression, nullEmulationExpression), ClauseType.ORDER_BY, hasGroupBy, joinVisitor);
}
}
groupByManager.collect(new ResolvedExpression(expressionString, extractedExpression), ClauseType.ORDER_BY, hasGroupBy, joinVisitor);
}
queryGenerator.setClauseType(null);
}
}
queryGenerator.setBooleanLiteralRenderingContext(oldBooleanLiteralRenderingContext);
groupByExpressionGatheringVisitor.clear();
}
use of com.blazebit.persistence.parser.expression.Expression in project blaze-persistence by Blazebit.
the class JpaUtils method getAttributeForJoining.
public static AttributeHolder getAttributeForJoining(EntityMetamodel metamodel, PathExpression expression) {
JoinNode expressionBaseNode = ((JoinNode) expression.getPathReference().getBaseNode());
Expression p = expression.getExpressions().get(0);
while (!(p instanceof PropertyExpression)) {
if (p instanceof PathExpression) {
p = ((PathExpression) p).getExpressions().get(0);
} else if (p instanceof QualifiedExpression) {
p = ((QualifiedExpression) p).getPath().getExpressions().get(0);
} else if (p instanceof ArrayExpression) {
p = ((ArrayExpression) p).getBase();
} else {
p = ((TreatExpression) p).getExpression();
}
}
String firstElementString = p.toString();
String baseNodeAlias;
JoinNode baseNode = expressionBaseNode;
do {
baseNodeAlias = baseNode.getAlias();
} while (!firstElementString.equals(baseNodeAlias) && (baseNode = baseNode.getParent()) != null);
if (baseNode == null) {
baseNodeAlias = null;
if (expressionBaseNode.getParent() == null) {
baseNode = expressionBaseNode;
} else {
baseNode = expressionBaseNode.getParent();
}
}
return getAttributeForJoining(metamodel, baseNode.getNodeType(), expression, baseNodeAlias);
}
Aggregations