Skip to content

Commit d5a3f04

Browse files
committed
HHH-18496 Add json_value function
1 parent ff57a6c commit d5a3f04

File tree

51 files changed

+2859
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+2859
-1
lines changed

documentation/src/main/asciidoc/userguide/chapters/query/hql/QueryLanguage.adoc

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
:core-project-dir: {root-project-dir}/hibernate-core
77
:example-dir-hql: {core-project-dir}/src/test/java/org/hibernate/orm/test/hql
88
:array-example-dir-hql: {core-project-dir}/src/test/java/org/hibernate/orm/test/function/array
9+
:json-example-dir-hql: {core-project-dir}/src/test/java/org/hibernate/orm/test/function/json
910
:extrasdir: extras
1011

1112
This chapter describes Hibernate Query Language (HQL) and Jakarta Persistence Query Language (JPQL).
@@ -1619,6 +1620,85 @@ include::{array-example-dir-hql}/ArrayToStringTest.java[tags=hql-array-to-string
16191620
----
16201621
====
16211622
1623+
[[hql-functions-json]]
1624+
==== Functions for dealing with JSON
1625+
1626+
The following functions deal with SQL JSON types, which are not supported on every database.
1627+
1628+
[[hql-json-functions]]
1629+
|===
1630+
| Function | Purpose
1631+
1632+
| `json_value()` | Extracts a value from a JSON document by JSON path
1633+
|===
1634+
1635+
[[hql-json-value-function]]
1636+
===== `json_value()`
1637+
1638+
Extracts a value by https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html[JSON path] from a JSON document.
1639+
1640+
[[hql-like-json-value-bnf]]
1641+
[source, antlrv4, indent=0]
1642+
----
1643+
include::{extrasdir}/json_value_bnf.txt[]
1644+
----
1645+
1646+
The first argument is an expression to a JSON document. The second argument is a JSON path as String expression.
1647+
1648+
NOTE: It is recommended to only us the dot notation for JSON paths, since most databases support only that.
1649+
1650+
[[hql-json-value-example]]
1651+
====
1652+
[source, java, indent=0]
1653+
----
1654+
include::{json-example-dir-hql}/JsonValueTest.java[tags=hql-json-value-example]
1655+
----
1656+
====
1657+
1658+
The `returning` clause allows to specify the <<hql-function-cast,cast target>> i.e. the type of value to extract.
1659+
1660+
[[hql-json-value-returning-example]]
1661+
====
1662+
[source, java, indent=0]
1663+
----
1664+
include::{json-example-dir-hql}/JsonValueTest.java[tags=hql-json-value-returning-example]
1665+
----
1666+
====
1667+
1668+
The `on empty` clause defines the behavior when the JSON path does not match the JSON document.
1669+
By default, `null` is returned on empty.
1670+
1671+
[[hql-json-value-on-error-example]]
1672+
====
1673+
[source, java, indent=0]
1674+
----
1675+
include::{json-example-dir-hql}/JsonValueTest.java[tags=hql-json-value-on-error-example]
1676+
----
1677+
====
1678+
1679+
The `on error` clause defines the behavior when an error occurs while resolving the value for the JSON path.
1680+
Conditions that classify as errors are database dependent, but usual errors which can be handled with this clause are:
1681+
1682+
* First argument is not a valid JSON document
1683+
* Second argument is not a valid JSON path
1684+
* JSON path does not resolve to a scalar value
1685+
1686+
The default behavior of `on error` is database specific, but usually, `null` is returned on an error.
1687+
It is recommended to specify this clause when the exact error behavior is important.
1688+
1689+
[[hql-json-value-on-empty-example]]
1690+
====
1691+
[source, java, indent=0]
1692+
----
1693+
include::{json-example-dir-hql}/JsonValueTest.java[tags=hql-json-value-on-empty-example]
1694+
----
1695+
====
1696+
1697+
To actually receive an error `on empty`, it is necessary to also specify `error on error`.
1698+
Depending on the database, an error might still be thrown even without that, but that is not portable.
1699+
1700+
NOTE: The H2 emulation only supports absolute JSON paths using the dot notation.
1701+
16221702
[[hql-user-defined-functions]]
16231703
==== Native and user-defined functions
16241704
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"json_value(" expression, expression ("returning" castTarget)? onErrorClause? onEmptyClause? ")"
2+
3+
onErrorClause
4+
: ( "error" | "null" | ( "default" expression ) ) "on error";
5+
6+
onEmptyClause
7+
: ( "error" | "null" | ( "default" expression ) ) "on empty";

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/CockroachLegacyDialect.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,8 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
492492
functionFactory.arrayFill_cockroachdb();
493493
functionFactory.arrayToString_postgresql();
494494

495+
functionFactory.jsonValue_cockroachdb();
496+
495497
// Postgres uses # instead of ^ for XOR
496498
functionContributions.getFunctionRegistry().patternDescriptorBuilder( "bitxor", "(?1#?2)" )
497499
.setExactArgumentCount( 2 )

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/DB2LegacyDialect.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,10 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
429429
functionFactory.windowFunctions();
430430
if ( getDB2Version().isSameOrAfter( 9, 5 ) ) {
431431
functionFactory.listagg( null );
432+
433+
if ( getDB2Version().isSameOrAfter( 11 ) ) {
434+
functionFactory.jsonValue();
435+
}
432436
}
433437
}
434438

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/H2LegacyDialect.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,10 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
398398
functionFactory.arrayTrim_trim_array();
399399
functionFactory.arrayFill_h2();
400400
functionFactory.arrayToString_h2( getMaximumArraySize() );
401+
402+
if ( getVersion().isSameOrAfter( 2, 2, 220 ) ) {
403+
functionFactory.jsonValue_h2();
404+
}
401405
}
402406
else {
403407
// Use group_concat until 2.x as listagg was buggy

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacyDialect.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
8989
.getBasicTypeRegistry()
9090
.resolve( StandardBasicTypes.BOOLEAN )
9191
);
92+
commonFunctionFactory.jsonValue_mariadb();
9293
if ( getVersion().isSameOrAfter( 10, 3, 3 ) ) {
9394
commonFunctionFactory.inverseDistributionOrderedSetAggregates_windowEmulation();
9495
functionContributions.getFunctionRegistry().patternDescriptorBuilder( "median", "median(?1) over ()" )

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MySQLLegacyDialect.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,10 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
651651
functionRegistry.registerAlternateKey( "char", "chr" );
652652

653653
functionFactory.listagg_groupConcat();
654+
655+
if ( getMySQLVersion().isSameOrAfter( 5, 7 ) ) {
656+
functionFactory.jsonValue_mysql();
657+
}
654658
}
655659

656660
@Override

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/OracleLegacyDialect.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,10 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
318318
functionFactory.arrayTrim_oracle();
319319
functionFactory.arrayFill_oracle();
320320
functionFactory.arrayToString_oracle();
321+
322+
if ( getVersion().isSameOrAfter( 12 ) ) {
323+
functionFactory.jsonValue_literal_path();
324+
}
321325
}
322326

323327
@Override

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/PostgreSQLLegacyDialect.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,13 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
629629
functionFactory.arrayFill_postgresql();
630630
functionFactory.arrayToString_postgresql();
631631

632+
if ( getVersion().isSameOrAfter( 17 ) ) {
633+
functionFactory.jsonValue();
634+
}
635+
else {
636+
functionFactory.jsonValue_postgresql();
637+
}
638+
632639
if ( getVersion().isSameOrAfter( 9, 4 ) ) {
633640
functionFactory.makeDateTimeTimestamp();
634641
// Note that PostgreSQL doesn't support the OVER clause for ordered set-aggregate functions

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SQLServerLegacyDialect.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,9 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
400400
functionFactory.windowFunctions();
401401
functionFactory.inverseDistributionOrderedSetAggregates_windowEmulation();
402402
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
403+
if ( getVersion().isSameOrAfter( 13 ) ) {
404+
functionFactory.jsonValue_sqlserver();
405+
}
403406
if ( getVersion().isSameOrAfter( 14 ) ) {
404407
functionFactory.listagg_stringAggWithinGroup( "varchar(max)" );
405408
}

0 commit comments

Comments
 (0)