Skip to content

Commit dc4dac9

Browse files
committed
HHH-16159 Fix some JSON related issues that came up
1 parent bd0285d commit dc4dac9

File tree

4 files changed

+140
-45
lines changed

4 files changed

+140
-45
lines changed

docker_db.sh

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
#! /bin/bash
22

3-
if command -v podman > /dev/null; then
3+
if command -v docker > /dev/null; then
4+
CONTAINER_CLI=$(command -v docker)
5+
HEALTCHECK_PATH="{{.State.Health.Status}}"
6+
PRIVILEGED_CLI=""
7+
else
48
CONTAINER_CLI=$(command -v podman)
59
HEALTCHECK_PATH="{{.State.Healthcheck.Status}}"
610
# Only use sudo for podman
@@ -9,10 +13,6 @@ if command -v podman > /dev/null; then
913
else
1014
PRIVILEGED_CLI=""
1115
fi
12-
else
13-
CONTAINER_CLI=$(command -v docker)
14-
HEALTCHECK_PATH="{{.State.Health.Status}}"
15-
PRIVILEGED_CLI=""
1616
fi
1717

1818
mysql() {
@@ -489,7 +489,7 @@ oracle_setup() {
489489
echo "Waiting for Oracle to start..."
490490
sleep 5;
491491
# On WSL, health-checks intervals don't work for Podman, so run them manually
492-
if command -v podman > /dev/null; then
492+
if ! command -v docker > /dev/null; then
493493
$PRIVILEGED_CLI $CONTAINER_CLI healthcheck run oracle > /dev/null
494494
fi
495495
HEALTHSTATUS="`$PRIVILEGED_CLI $CONTAINER_CLI inspect -f $HEALTCHECK_PATH oracle`"
@@ -569,7 +569,7 @@ oracle_free_setup() {
569569
echo "Waiting for Oracle Free to start..."
570570
sleep 5;
571571
# On WSL, health-checks intervals don't work for Podman, so run them manually
572-
if command -v podman > /dev/null; then
572+
if ! command -v docker > /dev/null; then
573573
$PRIVILEGED_CLI $CONTAINER_CLI healthcheck run oracle > /dev/null
574574
fi
575575
HEALTHSTATUS="`$PRIVILEGED_CLI $CONTAINER_CLI inspect -f $HEALTCHECK_PATH oracle`"
@@ -733,6 +733,21 @@ oracle() {
733733
oracle_23
734734
}
735735

736+
oracle_18() {
737+
$PRIVILEGED_CLI $CONTAINER_CLI rm -f oracle || true
738+
disable_userland_proxy
739+
# We need to use the defaults
740+
# SYSTEM/Oracle18
741+
$PRIVILEGED_CLI $CONTAINER_CLI run --name oracle -d -p 1521:1521 -e ORACLE_PASSWORD=Oracle18 \
742+
--cap-add cap_net_raw \
743+
--health-cmd healthcheck.sh \
744+
--health-interval 5s \
745+
--health-timeout 5s \
746+
--health-retries 10 \
747+
${DB_IMAGE_ORACLE_21:-docker.io/gvenzl/oracle-xe:18.4.0}
748+
oracle_setup
749+
}
750+
736751
oracle_21() {
737752
$PRIVILEGED_CLI $CONTAINER_CLI rm -f oracle || true
738753
disable_userland_proxy

hibernate-core/src/main/java/org/hibernate/dialect/aggregate/OracleAggregateSupport.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import org.hibernate.sql.ast.SqlAstTranslator;
3030
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
3131
import org.hibernate.sql.ast.spi.SqlAppender;
32+
import org.hibernate.sql.ast.tree.expression.Expression;
33+
import org.hibernate.sql.ast.tree.expression.Literal;
3234
import org.hibernate.type.BasicPluralType;
3335
import org.hibernate.type.BasicType;
3436
import org.hibernate.type.SqlTypes;
@@ -589,7 +591,14 @@ public void append(
589591
// We use NO_UNTYPED here so that expressions which require type inference are casted explicitly,
590592
// since we don't know how the custom write expression looks like where this is embedded,
591593
// so we have to be pessimistic and avoid ambiguities
592-
translator.render( expression.getValueExpression( selectableMapping ), SqlAstNodeRenderingMode.NO_UNTYPED );
594+
final Expression valueExpression = expression.getValueExpression( selectableMapping );
595+
if ( valueExpression instanceof Literal literal && literal.getLiteralValue() == null ) {
596+
// Except for the null literal. That is just rendered as-is
597+
sb.append( "null" );
598+
}
599+
else {
600+
translator.render( valueExpression, SqlAstNodeRenderingMode.NO_UNTYPED );
601+
}
593602
sb.append( customWriteExpressionEnd );
594603
}
595604
}

hibernate-core/src/main/java/org/hibernate/dialect/function/json/H2JsonTableFunction.java

Lines changed: 89 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
5454
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
5555
import org.hibernate.sql.ast.tree.predicate.Predicate;
56+
import org.hibernate.sql.ast.tree.predicate.PredicateContainer;
5657
import org.hibernate.sql.ast.tree.select.QuerySpec;
5758
import org.hibernate.type.BasicType;
5859
import org.hibernate.type.SqlTypes;
@@ -153,12 +154,31 @@ public QuerySpec transform(CteContainer cteContainer, QuerySpec querySpec, SqmTo
153154
final TableGroup parentTableGroup = querySpec.getFromClause().queryTableGroups(
154155
tg -> tg.findTableGroupJoin( functionTableGroup ) == null ? null : tg
155156
);
156-
final TableGroupJoin join = parentTableGroup.findTableGroupJoin( functionTableGroup );
157+
final PredicateContainer predicateContainer;
158+
if ( parentTableGroup != null ) {
159+
predicateContainer = parentTableGroup.findTableGroupJoin( functionTableGroup );
160+
}
161+
else {
162+
predicateContainer = querySpec;
163+
}
157164
final BasicType<Integer> integerType = converter.getCreationContext()
158165
.getSessionFactory()
159166
.getNodeBuilder()
160167
.getIntegerType();
161-
final Expression lhs = new ArrayLengthExpression( arguments.jsonDocument(), integerType );
168+
final Expression jsonDocument;
169+
if ( arguments.jsonDocument().getColumnReference() == null ) {
170+
jsonDocument = new ColumnReference(
171+
functionTableGroup.getPrimaryTableReference().getIdentificationVariable() + "_",
172+
"d",
173+
false,
174+
null,
175+
arguments.jsonDocument().getExpressionType().getSingleJdbcMapping()
176+
);
177+
}
178+
else {
179+
jsonDocument = arguments.jsonDocument();
180+
}
181+
final Expression lhs = new ArrayLengthExpression( jsonDocument, integerType );
162182
final Expression rhs = new ColumnReference(
163183
functionTableGroup.getPrimaryTableReference().getIdentificationVariable(),
164184
// The default column name for the system_range function
@@ -167,7 +187,7 @@ public QuerySpec transform(CteContainer cteContainer, QuerySpec querySpec, SqmTo
167187
null,
168188
integerType
169189
);
170-
join.applyPredicate(
190+
predicateContainer.applyPredicate(
171191
new ComparisonPredicate( lhs, ComparisonOperator.GREATER_THAN_OR_EQUAL, rhs ) );
172192
}
173193
final int lastArrayIndex = getLastArrayIndex( arguments.columnsClause(), 0 );
@@ -176,6 +196,19 @@ public QuerySpec transform(CteContainer cteContainer, QuerySpec querySpec, SqmTo
176196
// for every nested path for arrays
177197
final String tableIdentifierVariable = functionTableGroup.getPrimaryTableReference()
178198
.getIdentificationVariable();
199+
final Expression jsonDocument;
200+
if ( arguments.jsonDocument().getColumnReference() == null ) {
201+
jsonDocument = new ColumnReference(
202+
tableIdentifierVariable + "_",
203+
"d",
204+
false,
205+
null,
206+
arguments.jsonDocument().getExpressionType().getSingleJdbcMapping()
207+
);
208+
}
209+
else {
210+
jsonDocument = arguments.jsonDocument();
211+
}
179212
final TableGroup tableGroup = new FunctionTableGroup(
180213
functionTableGroup.getNavigablePath().append( "{synthetic}" ),
181214
null,
@@ -184,6 +217,7 @@ public QuerySpec transform(CteContainer cteContainer, QuerySpec querySpec, SqmTo
184217
new NestedPathFunctionRenderer(
185218
tableIdentifierVariable,
186219
arguments,
220+
jsonDocument,
187221
maximumArraySize,
188222
lastArrayIndex
189223
),
@@ -207,7 +241,7 @@ public QuerySpec transform(CteContainer cteContainer, QuerySpec querySpec, SqmTo
207241
// The join predicate compares the length of the last array expression against system_range() index.
208242
// Since a table function expression can't render its own `on` clause, this split of logic is necessary
209243
final Expression lhs = new ArrayLengthExpression(
210-
determineLastArrayExpression( tableIdentifierVariable, arguments ),
244+
determineLastArrayExpression( tableIdentifierVariable, arguments, jsonDocument ),
211245
integerType
212246
);
213247
final Expression rhs = new ColumnReference(
@@ -226,10 +260,10 @@ public QuerySpec transform(CteContainer cteContainer, QuerySpec querySpec, SqmTo
226260
return querySpec;
227261
}
228262

229-
private static Expression determineLastArrayExpression(String tableIdentifierVariable, JsonTableArguments arguments) {
263+
private static Expression determineLastArrayExpression(String tableIdentifierVariable, JsonTableArguments arguments, Expression jsonDocument) {
230264
final ArrayExpressionEntry arrayExpressionEntry = determineLastArrayExpression(
231265
tableIdentifierVariable,
232-
determineJsonElement( tableIdentifierVariable, arguments ),
266+
determineJsonElement( tableIdentifierVariable, arguments, jsonDocument ),
233267
arguments.columnsClause(),
234268
new ArrayExpressionEntry( 0, null )
235269
);
@@ -253,7 +287,7 @@ private static ArrayExpressionEntry determineLastArrayExpression(String tableIde
253287
final ArrayExpressionEntry nextArrayExpression;
254288
if ( isArray ) {
255289
final int nextArrayIndex = currentArrayEntry.arrayIndex() + 1;
256-
jsonElement = new ArrayAccessExpression( jsonQueryResult, tableIdentifierVariable + "_" + nextArrayIndex + "_.x" );
290+
jsonElement = new ArrayAccessExpression( jsonQueryResult, ordinalityExpression( tableIdentifierVariable, nextArrayIndex ) );
257291
nextArrayExpression = new ArrayExpressionEntry( nextArrayIndex, jsonQueryResult );
258292
}
259293
else {
@@ -271,10 +305,9 @@ private static ArrayExpressionEntry determineLastArrayExpression(String tableIde
271305
return currentArrayEntry;
272306
}
273307

274-
private static Expression determineJsonElement(String tableIdentifierVariable, JsonTableArguments arguments) {
308+
private static Expression determineJsonElement(String tableIdentifierVariable, JsonTableArguments arguments, Expression jsonDocument) {
275309
// Applies the json path and array index access to obtain the "current" processing element
276310

277-
final Expression jsonDocument = arguments.jsonDocument();
278311
final boolean isArray;
279312
final Expression jsonQueryResult;
280313
if ( arguments.jsonPath() != null ) {
@@ -309,19 +342,21 @@ private static Expression determineJsonElement(String tableIdentifierVariable, J
309342
private static class NestedPathFunctionRenderer implements FunctionRenderer {
310343
private final String tableIdentifierVariable;
311344
private final JsonTableArguments arguments;
345+
private final Expression jsonDocument;
312346
private final int maximumArraySize;
313347
private final int lastArrayIndex;
314348

315-
public NestedPathFunctionRenderer(String tableIdentifierVariable, JsonTableArguments arguments, int maximumArraySize, int lastArrayIndex) {
349+
public NestedPathFunctionRenderer(String tableIdentifierVariable, JsonTableArguments arguments, Expression jsonDocument, int maximumArraySize, int lastArrayIndex) {
316350
this.tableIdentifierVariable = tableIdentifierVariable;
317351
this.arguments = arguments;
352+
this.jsonDocument = jsonDocument;
318353
this.maximumArraySize = maximumArraySize;
319354
this.lastArrayIndex = lastArrayIndex;
320355
}
321356

322357
@Override
323358
public void render(SqlAppender sqlAppender, List<? extends SqlAstNode> sqlAstArguments, ReturnableType<?> returnType, SqlAstTranslator<?> walker) {
324-
final Expression jsonElement = determineJsonElement( tableIdentifierVariable, arguments );
359+
final Expression jsonElement = determineJsonElement( tableIdentifierVariable, arguments, jsonDocument );
325360
renderNestedColumnJoins( sqlAppender, tableIdentifierVariable, jsonElement, arguments.columnsClause(), 0, lastArrayIndex, walker );
326361
}
327362

@@ -352,17 +387,15 @@ private int renderNestedColumnJoins(SqlAppender sqlAppender, String tableIdentif
352387
sqlAppender.appendSql( nextArrayIndex );
353388
sqlAppender.appendSql( '_' );
354389

390+
final String ordinalityExpression = ordinalityExpression( tableIdentifierVariable, nextArrayIndex );
355391
// The join condition for the last array will be rendered via TableGroupJoin
356392
if ( nextArrayIndex != lastArrayIndex ) {
357393
sqlAppender.appendSql( " on coalesce(array_length(" );
358394
jsonQueryResult.accept( walker );
359395
sqlAppender.append( "),0)>=" );
360-
sqlAppender.appendSql( tableIdentifierVariable );
361-
sqlAppender.appendSql( '_' );
362-
sqlAppender.appendSql( nextArrayIndex );
363-
sqlAppender.appendSql( "_.x" );
396+
sqlAppender.appendSql( ordinalityExpression );
364397
}
365-
jsonElement = new ArrayAccessExpression( jsonQueryResult, tableIdentifierVariable + "_" + nextArrayIndex + "_.x" );
398+
jsonElement = new ArrayAccessExpression( jsonQueryResult, ordinalityExpression );
366399
}
367400
else {
368401
jsonElement = jsonQueryResult;
@@ -383,6 +416,12 @@ private int renderNestedColumnJoins(SqlAppender sqlAppender, String tableIdentif
383416
}
384417
}
385418

419+
@Override
420+
public boolean rendersIdentifierVariable(List<SqlAstNode> arguments, SessionFactoryImplementor sessionFactory) {
421+
// To make our lives simpler when supporting non-column JSON document arguments
422+
return true;
423+
}
424+
386425
@Override
387426
protected void renderJsonTable(
388427
SqlAppender sqlAppender,
@@ -397,13 +436,27 @@ protected void renderJsonTable(
397436
final Expression jsonPathExpression = arguments.jsonPath();
398437
final boolean isArray = isArrayAccess( jsonPathExpression, walker );
399438

439+
if ( arguments.jsonDocument().getColumnReference() == null ) {
440+
sqlAppender.append( '(' );
441+
}
400442
if ( isArray ) {
401443
sqlAppender.append( "system_range(1," );
402444
sqlAppender.append( Integer.toString( maximumArraySize ) );
403-
sqlAppender.append( ")" );
445+
sqlAppender.append( ") " );
404446
}
405447
else {
406-
sqlAppender.append( "system_range(1,1)" );
448+
sqlAppender.append( "system_range(1,1) " );
449+
}
450+
sqlAppender.append( tableIdentifierVariable );
451+
if ( arguments.jsonDocument().getColumnReference() == null ) {
452+
sqlAppender.append( " join (values (" );
453+
arguments.jsonDocument().accept( walker );
454+
if ( !arguments.isJsonType() ) {
455+
sqlAppender.append( " format json" );
456+
}
457+
sqlAppender.append( ")) " );
458+
sqlAppender.append( tableIdentifierVariable );
459+
sqlAppender.append( "_(d) on 1=1)" );
407460
}
408461
}
409462

@@ -526,6 +579,13 @@ public JdbcMappingContainer getExpressionType() {
526579
}
527580
}
528581

582+
private static String ordinalityExpression(String tableIdentifierVariable, int clauseLevel) {
583+
if ( clauseLevel == 0 ) {
584+
return tableIdentifierVariable + ".x";
585+
}
586+
return tableIdentifierVariable + "_" + clauseLevel + "_.x";
587+
}
588+
529589
/**
530590
* This type resolver essentially implements all the JSON path handling and casting via column read expressions
531591
* instead of rendering to the {@code from} clause like other {@code json_table()} implementations.
@@ -545,10 +605,15 @@ public SelectableMapping[] resolveFunctionReturnType(
545605
boolean withOrdinality,
546606
SqmToSqlAstConverter converter) {
547607
final JsonTableArguments arguments = JsonTableArguments.extract( sqlAstNodes );
548-
final ColumnReference columnReference = arguments.jsonDocument().getColumnReference();
549-
assert columnReference != null;
550-
551-
final String documentPath = columnReference.getExpressionText();
608+
final Expression jsonDocument = arguments.jsonDocument();
609+
final String documentPath;
610+
final ColumnReference columnReference = jsonDocument.getColumnReference();
611+
if ( columnReference != null ) {
612+
documentPath = columnReference.getExpressionText();
613+
}
614+
else {
615+
documentPath = tableIdentifierVariable + "_." + "d";
616+
}
552617

553618
final String parentPath;
554619
final boolean isArray;
@@ -620,7 +685,7 @@ protected int addSelectableMappings(List<SelectableMapping> selectableMappings,
620685
final String readExpression;
621686
if ( isArray ) {
622687
nextClauseLevel = clauseLevel + 1;
623-
readExpression = "array_get(" + parentPath + "," + tableIdentifierVariable + "_" + nextClauseLevel + "_.x)";
688+
readExpression = "array_get(" + parentPath + "," + ordinalityExpression( tableIdentifierVariable, nextClauseLevel ) + ")";
624689
}
625690
else {
626691
nextClauseLevel = clauseLevel;
@@ -633,7 +698,7 @@ protected void addSelectableMappings(List<SelectableMapping> selectableMappings,
633698
addSelectableMapping(
634699
selectableMappings,
635700
definition.name(),
636-
tableIdentifierVariable + "_" + clauseLevel + "_.x",
701+
ordinalityExpression( tableIdentifierVariable, clauseLevel ),
637702
converter.getCreationContext().getTypeConfiguration().getBasicTypeForJavaType( Long.class )
638703
);
639704
}

0 commit comments

Comments
 (0)