From 1a5db3bf42510077f0cb97b0d739be8776580ae5 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 3 Dec 2024 21:18:01 +0100 Subject: [PATCH] HHH-18892 add hex() function --- .../src/main/asciidoc/querylanguage/Expressions.adoc | 1 + .../java/org/hibernate/dialect/CockroachDialect.java | 1 + .../main/java/org/hibernate/dialect/DB2Dialect.java | 1 + .../main/java/org/hibernate/dialect/H2Dialect.java | 1 + .../main/java/org/hibernate/dialect/HANADialect.java | 1 + .../main/java/org/hibernate/dialect/HSQLDialect.java | 2 ++ .../java/org/hibernate/dialect/MySQLDialect.java | 1 + .../java/org/hibernate/dialect/OracleDialect.java | 1 + .../org/hibernate/dialect/PostgreSQLDialect.java | 1 + .../java/org/hibernate/dialect/SQLServerDialect.java | 1 + .../dialect/function/CommonFunctionFactory.java | 11 ++++++++++- .../sqm/produce/function/ArgumentTypesValidator.java | 1 + .../sqm/produce/function/FunctionParameterType.java | 4 ++++ .../hibernate/orm/test/query/hql/FunctionTests.java | 12 ++++++++++++ 14 files changed, 38 insertions(+), 1 deletion(-) diff --git a/documentation/src/main/asciidoc/querylanguage/Expressions.adoc b/documentation/src/main/asciidoc/querylanguage/Expressions.adoc index a8c2f75836..962b771460 100644 --- a/documentation/src/main/asciidoc/querylanguage/Expressions.adoc +++ b/documentation/src/main/asciidoc/querylanguage/Expressions.adoc @@ -841,6 +841,7 @@ Naturally, there are a good number of functions for working with strings. | `replace()` | Replace every occurrence of a pattern in a string | `replace(str, patt, rep)` | ✔ / ✖ | `repeat()` | Concatenate a string with itself multiple times | `repeat(str, times)` | ✖ / ✖ | `collate()` | Select a collation | `collate(p.name as collation)` | ✖ / ✖ +| `hex()` | Encode a binary value as a hexadecimal string | `hex(image.bytes)` | ✖ / ✖ |=== Let's take a closer look at just some of these. diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index 975a2ee6ab..c40d50ca4b 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -503,6 +503,7 @@ public class CockroachDialect extends Dialect { ); functionContributions.getFunctionRegistry().registerAlternateKey( "truncate", "trunc" ); + functionFactory.hex( "encode(?1, 'hex')" ); functionFactory.sha( "digest(?1, 'sha256')" ); functionFactory.md5( "digest(?1, 'md5')" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java index c93ffc9c9f..df81723669 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -453,6 +453,7 @@ public class DB2Dialect extends Dialect { functionFactory.unnest_db2( getMaximumSeriesSize() ); functionFactory.generateSeries_recursive( getMaximumSeriesSize(), false, true ); + functionFactory.hex( "hex(?1)" ); functionFactory.sha( "hash(?1, 2)" ); functionFactory.md5( "hash(?1, 0)" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index d11938e4fb..c95fcea4f9 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -369,6 +369,7 @@ public class H2Dialect extends Dialect { functionFactory.generateSeries_h2( getMaximumSeriesSize() ); functionFactory.jsonTable_h2( getMaximumArraySize() ); + functionFactory.hex( "rawtohex(?1)" ); functionFactory.sha( "hash('SHA-256', ?1)" ); functionFactory.md5( "hash('MD5', ?1)" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java index 2672717fe2..1a17f47c44 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HANADialect.java @@ -518,6 +518,7 @@ public class HANADialect extends Dialect { // functionFactory.xmlextract(); functionFactory.generateSeries_hana( getMaximumSeriesSize() ); + functionFactory.hex( "to_hex(?1)" ); functionFactory.sha( "hash_sha256(?1)" ); functionFactory.md5( "hash_md5(?1)" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java index c913615e81..819320c146 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java @@ -222,6 +222,8 @@ public class HSQLDialect extends Dialect { functionContributions.getTypeConfiguration(), SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER ) ); + + functionFactory.hex( "hex(?1)" ); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java index 681052d5ad..fc6305a52c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -687,6 +687,7 @@ public class MySQLDialect extends Dialect { functionFactory.generateSeries_recursive( getMaximumSeriesSize(), false, false ); } + functionFactory.hex( "hex(?1)" ); functionFactory.sha( "unhex(sha2(?1, 256))" ); functionFactory.md5( "unhex(md5(?1))" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index 518d839fb0..0c147bd9b5 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -441,6 +441,7 @@ public class OracleDialect extends Dialect { functionFactory.generateSeries_recursive( getMaximumSeriesSize(), true, false ); functionFactory.jsonTable_oracle(); + functionFactory.hex( "rawtohex(?1)" ); functionFactory.sha( "standard_hash(?1, 'SHA256')" ); functionFactory.md5( "standard_hash(?1, 'MD5')" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index 3e3c867bae..690fe89520 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -690,6 +690,7 @@ public class PostgreSQLDialect extends Dialect { } functionFactory.generateSeries( null, "ordinality", false ); + functionFactory.hex( "encode(?1, 'hex')" ); functionFactory.sha( "sha256(?1)" ); functionFactory.md5( "decode(md5(?1), 'hex')" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java index 4f73bc3f57..b9812f9ac3 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -488,6 +488,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { } } + functionFactory.hex( "convert(varchar(MAX), ?1, 2)" ); functionFactory.sha( "hashbytes('SHA2_256', ?1)" ); functionFactory.md5( "hashbytes('MD5', ?1)" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java b/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java index 9bf039e361..c21bbc1008 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/function/CommonFunctionFactory.java @@ -389,7 +389,8 @@ public class CommonFunctionFactory { } /** - * CockroachDB lacks implicit casting: https://github.com/cockroachdb/cockroach/issues/89965 + * CockroachDB lacks + * implicit casting */ public void median_percentileCont_castDouble() { functionRegistry.patternDescriptorBuilder( @@ -2325,6 +2326,14 @@ public class CommonFunctionFactory { .register(); } + public void hex(String pattern) { + functionRegistry.patternDescriptorBuilder( "hex", pattern ) + .setInvariantType(stringType) + .setParameterTypes( BINARY ) + .setExactArgumentCount( 1 ) + .register(); + } + public void md5(String pattern) { functionRegistry.patternDescriptorBuilder( "md5", pattern ) .setInvariantType(binaryType) diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java index 721f49f3c4..dc7b2dc9fa 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/ArgumentTypesValidator.java @@ -248,6 +248,7 @@ public class ArgumentTypesValidator implements ArgumentsValidator { case TEMPORAL -> jdbcType.isTemporal(); case DATE -> jdbcType.hasDatePart(); case TIME -> jdbcType.hasTimePart(); + case BINARY -> jdbcType.isBinary(); case SPATIAL -> jdbcType.isSpatial(); case JSON -> jdbcType.isJson(); case IMPLICIT_JSON -> jdbcType.isImplicitJson(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/FunctionParameterType.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/FunctionParameterType.java index 5667c64e91..14f1af14f9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/FunctionParameterType.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/produce/function/FunctionParameterType.java @@ -48,6 +48,10 @@ public enum FunctionParameterType { * a logical expression (predicate) */ BOOLEAN, + /** + * @see org.hibernate.type.SqlTypes#isBinaryType(int) + */ + BINARY, /** * Indicates a parameter that accepts any type */ diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java index 6004ff6a1e..f1f3f6c622 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/query/hql/FunctionTests.java @@ -23,6 +23,7 @@ import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.dialect.PostgresPlusDialect; import org.hibernate.dialect.SQLServerDialect; +import org.hibernate.dialect.SybaseASEDialect; import org.hibernate.dialect.SybaseDialect; import org.hibernate.dialect.TiDBDialect; import org.hibernate.query.sqm.produce.function.FunctionArgumentException; @@ -62,6 +63,7 @@ import java.time.ZoneOffset; import java.time.temporal.ChronoUnit; import java.util.Date; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.UUID; @@ -2627,4 +2629,14 @@ public class FunctionTests { } }); } + + @Test + @SkipForDialect(dialectClass = SybaseASEDialect.class) + public void testHexFunction(SessionFactoryScope scope) { + scope.inTransaction(s -> { + assertEquals( "DEADBEEF", + s.createSelectionQuery("select hex({0xDE, 0xAD, 0xBE, 0xEF})", String.class) + .getSingleResult().toUpperCase( Locale.ROOT ) ); + }); + } }