HHH-17859, HHH-17858 function() and column() functions
This commit is contained in:
parent
1ba67c2de9
commit
6441c60255
|
@ -11,7 +11,6 @@ import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
import org.hibernate.sql.ast.tree.cte.CteMaterialization;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
|
||||||
import org.hibernate.sql.ast.tree.expression.Any;
|
import org.hibernate.sql.ast.tree.expression.Any;
|
||||||
import org.hibernate.sql.ast.tree.expression.Every;
|
import org.hibernate.sql.ast.tree.expression.Every;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
|
|
@ -158,6 +158,7 @@ BY : [bB] [yY];
|
||||||
CASE : [cC] [aA] [sS] [eE];
|
CASE : [cC] [aA] [sS] [eE];
|
||||||
CAST : [cC] [aA] [sS] [tT];
|
CAST : [cC] [aA] [sS] [tT];
|
||||||
COLLATE : [cC] [oO] [lL] [lL] [aA] [tT] [eE];
|
COLLATE : [cC] [oO] [lL] [lL] [aA] [tT] [eE];
|
||||||
|
COLUMN : [cC] [oO] [lL] [uU] [mM] [nN];
|
||||||
CONFLICT : [cC] [oO] [nN] [fF] [lL] [iI] [cC] [tT];
|
CONFLICT : [cC] [oO] [nN] [fF] [lL] [iI] [cC] [tT];
|
||||||
CONSTRAINT : [cC] [oO] [nN] [sS] [tT] [rR] [aA] [iI] [nN] [tT];
|
CONSTRAINT : [cC] [oO] [nN] [sS] [tT] [rR] [aA] [iI] [nN] [tT];
|
||||||
COUNT : [cC] [oO] [uU] [nN] [tT];
|
COUNT : [cC] [oO] [uU] [nN] [tT];
|
||||||
|
|
|
@ -1083,6 +1083,7 @@ function
|
||||||
| collectionAggregateFunction
|
| collectionAggregateFunction
|
||||||
| collectionFunctionMisuse
|
| collectionFunctionMisuse
|
||||||
| jpaNonstandardFunction
|
| jpaNonstandardFunction
|
||||||
|
| columnFunction
|
||||||
| genericFunction
|
| genericFunction
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -1090,7 +1091,7 @@ function
|
||||||
* A syntax for calling user-defined or native database functions, required by JPQL
|
* A syntax for calling user-defined or native database functions, required by JPQL
|
||||||
*/
|
*/
|
||||||
jpaNonstandardFunction
|
jpaNonstandardFunction
|
||||||
: FUNCTION LEFT_PAREN jpaNonstandardFunctionName (COMMA genericFunctionArguments)? RIGHT_PAREN
|
: FUNCTION LEFT_PAREN jpaNonstandardFunctionName (AS castTarget)? (COMMA genericFunctionArguments)? RIGHT_PAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1098,6 +1099,11 @@ jpaNonstandardFunction
|
||||||
*/
|
*/
|
||||||
jpaNonstandardFunctionName
|
jpaNonstandardFunctionName
|
||||||
: STRING_LITERAL
|
: STRING_LITERAL
|
||||||
|
| identifier
|
||||||
|
;
|
||||||
|
|
||||||
|
columnFunction
|
||||||
|
: COLUMN LEFT_PAREN path DOT jpaNonstandardFunctionName (AS castTarget)? RIGHT_PAREN
|
||||||
;
|
;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1618,6 +1624,7 @@ rollup
|
||||||
| CASE
|
| CASE
|
||||||
| CAST
|
| CAST
|
||||||
| COLLATE
|
| COLLATE
|
||||||
|
| COLUMN
|
||||||
| CONFLICT
|
| CONFLICT
|
||||||
| CONSTRAINT
|
| CONSTRAINT
|
||||||
| COUNT
|
| COUNT
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.dialect.function;
|
||||||
|
|
||||||
|
import org.hibernate.query.ReturnableType;
|
||||||
|
import org.hibernate.query.hql.HqlInterpretationException;
|
||||||
|
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||||
|
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
|
||||||
|
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||||
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.update.Assignable;
|
||||||
|
import org.hibernate.type.BasicType;
|
||||||
|
import org.hibernate.type.JavaObjectType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Gavin King
|
||||||
|
*/
|
||||||
|
public class SqlColumn extends AbstractSqmSelfRenderingFunctionDescriptor {
|
||||||
|
private final String columnName;
|
||||||
|
|
||||||
|
public SqlColumn(String columnName, BasicType<?> type) {
|
||||||
|
super(
|
||||||
|
"column",
|
||||||
|
StandardArgumentsValidators.min( 1 ),
|
||||||
|
StandardFunctionReturnTypeResolvers.invariant( type == null ? JavaObjectType.INSTANCE : type ),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
this.columnName = columnName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void render(
|
||||||
|
SqlAppender sqlAppender,
|
||||||
|
List<? extends SqlAstNode> arguments,
|
||||||
|
ReturnableType<?> returnType,
|
||||||
|
SqlAstTranslator<?> walker) {
|
||||||
|
final SqlAstNode sqlAstNode = arguments.get(0);
|
||||||
|
final ColumnReference reference;
|
||||||
|
if ( sqlAstNode instanceof Assignable ) {
|
||||||
|
final Assignable assignable = (Assignable) sqlAstNode;
|
||||||
|
reference = assignable.getColumnReferences().get(0);
|
||||||
|
}
|
||||||
|
else if ( sqlAstNode instanceof Expression ) {
|
||||||
|
final Expression expression = (Expression) sqlAstNode;
|
||||||
|
reference = expression.getColumnReference();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new HqlInterpretationException( "path did not map to a column" );
|
||||||
|
}
|
||||||
|
sqlAppender.appendSql( reference.getQualifier() );
|
||||||
|
sqlAppender.appendSql( '.' );
|
||||||
|
sqlAppender.appendSql( columnName );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -62,7 +62,7 @@ public interface ConnectionProvider extends Service, Wrapped {
|
||||||
* <p>
|
* <p>
|
||||||
* Typically, this is only true in managed environments where a container tracks connections
|
* Typically, this is only true in managed environments where a container tracks connections
|
||||||
* by transaction or thread.
|
* by transaction or thread.
|
||||||
*
|
* <p>
|
||||||
* Note that JTA semantic depends on the fact that the underlying connection provider does
|
* Note that JTA semantic depends on the fact that the underlying connection provider does
|
||||||
* support aggressive release.
|
* support aggressive release.
|
||||||
*
|
*
|
||||||
|
|
|
@ -35,6 +35,7 @@ import java.util.Set;
|
||||||
|
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
|
||||||
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
|
||||||
|
import org.hibernate.dialect.function.SqlColumn;
|
||||||
import org.hibernate.grammars.hql.HqlLexer;
|
import org.hibernate.grammars.hql.HqlLexer;
|
||||||
import org.hibernate.grammars.hql.HqlParser;
|
import org.hibernate.grammars.hql.HqlParser;
|
||||||
import org.hibernate.grammars.hql.HqlParserBaseVisitor;
|
import org.hibernate.grammars.hql.HqlParserBaseVisitor;
|
||||||
|
@ -58,7 +59,6 @@ import org.hibernate.metamodel.model.domain.spi.JpaMetamodelImplementor;
|
||||||
import org.hibernate.query.NullPrecedence;
|
import org.hibernate.query.NullPrecedence;
|
||||||
import org.hibernate.query.ParameterLabelException;
|
import org.hibernate.query.ParameterLabelException;
|
||||||
import org.hibernate.query.PathException;
|
import org.hibernate.query.PathException;
|
||||||
import org.hibernate.query.ReturnableType;
|
|
||||||
import org.hibernate.query.SemanticException;
|
import org.hibernate.query.SemanticException;
|
||||||
import org.hibernate.query.SortDirection;
|
import org.hibernate.query.SortDirection;
|
||||||
import org.hibernate.query.SyntaxException;
|
import org.hibernate.query.SyntaxException;
|
||||||
|
@ -277,6 +277,9 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
private static final Logger log = Logger.getLogger( SemanticQueryBuilder.class );
|
private static final Logger log = Logger.getLogger( SemanticQueryBuilder.class );
|
||||||
private static final Set<String> JPA_STANDARD_FUNCTIONS;
|
private static final Set<String> JPA_STANDARD_FUNCTIONS;
|
||||||
|
|
||||||
|
private static final BasicTypeImpl<Object> OBJECT_BASIC_TYPE =
|
||||||
|
new BasicTypeImpl<>( new UnknownBasicJavaType<>(Object.class), ObjectJdbcType.INSTANCE );
|
||||||
|
|
||||||
static {
|
static {
|
||||||
final Set<String> jpaStandardFunctions = new HashSet<>();
|
final Set<String> jpaStandardFunctions = new HashSet<>();
|
||||||
// Extracted from the BNF in JPA spec 4.14.
|
// Extracted from the BNF in JPA spec 4.14.
|
||||||
|
@ -3910,43 +3913,64 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// Functions
|
// Functions
|
||||||
|
|
||||||
@Override
|
private String toName(HqlParser.JpaNonstandardFunctionNameContext ctx) {
|
||||||
public SqmExpression<?> visitJpaNonstandardFunction(HqlParser.JpaNonstandardFunctionContext ctx) {
|
return ctx.STRING_LITERAL() == null
|
||||||
final String functionName = unquoteStringLiteral( ctx.jpaNonstandardFunctionName().getText() ).toLowerCase();
|
? ctx.identifier().getText().toLowerCase()
|
||||||
final List<SqmTypedNode<?>> functionArguments;
|
: unquoteStringLiteral( ctx.STRING_LITERAL().getText() ).toLowerCase();
|
||||||
if ( ctx.getChildCount() > 4 ) {
|
|
||||||
//noinspection unchecked
|
|
||||||
functionArguments = (List<SqmTypedNode<?>>) ctx.genericFunctionArguments().accept( this );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
functionArguments = emptyList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmExpression<?> visitJpaNonstandardFunction(HqlParser.JpaNonstandardFunctionContext ctx) {
|
||||||
|
final String functionName = toName( ctx.jpaNonstandardFunctionName() );
|
||||||
|
final HqlParser.GenericFunctionArgumentsContext argumentsContext = ctx.genericFunctionArguments();
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final List<SqmTypedNode<?>> functionArguments =
|
||||||
|
argumentsContext == null
|
||||||
|
? emptyList()
|
||||||
|
: (List<SqmTypedNode<?>>) argumentsContext.accept(this);
|
||||||
|
|
||||||
|
final BasicType<?> returnableType = returnType( ctx.castTarget() );
|
||||||
SqmFunctionDescriptor functionTemplate = getFunctionDescriptor( functionName );
|
SqmFunctionDescriptor functionTemplate = getFunctionDescriptor( functionName );
|
||||||
if (functionTemplate == null) {
|
if ( functionTemplate == null ) {
|
||||||
functionTemplate = new NamedSqmFunctionDescriptor(
|
functionTemplate = new NamedSqmFunctionDescriptor(
|
||||||
functionName,
|
functionName,
|
||||||
true,
|
true,
|
||||||
null,
|
null,
|
||||||
StandardFunctionReturnTypeResolvers.invariant(
|
StandardFunctionReturnTypeResolvers.invariant(returnableType),
|
||||||
new BasicTypeImpl<>(
|
|
||||||
new UnknownBasicJavaType<>( Object.class ),
|
|
||||||
ObjectJdbcType.INSTANCE
|
|
||||||
)
|
|
||||||
),
|
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return functionTemplate.generateSqmExpression(
|
return functionTemplate.generateSqmExpression(
|
||||||
functionArguments,
|
functionArguments,
|
||||||
null,
|
returnableType,
|
||||||
creationContext.getQueryEngine()
|
creationContext.getQueryEngine()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmExpression<?> visitColumnFunction(HqlParser.ColumnFunctionContext ctx) {
|
||||||
|
final String columnName = toName( ctx.jpaNonstandardFunctionName() );
|
||||||
|
final SemanticPathPart semanticPathPart = visitPath( ctx.path() );
|
||||||
|
final BasicType<?> resultType = returnType( ctx.castTarget() );
|
||||||
|
return new SqlColumn( columnName, resultType ).generateSqmExpression(
|
||||||
|
(SqmTypedNode<?>) semanticPathPart,
|
||||||
|
resultType,
|
||||||
|
creationContext.getQueryEngine()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BasicType<?> returnType(HqlParser.CastTargetContext castTarget) {
|
||||||
|
if ( castTarget == null ) {
|
||||||
|
return OBJECT_BASIC_TYPE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return (BasicType<?>) visitCastTarget( castTarget ).getType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String visitGenericFunctionName(HqlParser.GenericFunctionNameContext ctx) {
|
public String visitGenericFunctionName(HqlParser.GenericFunctionNameContext ctx) {
|
||||||
StringBuilder functionName = new StringBuilder( visitIdentifier( ctx.simplePath().identifier() ) );
|
final StringBuilder functionName = new StringBuilder( visitIdentifier( ctx.simplePath().identifier() ) );
|
||||||
for ( HqlParser.SimplePathElementContext sp: ctx.simplePath().simplePathElement() ) {
|
for ( HqlParser.SimplePathElementContext sp: ctx.simplePath().simplePathElement() ) {
|
||||||
// allow function names of form foo.bar to be located in the registry
|
// allow function names of form foo.bar to be located in the registry
|
||||||
functionName.append('.').append( visitIdentifier( sp.identifier() ) );
|
functionName.append('.').append( visitIdentifier( sp.identifier() ) );
|
||||||
|
@ -4474,7 +4498,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
||||||
final Integer scale = secondArg == null ? null : Integer.valueOf( secondArg.getText() );
|
final Integer scale = secondArg == null ? null : Integer.valueOf( secondArg.getText() );
|
||||||
|
|
||||||
return new SqmCastTarget<>(
|
return new SqmCastTarget<>(
|
||||||
(ReturnableType<?>)
|
|
||||||
creationContext.getTypeConfiguration()
|
creationContext.getTypeConfiguration()
|
||||||
.resolveCastTargetType( targetName ),
|
.resolveCastTargetType( targetName ),
|
||||||
//TODO: is there some way to interpret as length vs precision/scale here at this point?
|
//TODO: is there some way to interpret as length vs precision/scale here at this point?
|
||||||
|
|
|
@ -41,7 +41,6 @@ import org.hibernate.internal.CoreMessageLogger;
|
||||||
import org.hibernate.internal.SessionFactoryRegistry;
|
import org.hibernate.internal.SessionFactoryRegistry;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
import org.hibernate.metamodel.model.domain.BasicDomainType;
|
|
||||||
import org.hibernate.metamodel.model.domain.DomainType;
|
import org.hibernate.metamodel.model.domain.DomainType;
|
||||||
import org.hibernate.metamodel.model.domain.JpaMetamodel;
|
import org.hibernate.metamodel.model.domain.JpaMetamodel;
|
||||||
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
|
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
|
||||||
|
@ -468,7 +467,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <X, T> SqmExpression<X> cast(JpaExpression<T> expression, Class<X> castTargetJavaType) {
|
public <X, T> SqmExpression<X> cast(JpaExpression<T> expression, Class<X> castTargetJavaType) {
|
||||||
final BasicDomainType<X> type = getTypeConfiguration().standardBasicTypeForJavaType( castTargetJavaType );
|
final BasicType<X> type = getTypeConfiguration().standardBasicTypeForJavaType( castTargetJavaType );
|
||||||
return getFunctionDescriptor( "cast" ).generateSqmExpression(
|
return getFunctionDescriptor( "cast" ).generateSqmExpression(
|
||||||
asList( (SqmTypedNode<?>) expression, new SqmCastTarget<>( type, this ) ),
|
asList( (SqmTypedNode<?>) expression, new SqmCastTarget<>( type, this ) ),
|
||||||
type,
|
type,
|
||||||
|
|
|
@ -27,6 +27,7 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
@ -323,7 +324,7 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable {
|
||||||
* <p>
|
* <p>
|
||||||
* The type names are not case-sensitive.
|
* The type names are not case-sensitive.
|
||||||
*/
|
*/
|
||||||
public BasicValuedMapping resolveCastTargetType(String name) {
|
public BasicType<?> resolveCastTargetType(String name) {
|
||||||
switch ( name.toLowerCase() ) {
|
switch ( name.toLowerCase() ) {
|
||||||
case "string": return getBasicTypeForJavaType( String.class );
|
case "string": return getBasicTypeForJavaType( String.class );
|
||||||
case "character": return getBasicTypeForJavaType( Character.class );
|
case "character": return getBasicTypeForJavaType( Character.class );
|
||||||
|
@ -346,6 +347,7 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable {
|
||||||
case "duration": return getBasicTypeForJavaType( Duration.class );
|
case "duration": return getBasicTypeForJavaType( Duration.class );
|
||||||
case "instant": return getBasicTypeForJavaType( Instant.class );
|
case "instant": return getBasicTypeForJavaType( Instant.class );
|
||||||
case "binary": return getBasicTypeForJavaType( byte[].class );
|
case "binary": return getBasicTypeForJavaType( byte[].class );
|
||||||
|
case "uuid": return getBasicTypeForJavaType( UUID.class );
|
||||||
//this one is very fragile ... works well for BIT or BOOLEAN columns only
|
//this one is very fragile ... works well for BIT or BOOLEAN columns only
|
||||||
//works OK, I suppose, for integer columns, but not at all for char columns
|
//works OK, I suppose, for integer columns, but not at all for char columns
|
||||||
case "boolean": return getBasicTypeForJavaType( Boolean.class );
|
case "boolean": return getBasicTypeForJavaType( Boolean.class );
|
||||||
|
@ -353,7 +355,7 @@ public class TypeConfiguration implements SessionFactoryObserver, Serializable {
|
||||||
case "yesno": return basicTypeRegistry.getRegisteredType( StandardBasicTypes.YES_NO.getName() );
|
case "yesno": return basicTypeRegistry.getRegisteredType( StandardBasicTypes.YES_NO.getName() );
|
||||||
case "numericboolean": return basicTypeRegistry.getRegisteredType( StandardBasicTypes.NUMERIC_BOOLEAN.getName() );
|
case "numericboolean": return basicTypeRegistry.getRegisteredType( StandardBasicTypes.NUMERIC_BOOLEAN.getName() );
|
||||||
default: {
|
default: {
|
||||||
final BasicType<Object> registeredBasicType = basicTypeRegistry.getRegisteredType( name );
|
final BasicType<?> registeredBasicType = basicTypeRegistry.getRegisteredType( name );
|
||||||
if ( registeredBasicType != null ) {
|
if ( registeredBasicType != null ) {
|
||||||
return registeredBasicType;
|
return registeredBasicType;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@ import java.time.OffsetDateTime;
|
||||||
import java.time.temporal.ChronoUnit;
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
|
|
||||||
|
@ -61,7 +62,6 @@ import static org.hamcrest.CoreMatchers.*;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.isOneOf;
|
import static org.hamcrest.Matchers.isOneOf;
|
||||||
|
|
||||||
import static org.hibernate.cfg.QuerySettings.PORTABLE_INTEGER_DIVISION;
|
|
||||||
import static org.hibernate.testing.orm.domain.gambit.EntityOfBasics.Gender.FEMALE;
|
import static org.hibernate.testing.orm.domain.gambit.EntityOfBasics.Gender.FEMALE;
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
@ -92,6 +92,7 @@ public class FunctionTests {
|
||||||
entity.setTheTime( new Time( 20, 10, 8 ) );
|
entity.setTheTime( new Time( 20, 10, 8 ) );
|
||||||
entity.setTheDuration( Duration.of(3, ChronoUnit.SECONDS).plus( Duration.of(23,ChronoUnit.MILLIS) ) );
|
entity.setTheDuration( Duration.of(3, ChronoUnit.SECONDS).plus( Duration.of(23,ChronoUnit.MILLIS) ) );
|
||||||
entity.setTheTimestamp( new Timestamp( 121, 4, 27, 13, 22, 50, 123456789 ) );
|
entity.setTheTimestamp( new Timestamp( 121, 4, 27, 13, 22, 50, 123456789 ) );
|
||||||
|
entity.setTheUuid( UUID.randomUUID() );
|
||||||
em.persist(entity);
|
em.persist(entity);
|
||||||
|
|
||||||
EntityOfLists eol = new EntityOfLists(1,"");
|
EntityOfLists eol = new EntityOfLists(1,"");
|
||||||
|
@ -1280,10 +1281,14 @@ public class FunctionTests {
|
||||||
session -> {
|
session -> {
|
||||||
assertThat( session.createQuery("select function('lower','HIBERNATE')", String.class).getSingleResult(),
|
assertThat( session.createQuery("select function('lower','HIBERNATE')", String.class).getSingleResult(),
|
||||||
equalTo("hibernate") );
|
equalTo("hibernate") );
|
||||||
|
assertThat( session.createQuery("select function(lower as String,'HIBERNATE')", String.class).getSingleResult(),
|
||||||
|
equalTo("hibernate") );
|
||||||
assertThat( session.createQuery("select 1 where function('lower','HIBERNATE') = 'hibernate'", Integer.class).getSingleResult(),
|
assertThat( session.createQuery("select 1 where function('lower','HIBERNATE') = 'hibernate'", Integer.class).getSingleResult(),
|
||||||
equalTo(1) );
|
equalTo(1) );
|
||||||
assertThat( session.createQuery("select function('current_user')", String.class).getSingleResult().toLowerCase(),
|
assertThat( session.createQuery("select function('current_user')", String.class).getSingleResult().toLowerCase(),
|
||||||
isOneOf("hibernate_orm_test", "hibernateormtest", "sa", "hibernateormtest@%", "hibernate_orm_test@%", "root@%") );
|
isOneOf("hibernate_orm_test", "hibernateormtest", "sa", "hibernateormtest@%", "hibernate_orm_test@%", "root@%") );
|
||||||
|
assertThat( session.createQuery("select function(current_user as String)", String.class).getSingleResult().toLowerCase(),
|
||||||
|
isOneOf("hibernate_orm_test", "hibernateormtest", "sa", "hibernateormtest@%", "hibernate_orm_test@%", "root@%") );
|
||||||
assertThat( session.createQuery("select lower(function('current_user'))", String.class).getSingleResult(),
|
assertThat( session.createQuery("select lower(function('current_user'))", String.class).getSingleResult(),
|
||||||
isOneOf("hibernate_orm_test", "hibernateormtest", "sa", "hibernateormtest@%", "hibernate_orm_test@%", "root@%") );
|
isOneOf("hibernate_orm_test", "hibernateormtest", "sa", "hibernateormtest@%", "hibernate_orm_test@%", "root@%") );
|
||||||
session.createQuery("select 1 where function('current_user') = 'hibernate_orm_test'", Integer.class).getSingleResultOrNull();
|
session.createQuery("select 1 where function('current_user') = 'hibernate_orm_test'", Integer.class).getSingleResultOrNull();
|
||||||
|
@ -2268,4 +2273,38 @@ public class FunctionTests {
|
||||||
.getSingleResultOrNull());
|
.getSingleResultOrNull());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testColumnFunction(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(s -> {
|
||||||
|
assertEquals("the string",
|
||||||
|
s.createSelectionQuery("select column(e.the_column) from EntityOfBasics e", String.class)
|
||||||
|
.getSingleResultOrNull());
|
||||||
|
assertEquals("the string",
|
||||||
|
s.createSelectionQuery("select column(e.'the_column') from EntityOfBasics e", String.class)
|
||||||
|
.getSingleResultOrNull());
|
||||||
|
s.createSelectionQuery("from EntityOfBasics e where column(e.the_column as String) = 'the string'", EntityOfBasics.class)
|
||||||
|
.getSingleResult();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUUIDColumnFunction(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(s -> {
|
||||||
|
byte[] bytes = s.createSelectionQuery("select column(e.theuuid as binary) from EntityOfBasics e", byte[].class)
|
||||||
|
.getSingleResultOrNull();
|
||||||
|
UUID uuid = s.createSelectionQuery("select column(e.theuuid as UUID) from EntityOfBasics e", UUID.class)
|
||||||
|
.getSingleResultOrNull();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test @RequiresDialect(PostgreSQLDialect.class)
|
||||||
|
public void testCtidColumnFunction(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction(s -> {
|
||||||
|
String string = s.createSelectionQuery("select column(e.ctid as String) from EntityOfBasics e", String.class)
|
||||||
|
.getSingleResultOrNull();
|
||||||
|
byte[] bytes = s.createSelectionQuery("select column(e.ctid as binary) from EntityOfBasics e", byte[].class)
|
||||||
|
.getSingleResultOrNull();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ import java.time.LocalTime;
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import jakarta.persistence.AttributeConverter;
|
import jakarta.persistence.AttributeConverter;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
import jakarta.persistence.Convert;
|
import jakarta.persistence.Convert;
|
||||||
|
@ -68,6 +70,7 @@ public class EntityOfBasics {
|
||||||
private Gender convertedGender;
|
private Gender convertedGender;
|
||||||
private Gender ordinalGender;
|
private Gender ordinalGender;
|
||||||
private Duration theDuration;
|
private Duration theDuration;
|
||||||
|
private UUID theUuid;
|
||||||
|
|
||||||
private LocalDateTime theLocalDateTime;
|
private LocalDateTime theLocalDateTime;
|
||||||
private LocalDate theLocalDate;
|
private LocalDate theLocalDate;
|
||||||
|
@ -77,6 +80,8 @@ public class EntityOfBasics {
|
||||||
|
|
||||||
private MutableValue mutableValue;
|
private MutableValue mutableValue;
|
||||||
|
|
||||||
|
private String theField = "the string";
|
||||||
|
|
||||||
public EntityOfBasics() {
|
public EntityOfBasics() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,6 +277,15 @@ public class EntityOfBasics {
|
||||||
this.theDuration = theDuration;
|
this.theDuration = theDuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Column(name = "theuuid")
|
||||||
|
public UUID getTheUuid() {
|
||||||
|
return theUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTheUuid(UUID theUuid) {
|
||||||
|
this.theUuid = theUuid;
|
||||||
|
}
|
||||||
|
|
||||||
public Boolean isTheBoolean() {
|
public Boolean isTheBoolean() {
|
||||||
return theBoolean;
|
return theBoolean;
|
||||||
}
|
}
|
||||||
|
@ -298,6 +312,15 @@ public class EntityOfBasics {
|
||||||
this.theStringBoolean = theStringBoolean;
|
this.theStringBoolean = theStringBoolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Column(name = "the_column")
|
||||||
|
public String getTheField() {
|
||||||
|
return theField;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTheField(String theField) {
|
||||||
|
this.theField = theField;
|
||||||
|
}
|
||||||
|
|
||||||
@Convert( converter = MutableValueConverter.class )
|
@Convert( converter = MutableValueConverter.class )
|
||||||
public MutableValue getMutableValue() {
|
public MutableValue getMutableValue() {
|
||||||
return mutableValue;
|
return mutableValue;
|
||||||
|
|
|
@ -135,6 +135,9 @@ public abstract class MockSessionFactory
|
||||||
implements SessionFactoryImplementor, QueryEngine, RuntimeModelCreationContext, MetadataBuildingOptions,
|
implements SessionFactoryImplementor, QueryEngine, RuntimeModelCreationContext, MetadataBuildingOptions,
|
||||||
BootstrapContext, MetadataBuildingContext, FunctionContributions, SessionFactoryOptions, JdbcTypeIndicators {
|
BootstrapContext, MetadataBuildingContext, FunctionContributions, SessionFactoryOptions, JdbcTypeIndicators {
|
||||||
|
|
||||||
|
private static final BasicTypeImpl<Object> OBJECT_BASIC_TYPE =
|
||||||
|
new BasicTypeImpl<>(new UnknownBasicJavaType<>(Object.class), ObjectJdbcType.INSTANCE);
|
||||||
|
|
||||||
// static so other things can get at it
|
// static so other things can get at it
|
||||||
// TODO: make a static instance of this whole object instead!
|
// TODO: make a static instance of this whole object instead!
|
||||||
static TypeConfiguration typeConfiguration;
|
static TypeConfiguration typeConfiguration;
|
||||||
|
@ -972,7 +975,7 @@ public abstract class MockSessionFactory
|
||||||
return (DomainType<?>) elementType;
|
return (DomainType<?>) elementType;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new BasicTypeImpl<>(new UnknownBasicJavaType<>(Object.class), ObjectJdbcType.INSTANCE);
|
return OBJECT_BASIC_TYPE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue