use of com.blazebit.persistence.impl.query.EntityFunctionNode in project blaze-persistence by Blazebit.
the class BaseFinalSetOperationBuilderImpl method getTypedQuery.
@Override
@SuppressWarnings("unchecked")
protected TypedQuery<T> getTypedQuery(StringBuilder lateralSb, JoinNode lateralJoinNode) {
if (lateralSb != null) {
throw new IllegalStateException("Lateral join with set operations is not yet supported!");
}
Set<String> parameterListNames = new HashSet<String>();
Query leftMostQuery = setOperationManager.getStartQueryBuilder().getTypedQueryForFinalOperationBuilder();
Query baseQuery;
parameterManager.collectParameterListNames(leftMostQuery, parameterListNames);
Query q = leftMostQuery;
if (leftMostQuery instanceof TypedQueryWrapper<?>) {
q = ((TypedQueryWrapper<?>) leftMostQuery).getDelegate();
}
if (q instanceof AbstractCustomQuery<?>) {
AbstractCustomQuery<?> customQuery = (AbstractCustomQuery<?>) q;
List<Query> customQueryParticipants = customQuery.getParticipatingQueries();
baseQuery = customQueryParticipants.get(0);
} else {
baseQuery = q;
}
List<Query> setOperands = new ArrayList<Query>();
for (AbstractCommonQueryBuilder<?, ?, ?, ?, ?> setOperand : setOperationManager.getSetOperations()) {
q = setOperand.getQuery();
setOperands.add(q);
parameterManager.collectParameterListNames(q, parameterListNames);
}
String limit = null;
String offset = null;
// Main query will get the limit applied by the native mechanism
if (!isMainQuery) {
if (firstResult != 0) {
offset = Integer.toString(firstResult);
}
if (maxResults != Integer.MAX_VALUE) {
limit = Integer.toString(maxResults);
}
}
// Since this builder has no query of it's own, there can be no joins
List<String> keyRestrictedLeftJoinAliases = Collections.emptyList();
List<EntityFunctionNode> entityFunctionNodes = getEntityFunctionNodes(baseQuery);
boolean shouldRenderCteNodes = renderCteNodes(false);
List<CTENode> ctes = shouldRenderCteNodes ? getCteNodes(false) : Collections.EMPTY_LIST;
QuerySpecification querySpecification = new SetOperationQuerySpecification(this, leftMostQuery, baseQuery, setOperands, setOperationManager.getOperator(), getOrderByElements(), setOperationManager.isNested(), parameterManager.getParameterImpls(), parameterListNames, limit, offset, keyRestrictedLeftJoinAliases, entityFunctionNodes, mainQuery.cteManager.isRecursive(), ctes, shouldRenderCteNodes, mainQuery.getQueryConfiguration().isQueryPlanCacheEnabled());
// Unfortunately we need this little adapter here
@SuppressWarnings("rawtypes") TypedQuery<T> query = new CustomSQLTypedQuery<T>(querySpecification, baseQuery, parameterManager.getCriteriaNameMapping(), parameterManager.getTransformers(), parameterManager.getValuesParameters(), parameterManager.getValuesBinders());
// The main query will use the native mechanism for limit/offset
if (isMainQuery) {
if (firstResult != 0) {
query.setFirstResult(firstResult);
}
if (maxResults != Integer.MAX_VALUE) {
query.setMaxResults(maxResults);
}
}
parameterManager.parameterizeQuery(query);
return applyObjectBuilder(query);
}
use of com.blazebit.persistence.impl.query.EntityFunctionNode in project blaze-persistence by Blazebit.
the class AbstractCommonQueryBuilder method getEntityFunctionNodes.
protected List<EntityFunctionNode> getEntityFunctionNodes(Query baseQuery, List<JoinNode> valuesNodes, List<JoinNode> lateInlineNodes, boolean filterNulls) {
List<EntityFunctionNode> entityFunctionNodes = new ArrayList<>();
DbmsDialect dbmsDialect = mainQuery.dbmsDialect;
ValuesStrategy strategy = dbmsDialect.getValuesStrategy();
String dummyTable = dbmsDialect.getDummyTable();
for (JoinNode node : valuesNodes) {
Class<?> clazz = node.getInternalEntityType().getJavaType();
String valueClazzAttributeName = node.getValuesLikeAttribute();
int valueCount = node.getValueCount();
boolean identifiableReference = node.getNodeType() instanceof EntityType<?> && node.getValuesIdNames() != null;
String rootAlias = node.getAlias();
String castedParameter = node.getValuesCastedParameter();
String[] attributes = node.getValuesAttributes();
// We construct an example query representing the values clause with a SELECT clause that selects the fields in the right order which we need to construct SQL
// that uses proper aliases and filters null values which are there in the first place to pad up parameters in case we don't reach the desired value count
StringBuilder valuesSb = new StringBuilder(20 + valueCount * attributes.length * 3);
Query valuesExampleQuery = getValuesExampleQuery(clazz, valueCount, identifiableReference, valueClazzAttributeName, rootAlias, castedParameter, attributes, valuesSb, strategy, dummyTable, node);
String exampleQuerySql = mainQuery.cbf.getExtendedQuerySupport().getSql(mainQuery.em, valuesExampleQuery);
String exampleQuerySqlAlias = mainQuery.cbf.getExtendedQuerySupport().getSqlAlias(mainQuery.em, valuesExampleQuery, "e");
String exampleQueryCollectionSqlAlias = null;
if (!node.isValueClazzAttributeSingular()) {
exampleQueryCollectionSqlAlias = mainQuery.cbf.getExtendedQuerySupport().getSqlAlias(mainQuery.em, valuesExampleQuery, node.getValueClazzAlias("e_"));
}
StringBuilder whereClauseSb = new StringBuilder(exampleQuerySql.length());
String filterNullsTableAlias = "fltr_nulls_tbl_als_";
String valuesAliases = getValuesAliases(exampleQuerySqlAlias, attributes.length, exampleQuerySql, whereClauseSb, filterNullsTableAlias, strategy, dummyTable);
if (strategy == ValuesStrategy.SELECT_VALUES) {
valuesSb.insert(0, valuesAliases);
valuesSb.append(')');
valuesAliases = null;
} else if (strategy == ValuesStrategy.SELECT_UNION) {
valuesSb.insert(0, valuesAliases);
if (!filterNulls) {
// We must order by all values and use a limit in such a case
valuesSb.insert(0, "(select * from ");
valuesSb.append(") val_tmp_ order by ");
if (dbmsDialect.isNullSmallest()) {
for (int i = 0; i < attributes.length; i++) {
valuesSb.append(i + 1);
valuesSb.append(',');
}
} else {
for (int i = 0; i < attributes.length; i++) {
dbmsDialect.appendOrderByElement(valuesSb, new DefaultOrderByElement(null, i + 1, true, true, true), null);
valuesSb.append(',');
}
}
valuesSb.setCharAt(valuesSb.length() - 1, ' ');
dbmsDialect.appendExtendedSql(valuesSb, DbmsStatementType.SELECT, false, true, null, Integer.toString(valueCount + 1), "1", null, null, null);
}
valuesSb.append(')');
valuesAliases = null;
}
if (filterNulls) {
valuesSb.insert(0, "(select * from ");
valuesSb.append(' ');
valuesSb.append(filterNullsTableAlias);
if (valuesAliases != null) {
valuesSb.append(valuesAliases);
valuesAliases = null;
}
valuesSb.append(whereClauseSb);
valuesSb.append(')');
}
String valuesClause = valuesSb.toString();
String valuesTableSqlAlias = exampleQuerySqlAlias;
String valuesTableJoin = null;
String pluralCollectionTableAlias = null;
String pluralTableAlias = null;
String syntheticPredicate = exampleQuerySql.substring(SqlUtils.indexOfWhere(exampleQuerySql) + " where ".length());
if (baseQuery != null) {
valuesTableSqlAlias = cbf.getExtendedQuerySupport().getSqlAlias(em, baseQuery, node.getAlias());
syntheticPredicate = syntheticPredicate.replace(exampleQuerySqlAlias, valuesTableSqlAlias);
if (exampleQueryCollectionSqlAlias != null) {
pluralTableAlias = cbf.getExtendedQuerySupport().getSqlAlias(em, baseQuery, node.getValueClazzAlias(node.getAlias() + "_"));
syntheticPredicate = syntheticPredicate.replace(exampleQueryCollectionSqlAlias, pluralTableAlias);
String baseQuerySql = cbf.getExtendedQuerySupport().getSql(em, baseQuery);
int[] indexRange = SqlUtils.indexOfFullJoin(baseQuerySql, pluralTableAlias);
String baseTableAlias = " " + valuesTableSqlAlias + " ";
int baseTableAliasIndex = baseQuerySql.indexOf(baseTableAlias);
int fullJoinStartIndex = baseTableAliasIndex + baseTableAlias.length();
if (fullJoinStartIndex != indexRange[0]) {
// TODO: find out pluralCollectionTableAlias
String onClause = " on ";
int onClauseIndex = baseQuerySql.indexOf(onClause, fullJoinStartIndex);
int[] collectionTableIndexRange = SqlUtils.rtrimBackwardsToFirstWhitespace(baseQuerySql, onClauseIndex);
pluralCollectionTableAlias = baseQuerySql.substring(collectionTableIndexRange[0], collectionTableIndexRange[1]);
}
valuesTableJoin = baseQuerySql.substring(fullJoinStartIndex, indexRange[1]);
}
}
entityFunctionNodes.add(new EntityFunctionNode(valuesClause, valuesAliases, node.getInternalEntityType().getName(), valuesTableSqlAlias, pluralCollectionTableAlias, pluralTableAlias, valuesTableJoin, syntheticPredicate, false));
}
// We assume to have to select via a union to apply aliases correctly when the values strategy requires that
boolean selectUnion = strategy == ValuesStrategy.SELECT_UNION;
boolean lateralStyle = dbmsDialect.getLateralStyle() == LateralStyle.LATERAL;
for (JoinNode lateInlineNode : lateInlineNodes) {
CTEInfo cteInfo = lateInlineNode.getInlineCte();
String aliases;
StringBuilder aliasesSb = new StringBuilder();
if (selectUnion) {
aliasesSb.append("select ");
for (int i = 0; i < cteInfo.columnNames.size(); i++) {
aliasesSb.append("null ");
aliasesSb.append(cteInfo.columnNames.get(i)).append(',');
}
aliasesSb.setCharAt(aliasesSb.length() - 1, ' ');
aliasesSb.append(" from ").append(dummyTable).append(" where 1=0 union all ");
aliases = null;
} else {
aliasesSb.append('(');
for (int i = 0; i < cteInfo.columnNames.size(); i++) {
aliasesSb.append(cteInfo.columnNames.get(i)).append(',');
}
aliasesSb.setCharAt(aliasesSb.length() - 1, ')');
aliases = aliasesSb.toString();
aliasesSb = null;
}
String subquery;
if (lateInlineNode.isLateral()) {
// We need to wrap the lateral subquery into a temporary HQL subquery to fake the correlation
// Then, we extract the sql part of the actual subquery
StringBuilder lateralExampleQueryString = new StringBuilder();
buildLateralExampleQueryString(lateralExampleQueryString);
String sql = getQuerySpecification(cteInfo.nonRecursiveCriteriaBuilder.getLateralQuery(lateralExampleQueryString, lateralStyle ? null : lateInlineNode)).getSql();
int start = SqlUtils.indexOfWhere(sql);
while (sql.charAt(start) != '(') {
start++;
}
String prefix;
if (dbmsDialect.getLateralStyle() == LateralStyle.LATERAL) {
prefix = "lateral (";
} else if (lateInlineNode.getJoinType() == JoinType.INNER) {
prefix = "cross apply (";
} else {
prefix = "outer apply (";
}
if (aliasesSb == null) {
subquery = prefix + sql.substring(start + 1, sql.lastIndexOf(')')) + ")";
} else {
aliasesSb.insert(0, prefix).append('(').append(sql, start + 1, sql.lastIndexOf(')')).append(')').append(')');
subquery = aliasesSb.toString();
}
} else {
if (aliasesSb == null) {
subquery = "(" + getQuerySpecification(cteInfo.nonRecursiveCriteriaBuilder.getQuery()).getSql() + ")";
} else {
aliasesSb.insert(0, "(").append('(').append(getQuerySpecification(cteInfo.nonRecursiveCriteriaBuilder.getQuery()).getSql()).append(')').append(')');
subquery = aliasesSb.toString();
}
}
String cteTableSqlAlias = baseQuery == null ? "" : cbf.getExtendedQuerySupport().getSqlAlias(em, baseQuery, lateInlineNode.getAlias());
entityFunctionNodes.add(new EntityFunctionNode(subquery, aliases, cteInfo.cteType.getName(), cteTableSqlAlias, null, null, null, null, lateInlineNode.isLateral()));
}
return entityFunctionNodes;
}
use of com.blazebit.persistence.impl.query.EntityFunctionNode in project blaze-persistence by Blazebit.
the class AbstractCommonQueryBuilder method asExpression.
protected Expression asExpression(AbstractCommonQueryBuilder<?, ?, ?, ?, ?> queryBuilder, boolean externalRepresentation, boolean quantifiedPredicate) {
if (queryBuilder instanceof BaseFinalSetOperationBuilderImpl<?, ?, ?>) {
return queryBuilder.asExpression(externalRepresentation, quantifiedPredicate);
}
final String queryString = queryBuilder.buildBaseQueryString(externalRepresentation);
Expression expression = new SubqueryExpression(new Subquery() {
@Override
public String getQueryString() {
return queryString;
}
});
if (externalRepresentation) {
return expression;
}
if (queryBuilder.joinManager.hasEntityFunctions()) {
for (EntityFunctionNode node : queryBuilder.getEntityFunctionNodes(null)) {
List<Expression> arguments = new ArrayList<>(6);
arguments.add(new StringLiteral("ENTITY_FUNCTION"));
arguments.add(expression);
String subquery = node.getSubquery();
String aliases = node.getAliases();
String syntheticPredicate = node.getSyntheticPredicate();
// TODO: this is a hibernate specific integration detail
// Replace the subview subselect that is generated for this subselect
String entityName = node.getEntityName();
arguments.add(new StringLiteral(entityName));
arguments.add(new StringLiteral(subquery));
arguments.add(new StringLiteral(aliases == null ? "" : aliases));
arguments.add(new StringLiteral(syntheticPredicate == null ? "" : syntheticPredicate));
expression = new FunctionExpression("FUNCTION", arguments);
}
}
if (queryBuilder.hasLimit()) {
final boolean hasFirstResult = queryBuilder.getFirstResult() != 0;
final boolean hasMaxResults = queryBuilder.getMaxResults() != Integer.MAX_VALUE;
List<Expression> arguments = new ArrayList<>(2);
arguments.add(new StringLiteral(LimitFunction.FUNCTION_NAME));
arguments.add(expression);
if (!hasMaxResults) {
throw new IllegalArgumentException("First result without max results is not supported!");
} else {
arguments.add(new NumericLiteral(Integer.toString(queryBuilder.getMaxResults()), NumericType.INTEGER));
}
if (hasFirstResult) {
arguments.add(new NumericLiteral(Integer.toString(queryBuilder.getFirstResult()), NumericType.INTEGER));
}
expression = new FunctionExpression("FUNCTION", arguments);
if (quantifiedPredicate && !mainQuery.dbmsDialect.supportsLimitInQuantifiedPredicateSubquery()) {
arguments = new ArrayList<>(2);
arguments.add(new StringLiteral(QueryWrapperFunction.FUNCTION_NAME));
arguments.add(expression);
expression = new FunctionExpression("FUNCTION", arguments);
}
}
return expression;
}
use of com.blazebit.persistence.impl.query.EntityFunctionNode in project blaze-persistence by Blazebit.
the class AbstractFullQueryBuilder method finishEntityFunctionNodes.
protected void finishEntityFunctionNodes(StringBuilder sbSelectFrom, List<EntityFunctionNode> entityFunctionNodes) {
for (EntityFunctionNode node : entityFunctionNodes) {
String subquery = node.getSubquery();
String aliases = node.getAliases();
String syntheticPredicate = node.getSyntheticPredicate();
// TODO: this is a hibernate specific integration detail
// Replace the subview subselect that is generated for this subselect
sbSelectFrom.append(",'");
sbSelectFrom.append(node.getEntityName());
sbSelectFrom.append("',");
TypeUtils.STRING_CONVERTER.appendTo(subquery, sbSelectFrom);
sbSelectFrom.append(",'");
if (aliases != null) {
sbSelectFrom.append(aliases);
}
sbSelectFrom.append("','");
if (syntheticPredicate != null) {
sbSelectFrom.append(syntheticPredicate);
}
sbSelectFrom.append("')");
}
}
use of com.blazebit.persistence.impl.query.EntityFunctionNode in project blaze-persistence by Blazebit.
the class BaseInsertCriteriaBuilderImpl method getQuery.
@Override
protected Query getQuery(Map<DbmsModificationState, String> includedModificationStates) {
// We need to change the underlying sql when doing a limit with hibernate since it does not support limiting insert ... select statements
Query baseQuery = em.createQuery(getBaseQueryStringWithCheck(null, null));
Set<String> parameterListNames = parameterManager.getParameterListNames(baseQuery);
Set<JoinNode> keyRestrictedLeftJoins = getKeyRestrictedLeftJoins();
List<String> keyRestrictedLeftJoinAliases = getKeyRestrictedLeftJoinAliases(baseQuery, keyRestrictedLeftJoins, Collections.EMPTY_SET);
List<EntityFunctionNode> entityFunctionNodes = getEntityFunctionNodes(baseQuery);
boolean isEmbedded = this instanceof ReturningBuilder;
String[] returningColumns = getReturningColumns();
boolean shouldRenderCteNodes = renderCteNodes(isEmbedded);
List<CTENode> ctes = shouldRenderCteNodes ? getCteNodes(isEmbedded) : Collections.EMPTY_LIST;
QuerySpecification querySpecification = new ModificationQuerySpecification(this, baseQuery, getCountExampleQuery(), parameterManager.getParameterImpls(), parameterListNames, keyRestrictedLeftJoinAliases, entityFunctionNodes, mainQuery.cteManager.isRecursive(), ctes, shouldRenderCteNodes, isEmbedded, returningColumns, null, includedModificationStates, returningAttributeBindingMap, mainQuery.getQueryConfiguration().isQueryPlanCacheEnabled());
CustomSQLQuery query = new CustomSQLQuery(querySpecification, baseQuery, parameterManager.getCriteriaNameMapping(), parameterManager.getTransformers(), parameterManager.getValuesParameters(), parameterManager.getValuesBinders());
parameterManager.parameterizeQuery(query);
query.setFirstResult(firstResult);
query.setMaxResults(maxResults);
return query;
}
Aggregations