Skip to content

Commit e1a8990

Browse files
committed
HHH-18759 Add xmltable() set-returning function
1 parent 854a982 commit e1a8990

File tree

46 files changed

+2616
-19
lines changed

Some content is hidden

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

46 files changed

+2616
-19
lines changed

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

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2257,14 +2257,15 @@ it is necessary to enable the `hibernate.query.hql.xml_functions_enabled` config
22572257
|===
22582258
| Function | Purpose
22592259
2260-
| `xmlelement()` | Constructs an XML element from arguments
2261-
| `xmlcomment()` | Constructs an XML comment from the single argument
2262-
| `xmlforest()` | Constructs an XML forest from the arguments
2263-
| `xmlconcat()` | Concatenates multiple XML fragments to each other
2264-
| `xmlpi()` | Constructs an XML processing instruction
2265-
| `xmlquery()` | Extracts content from XML document using XQuery or XPath
2266-
| `xmlexists()` | Checks if an XQuery or XPath expression exists in an XML document
2267-
| `xmlagg()` | Aggregates XML elements by concatenation
2260+
| <<hql-xmlelement-function,`xmlelement()`>> | Constructs an XML element from arguments
2261+
| <<hql-xmlcomment-function,`xmlcomment()`>> | Constructs an XML comment from the single argument
2262+
| <<hql-xmlforest-function,`xmlforest()`>> | Constructs an XML forest from the arguments
2263+
| <<hql-xmlconcat-function,`xmlconcat()`>> | Concatenates multiple XML fragments to each other
2264+
| <<hql-xmlpi-function,`xmlpi()`>> | Constructs an XML processing instruction
2265+
| <<hql-xmlquery-function,`xmlquery()`>> | Extracts content from XML document using XQuery or XPath
2266+
| <<hql-xmlexists-function,`xmlexists()`>> | Checks if an XQuery or XPath expression exists in an XML document
2267+
| <<hql-xmlagg-function,`xmlagg()`>> | Aggregates XML elements by concatenation
2268+
| <<hql-xmltable-function,`xmltable()`>> | Turns an XML document into rows
22682269
|===
22692270
22702271
@@ -2461,6 +2462,39 @@ include::{xml-example-dir-hql}/XmlAggTest.java[tags=hql-xmlagg-example]
24612462
24622463
WARNING: SAP HANA, MySQL, MariaDB and HSQLDB do not support this function.
24632464
2465+
[[hql-xmltable-function]]
2466+
===== `xmltable()`
2467+
2468+
A <<hql-from-set-returning-functions,set-returning function>>, which turns an XML document argument into rows.
2469+
Returns no rows if the document is `null` or the XPath expression resolves to no nodes.
2470+
2471+
[[hql-xmltable-bnf]]
2472+
[source, antlrv4, indent=0]
2473+
----
2474+
include::{extrasdir}/xmltable_bnf.txt[]
2475+
----
2476+
2477+
The first argument is the XPath expression. The second argument represents the XML document expression.
2478+
2479+
Columns that ought to be accessible via the `from` node alias are defined in the `columns` clause,
2480+
which can be of varying forms:
2481+
2482+
* Value attributes - denoted by a `castTarget` after the name, will cast the content of the XML node matching the XPath expression of the column
2483+
* Query attributes - denoted by the `xml` type after the name, returns the XML node matching the XPath expression of the column
2484+
* Ordinal attributes - denoted by the `for ordinality` syntax after the name, gives access to the 1-based index of the currently processed XML node
2485+
2486+
[[hql-xmltable-simple-example]]
2487+
====
2488+
[source, java, indent=0]
2489+
----
2490+
include::{xml-example-dir-hql}/XmlTableTest.java[tags=hql-xml-table-example]
2491+
----
2492+
====
2493+
2494+
The `lateral` keyword is mandatory if one of the arguments refer to a from node item of the same query level.
2495+
2496+
WARNING: H2, MySQL, MariaDB and HSQLDB do not support this function.
2497+
24642498
[[hql-user-defined-functions]]
24652499
==== Native and user-defined functions
24662500
@@ -3001,7 +3035,8 @@ The following set-returning functions are available on many platforms:
30013035
30023036
| <<hql-array-unnest,`unnest()`>> | Turns an array into rows
30033037
| <<hql-from-set-returning-functions-generate-series,`generate_series()`>> | Creates a series of values as rows
3004-
| <<hql-json-table,`json_table()`>> | Turns a JSON document into rows
3038+
| <<hql-json-table-function,`json_table()`>> | Turns a JSON document into rows
3039+
| <<hql-xmltable-function,`xmltable()`>> | Turns an XML document into rows
30053040
|===
30063041
30073042
To use set returning functions defined in the database, it is required to register them in a `FunctionContributor`:
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"xmltable(" expression "passing" expression columnsClause ")"
2+
3+
columnsClause
4+
: "columns" column ("," column)*
5+
6+
column
7+
: attributeName "xml" ("path" STRING_LITERAL)? defaultClause?
8+
| attributeName "for ordinality"
9+
| attributeName castTarget ("path" STRING_LITERAL)? defaultClause?
10+
11+
defaultClause
12+
: "default" expression;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
457457
functionFactory.xmlexists_db2_legacy();
458458
}
459459
functionFactory.xmlagg();
460+
functionFactory.xmltable_db2();
460461

461462
functionFactory.unnest_emulated();
462463
if ( supportsRecursiveCTE() ) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -508,7 +508,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
508508
functionFactory.jsonObjectAgg_hana();
509509
}
510510

511-
// functionFactory.xmltable();
511+
functionFactory.xmltable_hana();
512512
}
513513

514514
// functionFactory.xmlextract();

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
334334
functionFactory.xmlquery_oracle();
335335
functionFactory.xmlexists();
336336
functionFactory.xmlagg();
337+
functionFactory.xmltable_oracle();
337338

338339
functionFactory.unnest_oracle();
339340
functionFactory.generateSeries_recursive( getMaximumSeriesSize(), true, false );

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
678678
functionFactory.xmlquery_postgresql();
679679
functionFactory.xmlexists();
680680
functionFactory.xmlagg();
681+
functionFactory.xmltable( true );
681682

682683
if ( getVersion().isSameOrAfter( 9, 4 ) ) {
683684
functionFactory.makeDateTimeTimestamp();

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
429429
functionFactory.xmlquery_sqlserver();
430430
functionFactory.xmlexists_sqlserver();
431431
functionFactory.xmlagg_sqlserver();
432+
functionFactory.xmltable_sqlserver();
432433

433434
functionFactory.unnest_sqlserver();
434435

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
167167

168168
functionFactory.unnest_sybasease();
169169
functionFactory.generateSeries_sybasease( getMaximumSeriesSize() );
170+
functionFactory.xmltable_sybasease();
170171
}
171172

172173
/**

hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlLexer.g4

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,13 +336,15 @@ WITH : [wW] [iI] [tT] [hH];
336336
WITHIN : [wW] [iI] [tT] [hH] [iI] [nN];
337337
WITHOUT : [wW] [iI] [tT] [hH] [oO] [uU] [tT];
338338
WRAPPER : [wW] [rR] [aA] [pP] [pP] [eE] [rR];
339+
XML : [xX] [mM] [lL];
339340
XMLAGG : [xX] [mM] [lL] [aA] [gG] [gG];
340341
XMLATTRIBUTES : [xX] [mM] [lL] [aA] [tT] [tT] [rR] [iI] [bB] [uU] [tT] [eE] [sS];
341342
XMLELEMENT : [xX] [mM] [lL] [eE] [lL] [eE] [mM] [eE] [nN] [tT];
342343
XMLEXISTS : [xX] [mM] [lL] [eE] [xX] [iI] [sS] [tT] [sS];
343344
XMLFOREST : [xX] [mM] [lL] [fF] [oO] [rR] [eE] [sS] [tT];
344345
XMLPI : [xX] [mM] [lL] [pP] [iI];
345346
XMLQUERY : [xX] [mM] [lL] [qQ] [uU] [eE] [rR] [yY];
347+
XMLTABLE : [xX] [mM] [lL] [tT] [aA] [bB] [lL] [eE];
346348
YEAR : [yY] [eE] [aA] [rR];
347349
ZONED : [zZ] [oO] [nN] [eE] [dD];
348350

hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,7 @@ function
11191119
setReturningFunction
11201120
: simpleSetReturningFunction
11211121
| jsonTableFunction
1122+
| xmltableFunction
11221123
;
11231124

11241125
/**
@@ -1813,6 +1814,24 @@ xmlaggFunction
18131814
: XMLAGG LEFT_PAREN expression orderByClause? RIGHT_PAREN filterClause? overClause?
18141815
;
18151816

1817+
xmltableFunction
1818+
: XMLTABLE LEFT_PAREN expression PASSING expression xmltableColumnsClause RIGHT_PAREN
1819+
;
1820+
1821+
xmltableColumnsClause
1822+
: COLUMNS xmltableColumn (COMMA xmltableColumn)*
1823+
;
1824+
1825+
xmltableColumn
1826+
: identifier XML (PATH STRING_LITERAL)? xmltableDefaultClause? # XmlTableQueryColumn
1827+
| identifier FOR ORDINALITY # XmlTableOrdinalityColumn
1828+
| identifier castTarget (PATH STRING_LITERAL)? xmltableDefaultClause? # XmlTableValueColumn
1829+
;
1830+
1831+
xmltableDefaultClause
1832+
: DEFAULT expression
1833+
;
1834+
18161835
/**
18171836
* Support for "soft" keywords which may be used as identifiers
18181837
*
@@ -2025,13 +2044,15 @@ xmlaggFunction
20252044
| WITHIN
20262045
| WITHOUT
20272046
| WRAPPER
2047+
| XML
20282048
| XMLAGG
20292049
| XMLATTRIBUTES
20302050
| XMLELEMENT
20312051
| XMLEXISTS
20322052
| XMLFOREST
20332053
| XMLPI
20342054
| XMLQUERY
2055+
| XMLTABLE
20352056
| YEAR
20362057
| ZONED) {
20372058
logUseOfReservedWordAsIdentifier( getCurrentToken() );

0 commit comments

Comments
 (0)