HHH-18497 Add xmlquery function

This commit is contained in:
Christian Beikov 2024-09-21 02:13:20 +02:00
parent 574959a591
commit 449d002563
25 changed files with 358 additions and 0 deletions

View File

@ -2176,6 +2176,7 @@ it is necessary to enable the `hibernate.query.hql.xml_functions_enabled` config
| `xmlforest()` | Constructs an XML forest from the arguments | `xmlforest()` | Constructs an XML forest from the arguments
| `xmlconcat()` | Concatenates multiple XML fragments to each other | `xmlconcat()` | Concatenates multiple XML fragments to each other
| `xmlpi()` | Constructs an XML processing instruction | `xmlpi()` | Constructs an XML processing instruction
| `xmlquery()` | Extracts content from XML document using XQuery or XPath
|=== |===
@ -2300,6 +2301,30 @@ include::{xml-example-dir-hql}/XmlPiTest.java[tags=hql-xmlpi-content-example]
WARNING: SAP HANA, MySQL, MariaDB and HSQLDB do not support this function. WARNING: SAP HANA, MySQL, MariaDB and HSQLDB do not support this function.
[[hql-xmlquery-function]]
===== `xmlquery()`
Extracts content from an XML document using XQuery or XPath.
[[hql-xmlquery-bnf]]
[source, antlrv4, indent=0]
----
include::{extrasdir}/xmlquery_bnf.txt[]
----
The first argument represents the XQuery or XPath expression.
The second argument after the `passing` keyword represents the XML document.
[[hql-xmlquery-example]]
====
[source, java, indent=0]
----
include::{xml-example-dir-hql}/XmlQueryTest.java[tags=hql-xmlquery-example]
----
====
WARNING: SAP HANA, MySQL, MariaDB and HSQLDB do not support this function.
[[hql-user-defined-functions]] [[hql-user-defined-functions]]
==== Native and user-defined functions ==== Native and user-defined functions

View File

@ -0,0 +1 @@
"xmlquery(" expression "passing" expression ")"

View File

@ -446,6 +446,7 @@ public class DB2LegacyDialect extends Dialect {
functionFactory.xmlforest(); functionFactory.xmlforest();
functionFactory.xmlconcat(); functionFactory.xmlconcat();
functionFactory.xmlpi(); functionFactory.xmlpi();
functionFactory.xmlquery_db2();
} }
@Override @Override

View File

@ -331,6 +331,7 @@ public class OracleLegacyDialect extends Dialect {
functionFactory.xmlforest(); functionFactory.xmlforest();
functionFactory.xmlconcat(); functionFactory.xmlconcat();
functionFactory.xmlpi(); functionFactory.xmlpi();
functionFactory.xmlquery_oracle();
} }
@Override @Override

View File

@ -674,6 +674,7 @@ public class PostgreSQLLegacyDialect extends Dialect {
functionFactory.xmlforest(); functionFactory.xmlforest();
functionFactory.xmlconcat(); functionFactory.xmlconcat();
functionFactory.xmlpi(); functionFactory.xmlpi();
functionFactory.xmlquery_postgresql();
if ( getVersion().isSameOrAfter( 9, 4 ) ) { if ( getVersion().isSameOrAfter( 9, 4 ) ) {
functionFactory.makeDateTimeTimestamp(); functionFactory.makeDateTimeTimestamp();

View File

@ -418,6 +418,7 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
functionFactory.xmlforest_sqlserver(); functionFactory.xmlforest_sqlserver();
functionFactory.xmlconcat_sqlserver(); functionFactory.xmlconcat_sqlserver();
functionFactory.xmlpi_sqlserver(); functionFactory.xmlpi_sqlserver();
functionFactory.xmlquery_sqlserver();
if ( getVersion().isSameOrAfter( 14 ) ) { if ( getVersion().isSameOrAfter( 14 ) ) {
functionFactory.listagg_stringAggWithinGroup( "varchar(max)" ); functionFactory.listagg_stringAggWithinGroup( "varchar(max)" );
functionFactory.jsonArrayAgg_sqlserver( getVersion().isSameOrAfter( 16 ) ); functionFactory.jsonArrayAgg_sqlserver( getVersion().isSameOrAfter( 16 ) );

View File

@ -334,6 +334,7 @@ XMLATTRIBUTES : [xX] [mM] [lL] [aA] [tT] [tT] [rR] [iI] [bB] [uU] [tT] [eE] [sS
XMLELEMENT : [xX] [mM] [lL] [eE] [lL] [eE] [mM] [eE] [nN] [tT]; XMLELEMENT : [xX] [mM] [lL] [eE] [lL] [eE] [mM] [eE] [nN] [tT];
XMLFOREST : [xX] [mM] [lL] [fF] [oO] [rR] [eE] [sS] [tT]; XMLFOREST : [xX] [mM] [lL] [fF] [oO] [rR] [eE] [sS] [tT];
XMLPI : [xX] [mM] [lL] [pP] [iI]; XMLPI : [xX] [mM] [lL] [pP] [iI];
XMLQUERY : [xX] [mM] [lL] [qQ] [uU] [eE] [rR] [yY];
YEAR : [yY] [eE] [aA] [rR]; YEAR : [yY] [eE] [aA] [rR];
ZONED : [zZ] [oO] [nN] [eE] [dD]; ZONED : [zZ] [oO] [nN] [eE] [dD];

View File

@ -1721,6 +1721,7 @@ xmlFunction
: xmlelementFunction : xmlelementFunction
| xmlforestFunction | xmlforestFunction
| xmlpiFunction | xmlpiFunction
| xmlqueryFunction
; ;
/** /**
@ -1751,6 +1752,13 @@ xmlpiFunction
: XMLPI LEFT_PAREN NAME identifier (COMMA expression)? RIGHT_PAREN : XMLPI LEFT_PAREN NAME identifier (COMMA expression)? RIGHT_PAREN
; ;
/**
* The 'xmlquery()' function
*/
xmlqueryFunction
: XMLQUERY LEFT_PAREN expression PASSING expression RIGHT_PAREN
;
/** /**
* Support for "soft" keywords which may be used as identifiers * Support for "soft" keywords which may be used as identifiers
* *
@ -1961,6 +1969,7 @@ xmlpiFunction
| XMLELEMENT | XMLELEMENT
| XMLFOREST | XMLFOREST
| XMLPI | XMLPI
| XMLQUERY
| YEAR | YEAR
| ZONED) { | ZONED) {
logUseOfReservedWordAsIdentifier( getCurrentToken() ); logUseOfReservedWordAsIdentifier( getCurrentToken() );

View File

@ -431,6 +431,7 @@ public class DB2Dialect extends Dialect {
functionFactory.xmlforest(); functionFactory.xmlforest();
functionFactory.xmlconcat(); functionFactory.xmlconcat();
functionFactory.xmlpi(); functionFactory.xmlpi();
functionFactory.xmlquery_db2();
} }
@Override @Override

View File

@ -421,6 +421,7 @@ public class OracleDialect extends Dialect {
functionFactory.xmlforest(); functionFactory.xmlforest();
functionFactory.xmlconcat(); functionFactory.xmlconcat();
functionFactory.xmlpi(); functionFactory.xmlpi();
functionFactory.xmlquery_oracle();
} }
@Override @Override

View File

@ -635,6 +635,7 @@ public class PostgreSQLDialect extends Dialect {
functionFactory.xmlforest(); functionFactory.xmlforest();
functionFactory.xmlconcat(); functionFactory.xmlconcat();
functionFactory.xmlpi(); functionFactory.xmlpi();
functionFactory.xmlquery_postgresql();
functionFactory.makeDateTimeTimestamp(); functionFactory.makeDateTimeTimestamp();
// Note that PostgreSQL doesn't support the OVER clause for ordered set-aggregate functions // Note that PostgreSQL doesn't support the OVER clause for ordered set-aggregate functions

View File

@ -436,6 +436,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
functionFactory.xmlforest_sqlserver(); functionFactory.xmlforest_sqlserver();
functionFactory.xmlconcat_sqlserver(); functionFactory.xmlconcat_sqlserver();
functionFactory.xmlpi_sqlserver(); functionFactory.xmlpi_sqlserver();
functionFactory.xmlquery_sqlserver();
if ( getVersion().isSameOrAfter( 14 ) ) { if ( getVersion().isSameOrAfter( 14 ) ) {
functionFactory.listagg_stringAggWithinGroup( "varchar(max)" ); functionFactory.listagg_stringAggWithinGroup( "varchar(max)" );
functionFactory.jsonArrayAgg_sqlserver( getVersion().isSameOrAfter( 16 ) ); functionFactory.jsonArrayAgg_sqlserver( getVersion().isSameOrAfter( 16 ) );

View File

@ -157,14 +157,17 @@ import org.hibernate.dialect.function.xml.H2XmlConcatFunction;
import org.hibernate.dialect.function.xml.H2XmlElementFunction; import org.hibernate.dialect.function.xml.H2XmlElementFunction;
import org.hibernate.dialect.function.xml.H2XmlForestFunction; import org.hibernate.dialect.function.xml.H2XmlForestFunction;
import org.hibernate.dialect.function.xml.H2XmlPiFunction; import org.hibernate.dialect.function.xml.H2XmlPiFunction;
import org.hibernate.dialect.function.xml.PostgreSQLXmlQueryFunction;
import org.hibernate.dialect.function.xml.SQLServerXmlConcatFunction; import org.hibernate.dialect.function.xml.SQLServerXmlConcatFunction;
import org.hibernate.dialect.function.xml.SQLServerXmlElementFunction; import org.hibernate.dialect.function.xml.SQLServerXmlElementFunction;
import org.hibernate.dialect.function.xml.SQLServerXmlForestFunction; import org.hibernate.dialect.function.xml.SQLServerXmlForestFunction;
import org.hibernate.dialect.function.xml.SQLServerXmlPiFunction; import org.hibernate.dialect.function.xml.SQLServerXmlPiFunction;
import org.hibernate.dialect.function.xml.SQLServerXmlQueryFunction;
import org.hibernate.dialect.function.xml.XmlConcatFunction; import org.hibernate.dialect.function.xml.XmlConcatFunction;
import org.hibernate.dialect.function.xml.XmlElementFunction; import org.hibernate.dialect.function.xml.XmlElementFunction;
import org.hibernate.dialect.function.xml.XmlForestFunction; import org.hibernate.dialect.function.xml.XmlForestFunction;
import org.hibernate.dialect.function.xml.XmlPiFunction; import org.hibernate.dialect.function.xml.XmlPiFunction;
import org.hibernate.dialect.function.xml.XmlQueryFunction;
import org.hibernate.query.sqm.function.SqmFunctionRegistry; import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator; import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator;
import org.hibernate.query.sqm.produce.function.FunctionParameterType; import org.hibernate.query.sqm.produce.function.FunctionParameterType;
@ -4215,4 +4218,32 @@ public class CommonFunctionFactory {
public void xmlpi_sqlserver() { public void xmlpi_sqlserver() {
functionRegistry.register( "xmlpi", new SQLServerXmlPiFunction( typeConfiguration ) ); functionRegistry.register( "xmlpi", new SQLServerXmlPiFunction( typeConfiguration ) );
} }
/**
* Oracle xmlquery() function
*/
public void xmlquery_oracle() {
functionRegistry.register( "xmlquery", new XmlQueryFunction( true, typeConfiguration ) );
}
/**
* DB2 xmlquery() function
*/
public void xmlquery_db2() {
functionRegistry.register( "xmlquery", new XmlQueryFunction( false, typeConfiguration ) );
}
/**
* PostgreSQL xmlquery() function
*/
public void xmlquery_postgresql() {
functionRegistry.register( "xmlquery", new PostgreSQLXmlQueryFunction( typeConfiguration ) );
}
/**
* SQL Server xmlquery() function
*/
public void xmlquery_sqlserver() {
functionRegistry.register( "xmlquery", new SQLServerXmlQueryFunction( typeConfiguration ) );
}
} }

View File

@ -35,6 +35,13 @@ public class ExpressionTypeHelper {
&& expressionType.getSingleJdbcMapping().getJdbcType().isJson(); && expressionType.getSingleJdbcMapping().getJdbcType().isJson();
} }
public static boolean isXml(SqlAstNode node) {
final Expression expression = (Expression) node;
final JdbcMappingContainer expressionType = expression.getExpressionType();
return expressionType.getJdbcTypeCount() == 1
&& expressionType.getSingleJdbcMapping().getJdbcType().isXml();
}
public static JdbcType getSingleJdbcType(SqlAstNode node) { public static JdbcType getSingleJdbcType(SqlAstNode node) {
final Expression expression = (Expression) node; final Expression expression = (Expression) node;
final JdbcMappingContainer expressionType = expression.getExpressionType(); final JdbcMappingContainer expressionType = expression.getExpressionType();

View File

@ -0,0 +1,46 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.dialect.function.xml;
import java.util.List;
import org.hibernate.dialect.function.json.ExpressionTypeHelper;
import org.hibernate.query.ReturnableType;
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.Expression;
import org.hibernate.type.spi.TypeConfiguration;
/**
* PostgreSQL xmlquery function.
*/
public class PostgreSQLXmlQueryFunction extends XmlQueryFunction {
public PostgreSQLXmlQueryFunction(TypeConfiguration typeConfiguration) {
super( false, typeConfiguration );
}
@Override
public void render(
SqlAppender sqlAppender,
List<? extends SqlAstNode> sqlAstArguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
final Expression xmlDocument = (Expression) sqlAstArguments.get( 1 );
final boolean needsCast = !ExpressionTypeHelper.isXml( xmlDocument );
sqlAppender.appendSql( "(select xmlagg(v) from unnest(xpath(" );
sqlAstArguments.get( 0 ).accept( walker );
sqlAppender.appendSql( ',' );
if ( needsCast ) {
sqlAppender.appendSql( "cast(" );
}
sqlAstArguments.get( 1 ).accept( walker );
if ( needsCast ) {
sqlAppender.appendSql( " as xml)" );
}
sqlAppender.appendSql( ")) t(v))" );
}
}

View File

@ -0,0 +1,45 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.dialect.function.xml;
import java.util.List;
import org.hibernate.dialect.function.json.ExpressionTypeHelper;
import org.hibernate.query.ReturnableType;
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.Expression;
import org.hibernate.type.spi.TypeConfiguration;
/**
* SQL Server xmlquery function.
*/
public class SQLServerXmlQueryFunction extends XmlQueryFunction {
public SQLServerXmlQueryFunction(TypeConfiguration typeConfiguration) {
super( false, typeConfiguration );
}
@Override
public void render(
SqlAppender sqlAppender,
List<? extends SqlAstNode> sqlAstArguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
final Expression xmlDocument = (Expression) sqlAstArguments.get( 1 );
final boolean needsCast = !ExpressionTypeHelper.isXml( xmlDocument );
if ( needsCast ) {
sqlAppender.appendSql( "cast(" );
}
sqlAstArguments.get( 1 ).accept( walker );
if ( needsCast ) {
sqlAppender.appendSql( " as xml)" );
}
sqlAppender.appendSql( ".query(" );
sqlAstArguments.get( 0 ).accept( walker );
sqlAppender.appendSql( ')' );
}
}

View File

@ -0,0 +1,73 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.dialect.function.xml;
import java.util.List;
import org.hibernate.dialect.function.json.ExpressionTypeHelper;
import org.hibernate.query.ReturnableType;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.function.FunctionKind;
import org.hibernate.query.sqm.produce.function.ArgumentTypesValidator;
import org.hibernate.query.sqm.produce.function.StandardArgumentsValidators;
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
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.Expression;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.spi.TypeConfiguration;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.IMPLICIT_XML;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.XML;
/**
* Standard xmlquery function.
*/
public class XmlQueryFunction extends AbstractSqmSelfRenderingFunctionDescriptor {
private final boolean returningContent;
public XmlQueryFunction(boolean returningContent, TypeConfiguration typeConfiguration) {
super(
"xmlquery",
FunctionKind.NORMAL,
StandardArgumentsValidators.composite(
new ArgumentTypesValidator( null, STRING, IMPLICIT_XML )
),
StandardFunctionReturnTypeResolvers.invariant(
typeConfiguration.getBasicTypeRegistry().resolve( String.class, SqlTypes.SQLXML )
),
StandardFunctionArgumentTypeResolvers.invariant( typeConfiguration, STRING, XML )
);
this.returningContent = returningContent;
}
@Override
public void render(
SqlAppender sqlAppender,
List<? extends SqlAstNode> sqlAstArguments,
ReturnableType<?> returnType,
SqlAstTranslator<?> walker) {
final Expression xmlDocument = (Expression) sqlAstArguments.get( 1 );
final boolean needsCast = !ExpressionTypeHelper.isXml( xmlDocument );
sqlAppender.appendSql( "xmlquery(" );
sqlAstArguments.get( 0 ).accept( walker );
sqlAppender.appendSql( " passing " );
if ( needsCast ) {
sqlAppender.appendSql( "xmlparse(document " );
}
sqlAstArguments.get( 1 ).accept( walker );
if ( needsCast ) {
sqlAppender.appendSql( ')' );
}
if ( returningContent ) {
sqlAppender.appendSql( " returning content" );
}
sqlAppender.appendSql( ')' );
}
}

View File

@ -4113,6 +4113,22 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
@Incubating @Incubating
JpaExpression<String> xmlpi(String elementName, Expression<String> content); JpaExpression<String> xmlpi(String elementName, Expression<String> content);
/**
* Queries the given XML document with the given XPath or XQuery query.
*
* @since 7.0
*/
@Incubating
JpaExpression<String> xmlquery(String query, Expression<?> xmlDocument);
/**
* Queries the given XML document with the given XPath or XQuery query.
*
* @since 7.0
*/
@Incubating
JpaExpression<String> xmlquery(Expression<String> query, Expression<?> xmlDocument);
/** /**
* Creates a named expression. The name is important for the result of the expression, * Creates a named expression. The name is important for the result of the expression,
* e.g. when building an {@code xmlforest}, the name acts as the XML element name. * e.g. when building an {@code xmlforest}, the name acts as the XML element name.

View File

@ -3681,6 +3681,18 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
return criteriaBuilder.xmlpi( elementName, content ); return criteriaBuilder.xmlpi( elementName, content );
} }
@Override
@Incubating
public JpaExpression<String> xmlquery(String query, Expression<?> xmlDocument) {
return criteriaBuilder.xmlquery( query, xmlDocument );
}
@Override
@Incubating
public JpaExpression<String> xmlquery(Expression<String> query, Expression<?> xmlDocument) {
return criteriaBuilder.xmlquery( query, xmlDocument );
}
@Override @Override
@Incubating @Incubating
public <T> JpaExpression<T> named(Expression<T> expression, String name) { public <T> JpaExpression<T> named(Expression<T> expression, String name) {

View File

@ -3043,6 +3043,14 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
: creationContext.getNodeBuilder().xmlpi( name, (Expression<String>) exprCtx.accept( this ) ); : creationContext.getNodeBuilder().xmlpi( name, (Expression<String>) exprCtx.accept( this ) );
} }
@Override
public SqmExpression<?> visitXmlqueryFunction(HqlParser.XmlqueryFunctionContext ctx) {
checkXmlFunctionsEnabled( ctx );
final SqmExpression<String> query = (SqmExpression<String>) ctx.expression( 0 ).accept( this );
final SqmExpression<?> xmlDocument = (SqmExpression<?>) ctx.expression( 1 ).accept( this );
return creationContext.getNodeBuilder().xmlquery( query, xmlDocument );
}
private void checkXmlFunctionsEnabled(ParserRuleContext ctx) { private void checkXmlFunctionsEnabled(ParserRuleContext ctx) {
if ( !creationOptions.isXmlFunctionsEnabled() ) { if ( !creationOptions.isXmlFunctionsEnabled() ) {
throw new SemanticException( throw new SemanticException(

View File

@ -777,6 +777,12 @@ public interface NodeBuilder extends HibernateCriteriaBuilder, BindingContext {
@Override @Override
SqmExpression<String> xmlpi(String elementName, Expression<String> content); SqmExpression<String> xmlpi(String elementName, Expression<String> content);
@Override
SqmExpression<String> xmlquery(String query, Expression<?> xmlDocument);
@Override
SqmExpression<String> xmlquery(Expression<String> query, Expression<?> xmlDocument);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Covariant overrides // Covariant overrides

View File

@ -5764,4 +5764,18 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, Serializable {
queryEngine queryEngine
); );
} }
@Override
public SqmExpression<String> xmlquery(String query, Expression<?> xmlDocument) {
return xmlquery( value( query ), xmlDocument );
}
@Override
public SqmExpression<String> xmlquery(Expression<String> query, Expression<?> xmlDocument) {
return getFunctionDescriptor( "xmlquery" ).generateSqmExpression(
asList( (SqmTypedNode<?>) query, (SqmTypedNode<?>) xmlDocument ),
null,
queryEngine
);
}
} }

View File

@ -0,0 +1,36 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.function.xml;
import org.hibernate.cfg.QuerySettings;
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
/**
* @author Christian Beikov
*/
@DomainModel
@SessionFactory
@ServiceRegistry(settings = @Setting(name = QuerySettings.XML_FUNCTIONS_ENABLED, value = "true"))
@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsXmlquery.class)
public class XmlQueryTest {
@Test
public void testSimple(SessionFactoryScope scope) {
scope.inSession( em -> {
//tag::hql-xmlquery-example[]
em.createQuery( "select xmlquery('/a/val' passing '<a><val>asd</val></a>')" ).getResultList();
//end::hql-xmlquery-example[]
} );
}
}

View File

@ -201,6 +201,20 @@ public class XmlFunctionTests {
); );
} }
@Test
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsXmlquery.class)
public void testXmlquery(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
Tuple tuple = session.createQuery(
"select xmlquery('/a/val' passing '<a><val>asd</val></a>')",
Tuple.class
).getSingleResult();
assertXmlEquals( "<val>asd</val>", tuple.get( 0, String.class ) );
}
);
}
private void assertXmlEquals(String expected, String actual) { private void assertXmlEquals(String expected, String actual) {
final Document expectedDoc = parseXml( xmlNormalize( expected ) ); final Document expectedDoc = parseXml( xmlNormalize( expected ) );
final Document actualDoc = parseXml( xmlNormalize( actual ) ); final Document actualDoc = parseXml( xmlNormalize( actual ) );

View File

@ -868,6 +868,12 @@ abstract public class DialectFeatureChecks {
} }
} }
public static class SupportsXmlquery implements DialectFeatureCheck {
public boolean apply(Dialect dialect) {
return definesFunction( dialect, "xmlquery" );
}
}
public static class IsJtds implements DialectFeatureCheck { public static class IsJtds implements DialectFeatureCheck {
public boolean apply(Dialect dialect) { public boolean apply(Dialect dialect) {
return dialect instanceof SybaseDialect && ( (SybaseDialect) dialect ).getDriverKind() == SybaseDriverKind.JTDS; return dialect instanceof SybaseDialect && ( (SybaseDialect) dialect ).getDriverKind() == SybaseDriverKind.JTDS;