use of org.hibernate.metamodel.mapping.JdbcMappingContainer in project hibernate-orm by hibernate.
the class CountFunction method canReplaceWithStar.
private boolean canReplaceWithStar(SqlAstNode arg, SqlAstTranslator<?> translator) {
// To determine if we can replace the argument with a star, we must know if the argument is nullable
if (arg instanceof AbstractSqmPathInterpretation<?>) {
final AbstractSqmPathInterpretation<?> pathInterpretation = (AbstractSqmPathInterpretation<?>) arg;
final TableGroup tableGroup = pathInterpretation.getTableGroup();
final Expression sqlExpression = pathInterpretation.getSqlExpression();
final JdbcMappingContainer expressionType = sqlExpression.getExpressionType();
// The entity identifier mapping is always considered non-nullable
final boolean isNonNullable = expressionType instanceof EntityIdentifierMapping;
// But we also have to check if it contains joins that could alter the nullability (RIGHT or FULL)
if (isNonNullable && tableGroup.canUseInnerJoins() && !hasJoinsAlteringNullability(tableGroup)) {
// COUNT can only be used in query specs as query groups can only refer positionally in the order by
final QuerySpec querySpec = (QuerySpec) translator.getCurrentQueryPart();
// On top of this, we also have to ensure that there are no neighbouring joins that alter nullability
for (TableGroup root : querySpec.getFromClause().getRoots()) {
final Boolean result = hasNeighbouringJoinsAlteringNullability(root, tableGroup);
if (result != null) {
return !result;
}
}
return true;
}
}
return false;
}
use of org.hibernate.metamodel.mapping.JdbcMappingContainer in project hibernate-orm by hibernate.
the class AnsiTrimEmulationFunctionTest method render.
private String render(Dialect dialect, TrimFunction function, TrimSpec trimSpec, char trimCharacter, String trimSource) {
SessionFactoryImplementor factory = Mockito.mock(SessionFactoryImplementor.class);
JdbcServices jdbcServices = Mockito.mock(JdbcServices.class);
Mockito.doReturn(jdbcServices).when(factory).getJdbcServices();
Mockito.doReturn(dialect).when(jdbcServices).getDialect();
StandardSqlAstTranslator<JdbcOperation> walker = new StandardSqlAstTranslator<>(factory, null);
List<SqlAstNode> sqlAstArguments = new ArrayList<>();
sqlAstArguments.add(new TrimSpecification(trimSpec));
sqlAstArguments.add(new QueryLiteral<>(trimCharacter, new BasicTypeImpl<>(CharacterJavaType.INSTANCE, CharJdbcType.INSTANCE)));
sqlAstArguments.add(new SelfRenderingExpression() {
@Override
public void renderToSql(SqlAppender sqlAppender, SqlAstTranslator<?> walker, SessionFactoryImplementor sessionFactory) {
sqlAppender.appendSql(trimSource);
}
@Override
public JdbcMappingContainer getExpressionType() {
return null;
}
});
function.render(walker, sqlAstArguments, walker);
return walker.getSql();
}
use of org.hibernate.metamodel.mapping.JdbcMappingContainer in project hibernate-orm by hibernate.
the class SmokeTests method testConvertedHqlInterpretation.
@Test
public void testConvertedHqlInterpretation(SessionFactoryScope scope) {
scope.inTransaction(session -> {
final JdbcTypeRegistry jdbcTypeRegistry = session.getFactory().getTypeConfiguration().getJdbcTypeRegistry();
final QueryImplementor<Gender> query = session.createQuery("select e.gender from SimpleEntity e", Gender.class);
final SqmQueryImplementor<Gender> hqlQuery = (SqmQueryImplementor<Gender>) query;
final SqmSelectStatement<Gender> sqmStatement = (SqmSelectStatement<Gender>) hqlQuery.getSqmStatement();
final StandardSqmTranslator<SelectStatement> sqmConverter = new StandardSqmTranslator<>(sqmStatement, hqlQuery.getQueryOptions(), ((QuerySqmImpl<?>) hqlQuery).getDomainParameterXref(), query.getParameterBindings(), session.getLoadQueryInfluencers(), scope.getSessionFactory(), true);
final SqmTranslation<SelectStatement> sqmInterpretation = sqmConverter.translate();
final SelectStatement sqlAst = sqmInterpretation.getSqlAst();
final FromClause fromClause = sqlAst.getQuerySpec().getFromClause();
assertThat(fromClause.getRoots().size(), is(1));
final TableGroup rootTableGroup = fromClause.getRoots().get(0);
assertThat(rootTableGroup.getPrimaryTableReference(), notNullValue());
assertThat(rootTableGroup.getPrimaryTableReference().getTableId(), is("mapping_simple_entity"));
assertThat(rootTableGroup.getTableReferenceJoins().size(), is(0));
assertThat(rootTableGroup.getTableGroupJoins().isEmpty(), is(true));
// `s` is the "alias stem" for `SimpleEntity` and as it is the first entity with that stem in
// the query the base becomes `s1`. The primary table reference is always suffixed as `_0`
assertThat(rootTableGroup.getPrimaryTableReference().getIdentificationVariable(), is("s1_0"));
final SelectClause selectClause = sqlAst.getQuerySpec().getSelectClause();
assertThat(selectClause.getSqlSelections().size(), is(1));
final SqlSelection sqlSelection = selectClause.getSqlSelections().get(0);
assertThat(sqlSelection.getJdbcResultSetIndex(), is(1));
assertThat(sqlSelection.getValuesArrayPosition(), is(0));
assertThat(sqlSelection.getJdbcValueExtractor(), notNullValue());
assertThat(sqlSelection, instanceOf(SqlSelectionImpl.class));
final Expression selectedExpression = sqlSelection.getExpression();
assertThat(selectedExpression, instanceOf(ColumnReference.class));
final ColumnReference columnReference = (ColumnReference) selectedExpression;
assertThat(columnReference.renderSqlFragment(scope.getSessionFactory()), is("s1_0.gender"));
final JdbcMappingContainer selectedExpressible = selectedExpression.getExpressionType();
assertThat(selectedExpressible, instanceOf(BasicTypeImpl.class));
final BasicTypeImpl<?> basicType = (BasicTypeImpl<?>) selectedExpressible;
assertThat(basicType.getJavaTypeDescriptor().getJavaTypeClass(), AssignableMatcher.assignableTo(Integer.class));
assertThat(basicType.getJdbcType(), is(jdbcTypeRegistry.getDescriptor(Types.TINYINT)));
assertThat(sqlAst.getDomainResultDescriptors().size(), is(1));
final DomainResult<?> domainResult = sqlAst.getDomainResultDescriptors().get(0);
assertThat(domainResult, instanceOf(BasicResult.class));
final BasicResult<?> scalarDomainResult = (BasicResult<?>) domainResult;
assertThat(scalarDomainResult.getAssembler(), instanceOf(BasicResultAssembler.class));
final BasicResultAssembler<?> assembler = (BasicResultAssembler<?>) scalarDomainResult.getAssembler();
assertThat(assembler.getValueConverter(), notNullValue());
assertThat(assembler.getValueConverter(), instanceOf(OrdinalEnumValueConverter.class));
final NavigablePath expectedSelectedPath = new NavigablePath(SimpleEntity.class.getName(), "e").append("gender");
assertThat(domainResult.getNavigablePath(), equalTo(expectedSelectedPath));
assertThat(domainResult, instanceOf(BasicResult.class));
// ScalarDomainResultImpl creates and caches the assembler at its creation.
// this just gets access to that cached one
final DomainResultAssembler<?> resultAssembler = domainResult.createResultAssembler(null, null);
assertThat(resultAssembler, instanceOf(BasicResultAssembler.class));
final BasicValueConverter<?, ?> valueConverter = ((BasicResultAssembler<?>) resultAssembler).getValueConverter();
assertThat(valueConverter, notNullValue());
assertThat(valueConverter, instanceOf(OrdinalEnumValueConverter.class));
final JdbcSelect jdbcSelectOperation = new StandardSqlAstTranslator<JdbcSelect>(session.getSessionFactory(), sqlAst).translate(null, QueryOptions.NONE);
assertThat(jdbcSelectOperation.getSql(), is("select s1_0.gender from mapping_simple_entity s1_0"));
});
}
use of org.hibernate.metamodel.mapping.JdbcMappingContainer in project hibernate-orm by hibernate.
the class BaseSqmToSqlAstConverter method transformDurationArithmetic.
private Object transformDurationArithmetic(SqmBinaryArithmetic<?> expression) {
BinaryArithmeticOperator operator = expression.getOperator();
// the expression tree
switch(operator) {
case ADD:
case SUBTRACT:
// the only legal binary operations involving
// a duration with a date or timestamp are
// addition and subtraction with the duration
// on the right and the date or timestamp on
// the left, producing a date or timestamp
//
// ts + d or ts - d
//
// the only legal binary operations involving
// two durations are addition and subtraction,
// producing a duration
//
// d1 + d2
// re-express addition of non-leaf duration
// expressions to a date or timestamp as
// addition of leaf durations to a date or
// timestamp
// ts + x * (d1 + d2) => (ts + x * d1) + x * d2
// ts - x * (d1 + d2) => (ts - x * d1) - x * d2
// ts + x * (d1 - d2) => (ts + x * d1) - x * d2
// ts - x * (d1 - d2) => (ts - x * d1) + x * d2
Expression timestamp = adjustedTimestamp;
SqmExpressible<?> timestampType = adjustedTimestampType;
adjustedTimestamp = toSqlExpression(expression.getLeftHandOperand().accept(this));
JdbcMappingContainer type = adjustedTimestamp.getExpressionType();
if (type instanceof SqmExpressible) {
adjustedTimestampType = (SqmExpressible<?>) type;
} else if (type instanceof AttributeMapping) {
adjustedTimestampType = (SqmExpressible<?>) ((AttributeMapping) type).getMappedType();
} else {
// else we know it has not been transformed
adjustedTimestampType = expression.getLeftHandOperand().getNodeType();
}
if (operator == SUBTRACT) {
negativeAdjustment = !negativeAdjustment;
}
try {
return expression.getRightHandOperand().accept(this);
} finally {
if (operator == SUBTRACT) {
negativeAdjustment = !negativeAdjustment;
}
adjustedTimestamp = timestamp;
adjustedTimestampType = timestampType;
}
case MULTIPLY:
// finally, we can multiply a duration on the
// right by a scalar value on the left
// scalar multiplication produces a duration
// x * d
// distribute scalar multiplication over the
// terms, not forgetting the propagated scale
// x * (d1 + d2) => x * d1 + x * d2
// x * (d1 - d2) => x * d1 - x * d2
// -x * (d1 + d2) => - x * d1 - x * d2
// -x * (d1 - d2) => - x * d1 + x * d2
Expression duration = toSqlExpression(expression.getLeftHandOperand().accept(this));
Expression scale = adjustmentScale;
boolean negate = negativeAdjustment;
adjustmentScale = applyScale(duration);
// was sucked into the scale
negativeAdjustment = false;
try {
return expression.getRightHandOperand().accept(this);
} finally {
adjustmentScale = scale;
negativeAdjustment = negate;
}
default:
throw new SemanticException("illegal operator for a duration " + operator);
}
}
use of org.hibernate.metamodel.mapping.JdbcMappingContainer in project hibernate-orm by hibernate.
the class BaseSqmToSqlAstConverter method visitBasicValuedPath.
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SqmPath
@Override
public Expression visitBasicValuedPath(SqmBasicValuedSimplePath<?> sqmPath) {
final BasicValuedPathInterpretation<?> path = prepareReusablePath(sqmPath, () -> BasicValuedPathInterpretation.from(sqmPath, this, this, jpaQueryComplianceEnabled));
Expression result = path;
if (isDuration(sqmPath.getNodeType())) {
// Durations are stored (at least by default)
// in a NUMERIC column in seconds with fractional
// seconds in the decimal places
// which we need to convert to the given unit
//
// This does not work at all for a Duration
// mapped to a VARCHAR column, in which case
// we would need to parse the weird format
// defined by java.time.Duration (a bit hard
// to do without some custom function).
// Nor does it work for databases which have
// a well-defined INTERVAL type, but that is
// something we could implement.
// first let's apply the propagated scale
Expression scaledExpression = applyScale(toSqlExpression(path));
if (adjustedTimestamp != null) {
if (appliedByUnit != null) {
throw new IllegalStateException();
}
// we're adding this variable duration to the
// given date or timestamp, producing an
// adjusted date or timestamp
result = timestampadd().expression((ReturnableType<?>) adjustedTimestampType, new DurationUnit(SECOND, basicType(Long.class)), scaledExpression, adjustedTimestamp);
} else if (appliedByUnit != null) {
// we're applying the 'by unit' operator,
// producing a literal scalar value, so
// we must convert this duration from
// nanoseconds to the given unit
JdbcMappingContainer durationType = scaledExpression.getExpressionType();
Duration duration;
if (durationType.getJdbcMappings().get(0).getJdbcType().isInterval()) {
// For interval types, we need to extract the epoch for integer arithmetic for the 'by unit' operator
duration = new Duration(extractEpoch(scaledExpression), SECOND, (BasicValuedMapping) durationType);
} else {
// The absolute value of the expression is in seconds
// as the fractional seconds are in the fraction part as can be seen in DurationJavaType
duration = new Duration(scaledExpression, SECOND, (BasicValuedMapping) durationType);
}
TemporalUnit appliedUnit = appliedByUnit.getUnit().getUnit();
BasicValuedMapping scalarType = (BasicValuedMapping) appliedByUnit.getNodeType();
result = new Conversion(duration, appliedUnit, scalarType);
} else {
// a "bare" Duration value in nanoseconds
result = scaledExpression;
}
}
return withTreatRestriction(result, sqmPath);
}
Aggregations