HHH-18497 Add xmlcomment function
This commit is contained in:
parent
4baba673cb
commit
f10ec5db1d
|
@ -2172,6 +2172,7 @@ it is necessary to enable the `hibernate.query.hql.xml_functions_enabled` config
|
||||||
| Function | Purpose
|
| Function | Purpose
|
||||||
|
|
||||||
| `xmlelement()` | Constructs an XML element from arguments
|
| `xmlelement()` | Constructs an XML element from arguments
|
||||||
|
| `xmlcomment()` | Constructs an XML comment from the single argument
|
||||||
|===
|
|===
|
||||||
|
|
||||||
|
|
||||||
|
@ -2209,6 +2210,21 @@ include::{xml-example-dir-hql}/XmlElementTest.java[tags=hql-xmlelement-attribute
|
||||||
|
|
||||||
WARNING: SAP HANA, MySQL, MariaDB, H2 and HSQLDB do not support this function.
|
WARNING: SAP HANA, MySQL, MariaDB, H2 and HSQLDB do not support this function.
|
||||||
|
|
||||||
|
[[hql-xmlcomment-function]]
|
||||||
|
===== `xmlcomment()`
|
||||||
|
|
||||||
|
Constructs an XML comment from the single string argument.
|
||||||
|
|
||||||
|
[[hql-xmlcomment-example]]
|
||||||
|
====
|
||||||
|
[source, java, indent=0]
|
||||||
|
----
|
||||||
|
include::{xml-example-dir-hql}/XmlCommentTest.java[tags=hql-xmlcomment-example]
|
||||||
|
----
|
||||||
|
====
|
||||||
|
|
||||||
|
WARNING: SAP HANA, MySQL, MariaDB, H2 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
|
||||||
|
|
||||||
|
|
|
@ -442,6 +442,7 @@ public class DB2LegacyDialect extends Dialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
functionFactory.xmlelement();
|
functionFactory.xmlelement();
|
||||||
|
functionFactory.xmlcomment();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -419,6 +419,7 @@ public class H2LegacyDialect extends Dialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
functionFactory.xmlelement_h2();
|
functionFactory.xmlelement_h2();
|
||||||
|
functionFactory.xmlcomment();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
functionFactory.listagg_groupConcat();
|
functionFactory.listagg_groupConcat();
|
||||||
|
|
|
@ -327,6 +327,7 @@ public class OracleLegacyDialect extends Dialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
functionFactory.xmlelement();
|
functionFactory.xmlelement();
|
||||||
|
functionFactory.xmlcomment();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -670,6 +670,7 @@ public class PostgreSQLLegacyDialect extends Dialect {
|
||||||
functionFactory.jsonArrayInsert_postgresql();
|
functionFactory.jsonArrayInsert_postgresql();
|
||||||
|
|
||||||
functionFactory.xmlelement();
|
functionFactory.xmlelement();
|
||||||
|
functionFactory.xmlcomment();
|
||||||
|
|
||||||
if ( getVersion().isSameOrAfter( 9, 4 ) ) {
|
if ( getVersion().isSameOrAfter( 9, 4 ) ) {
|
||||||
functionFactory.makeDateTimeTimestamp();
|
functionFactory.makeDateTimeTimestamp();
|
||||||
|
|
|
@ -414,6 +414,7 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
||||||
functionFactory.jsonArrayInsert_sqlserver();
|
functionFactory.jsonArrayInsert_sqlserver();
|
||||||
}
|
}
|
||||||
functionFactory.xmlelement_sqlserver();
|
functionFactory.xmlelement_sqlserver();
|
||||||
|
functionFactory.xmlcomment_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 ) );
|
||||||
|
|
|
@ -427,6 +427,7 @@ public class DB2Dialect extends Dialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
functionFactory.xmlelement();
|
functionFactory.xmlelement();
|
||||||
|
functionFactory.xmlcomment();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -354,6 +354,7 @@ public class H2Dialect extends Dialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
functionFactory.xmlelement_h2();
|
functionFactory.xmlelement_h2();
|
||||||
|
functionFactory.xmlcomment();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -417,6 +417,7 @@ public class OracleDialect extends Dialect {
|
||||||
functionFactory.jsonArrayInsert_oracle();
|
functionFactory.jsonArrayInsert_oracle();
|
||||||
|
|
||||||
functionFactory.xmlelement();
|
functionFactory.xmlelement();
|
||||||
|
functionFactory.xmlcomment();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -631,6 +631,7 @@ public class PostgreSQLDialect extends Dialect {
|
||||||
functionFactory.jsonArrayInsert_postgresql();
|
functionFactory.jsonArrayInsert_postgresql();
|
||||||
|
|
||||||
functionFactory.xmlelement();
|
functionFactory.xmlelement();
|
||||||
|
functionFactory.xmlcomment();
|
||||||
|
|
||||||
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
|
||||||
|
|
|
@ -432,6 +432,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
||||||
functionFactory.jsonArrayInsert_sqlserver();
|
functionFactory.jsonArrayInsert_sqlserver();
|
||||||
}
|
}
|
||||||
functionFactory.xmlelement_sqlserver();
|
functionFactory.xmlelement_sqlserver();
|
||||||
|
functionFactory.xmlcomment_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 ) );
|
||||||
|
|
|
@ -4121,4 +4121,26 @@ public class CommonFunctionFactory {
|
||||||
public void xmlelement_sqlserver() {
|
public void xmlelement_sqlserver() {
|
||||||
functionRegistry.register( "xmlelement", new SQLServerXmlElementFunction( typeConfiguration ) );
|
functionRegistry.register( "xmlelement", new SQLServerXmlElementFunction( typeConfiguration ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard xmlcomment() function
|
||||||
|
*/
|
||||||
|
public void xmlcomment() {
|
||||||
|
functionRegistry.namedDescriptorBuilder( "xmlcomment" )
|
||||||
|
.setExactArgumentCount( 1 )
|
||||||
|
.setParameterTypes( STRING )
|
||||||
|
.setInvariantType( typeConfiguration.getBasicTypeRegistry().resolve( String.class, SqlTypes.SQLXML ) )
|
||||||
|
.register();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard xmlcomment() function
|
||||||
|
*/
|
||||||
|
public void xmlcomment_sqlserver() {
|
||||||
|
functionRegistry.patternDescriptorBuilder( "xmlcomment", "cast(('<!--'+?1+'-->') AS xml)" )
|
||||||
|
.setExactArgumentCount( 1 )
|
||||||
|
.setParameterTypes( STRING )
|
||||||
|
.setInvariantType( typeConfiguration.getBasicTypeRegistry().resolve( String.class, SqlTypes.SQLXML ) )
|
||||||
|
.register();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4055,6 +4055,14 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
|
||||||
@Incubating
|
@Incubating
|
||||||
JpaXmlElementExpression xmlelement(String elementName);
|
JpaXmlElementExpression xmlelement(String elementName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an XML comment with the given argument as content.
|
||||||
|
*
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
@Incubating
|
||||||
|
JpaExpression<String> xmlcomment(String comment);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
JpaPredicate and(List<Predicate> restrictions);
|
JpaPredicate and(List<Predicate> restrictions);
|
||||||
|
|
||||||
|
|
|
@ -3638,4 +3638,10 @@ public class HibernateCriteriaBuilderDelegate implements HibernateCriteriaBuilde
|
||||||
public JpaXmlElementExpression xmlelement(String elementName) {
|
public JpaXmlElementExpression xmlelement(String elementName) {
|
||||||
return criteriaBuilder.xmlelement( elementName );
|
return criteriaBuilder.xmlelement( elementName );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Incubating
|
||||||
|
public JpaExpression<String> xmlcomment(String comment) {
|
||||||
|
return criteriaBuilder.xmlcomment( comment );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -753,6 +753,9 @@ public interface NodeBuilder extends HibernateCriteriaBuilder, BindingContext {
|
||||||
@Override
|
@Override
|
||||||
SqmXmlElementExpression xmlelement(String elementName);
|
SqmXmlElementExpression xmlelement(String elementName);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
SqmExpression<String> xmlcomment(String comment);
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// Covariant overrides
|
// Covariant overrides
|
||||||
|
|
||||||
|
|
|
@ -5687,4 +5687,13 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, Serializable {
|
||||||
queryEngine
|
queryEngine
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmExpression<String> xmlcomment(String comment) {
|
||||||
|
return getFunctionDescriptor( "xmlcomment" ).generateSqmExpression(
|
||||||
|
List.of( value( comment ) ),
|
||||||
|
null,
|
||||||
|
queryEngine
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.SupportsXmlcomment.class)
|
||||||
|
public class XmlCommentTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimple(SessionFactoryScope scope) {
|
||||||
|
scope.inSession( em -> {
|
||||||
|
//tag::hql-xmlcomment-example[]
|
||||||
|
em.createQuery( "select xmlcomment('This is my comment')" ).getResultList();
|
||||||
|
//end::hql-xmlcomment-example[]
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -43,7 +43,6 @@ import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.Tuple;
|
import jakarta.persistence.Tuple;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Element;
|
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
import org.xml.sax.InputSource;
|
import org.xml.sax.InputSource;
|
||||||
|
@ -137,24 +136,40 @@ public class XmlFunctionTests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertXmlEquals(String doc1, String doc2) {
|
@Test
|
||||||
final Document d1 = parseXml( xmlNormalize( doc1 ) );
|
@RequiresDialectFeature(feature = DialectFeatureChecks.SupportsXmlcomment.class)
|
||||||
final Document d2 = parseXml( xmlNormalize( doc2 ) );
|
public void testXmlcomment(SessionFactoryScope scope) {
|
||||||
normalize( d1 );
|
scope.inTransaction(
|
||||||
normalize( d2 );
|
session -> {
|
||||||
assertEquals( toXml( d1 ), toXml( d2 ) );
|
Tuple tuple = session.createQuery(
|
||||||
|
"select " +
|
||||||
|
"xmlcomment('Abc'), " +
|
||||||
|
"xmlcomment('<>')",
|
||||||
|
Tuple.class
|
||||||
|
).getSingleResult();
|
||||||
|
assertXmlEquals( "<!--Abc--><a/>", tuple.get( 0, String.class ) + "<a/>" );
|
||||||
|
assertXmlEquals( "<!--<>--><a/>", tuple.get( 1 , String.class ) + "<a/>" );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertXmlEquals(String expected, String actual) {
|
||||||
|
final Document expectedDoc = parseXml( xmlNormalize( expected ) );
|
||||||
|
final Document actualDoc = parseXml( xmlNormalize( actual ) );
|
||||||
|
normalize( expectedDoc );
|
||||||
|
normalize( actualDoc );
|
||||||
|
assertEquals( toXml( expectedDoc ).trim(), toXml( actualDoc ).trim() );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void normalize(Document document) {
|
private void normalize(Document document) {
|
||||||
normalize( document.getDocumentElement() );
|
normalize( document.getChildNodes() );
|
||||||
}
|
}
|
||||||
|
|
||||||
private void normalize(Element element) {
|
private void normalize(NodeList childNodes) {
|
||||||
final NodeList childNodes = element.getChildNodes();
|
|
||||||
for ( int i = 0; i < childNodes.getLength(); i++ ) {
|
for ( int i = 0; i < childNodes.getLength(); i++ ) {
|
||||||
final Node childNode = childNodes.item( i );
|
final Node childNode = childNodes.item( i );
|
||||||
if ( childNode.getNodeType() == Node.ELEMENT_NODE ) {
|
if ( childNode.getNodeType() == Node.ELEMENT_NODE ) {
|
||||||
normalize( (Element) childNode );
|
normalize( childNode.getChildNodes() );
|
||||||
}
|
}
|
||||||
else if ( childNode.getNodeType() == Node.TEXT_NODE ) {
|
else if ( childNode.getNodeType() == Node.TEXT_NODE ) {
|
||||||
if ( childNode.getNodeValue().isBlank() ) {
|
if ( childNode.getNodeValue().isBlank() ) {
|
||||||
|
@ -164,6 +179,9 @@ public class XmlFunctionTests {
|
||||||
childNode.setNodeValue( childNode.getNodeValue().trim() );
|
childNode.setNodeValue( childNode.getNodeValue().trim() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if ( childNode.getNodeType() == Node.COMMENT_NODE ) {
|
||||||
|
childNode.setNodeValue( childNode.getNodeValue().trim() );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -844,6 +844,12 @@ abstract public class DialectFeatureChecks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class SupportsXmlcomment implements DialectFeatureCheck {
|
||||||
|
public boolean apply(Dialect dialect) {
|
||||||
|
return definesFunction( dialect, "xmlcomment" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
Loading…
Reference in New Issue