HHH-16159 Fix some JSON related issues that came up

This commit is contained in:
Christian Beikov 2024-11-07 16:52:25 +01:00
parent 4a07b5ed1d
commit 839793e543
15 changed files with 168 additions and 66 deletions

View File

@ -1,6 +1,10 @@
#! /bin/bash #! /bin/bash
if command -v podman > /dev/null; then if command -v docker > /dev/null; then
CONTAINER_CLI=$(command -v docker)
HEALTCHECK_PATH="{{.State.Health.Status}}"
PRIVILEGED_CLI=""
else
CONTAINER_CLI=$(command -v podman) CONTAINER_CLI=$(command -v podman)
HEALTCHECK_PATH="{{.State.Healthcheck.Status}}" HEALTCHECK_PATH="{{.State.Healthcheck.Status}}"
# Only use sudo for podman # Only use sudo for podman
@ -9,10 +13,6 @@ if command -v podman > /dev/null; then
else else
PRIVILEGED_CLI="" PRIVILEGED_CLI=""
fi fi
else
CONTAINER_CLI=$(command -v docker)
HEALTCHECK_PATH="{{.State.Health.Status}}"
PRIVILEGED_CLI=""
fi fi
mysql() { mysql() {
@ -489,7 +489,7 @@ oracle_setup() {
echo "Waiting for Oracle to start..." echo "Waiting for Oracle to start..."
sleep 5; sleep 5;
# On WSL, health-checks intervals don't work for Podman, so run them manually # On WSL, health-checks intervals don't work for Podman, so run them manually
if command -v podman > /dev/null; then if ! command -v docker > /dev/null; then
$PRIVILEGED_CLI $CONTAINER_CLI healthcheck run oracle > /dev/null $PRIVILEGED_CLI $CONTAINER_CLI healthcheck run oracle > /dev/null
fi fi
HEALTHSTATUS="`$PRIVILEGED_CLI $CONTAINER_CLI inspect -f $HEALTCHECK_PATH oracle`" HEALTHSTATUS="`$PRIVILEGED_CLI $CONTAINER_CLI inspect -f $HEALTCHECK_PATH oracle`"
@ -569,7 +569,7 @@ oracle_free_setup() {
echo "Waiting for Oracle Free to start..." echo "Waiting for Oracle Free to start..."
sleep 5; sleep 5;
# On WSL, health-checks intervals don't work for Podman, so run them manually # On WSL, health-checks intervals don't work for Podman, so run them manually
if command -v podman > /dev/null; then if ! command -v docker > /dev/null; then
$PRIVILEGED_CLI $CONTAINER_CLI healthcheck run oracle > /dev/null $PRIVILEGED_CLI $CONTAINER_CLI healthcheck run oracle > /dev/null
fi fi
HEALTHSTATUS="`$PRIVILEGED_CLI $CONTAINER_CLI inspect -f $HEALTCHECK_PATH oracle`" HEALTHSTATUS="`$PRIVILEGED_CLI $CONTAINER_CLI inspect -f $HEALTCHECK_PATH oracle`"
@ -658,9 +658,13 @@ disable_userland_proxy() {
echo "Stopping docker..." echo "Stopping docker..."
sudo service docker stop sudo service docker stop
echo "Updating /etc/docker/daemon.json..." echo "Updating /etc/docker/daemon.json..."
sudo bash -c 'echo "${docker_daemon_json/\}/,}\"userland-proxy\": false}" > /etc/docker/daemon.json' sudo bash -c "export docker_daemon_json='$docker_daemon_json'; echo \"\${docker_daemon_json/\}/,}\\\"userland-proxy\\\": false}\" > /etc/docker/daemon.json"
echo "New docker daemon config:"
cat /etc/docker/daemon.json
echo "Starting docker..." echo "Starting docker..."
sudo service docker start sudo service docker start
echo "Service status:"
sudo journalctl -xeu docker.service
echo "Docker successfully started with userland proxies disabled" echo "Docker successfully started with userland proxies disabled"
fi fi
fi fi
@ -733,6 +737,21 @@ oracle() {
oracle_23 oracle_23
} }
oracle_18() {
$PRIVILEGED_CLI $CONTAINER_CLI rm -f oracle || true
disable_userland_proxy
# We need to use the defaults
# SYSTEM/Oracle18
$PRIVILEGED_CLI $CONTAINER_CLI run --name oracle -d -p 1521:1521 -e ORACLE_PASSWORD=Oracle18 \
--cap-add cap_net_raw \
--health-cmd healthcheck.sh \
--health-interval 5s \
--health-timeout 5s \
--health-retries 10 \
${DB_IMAGE_ORACLE_21:-docker.io/gvenzl/oracle-xe:18.4.0}
oracle_setup
}
oracle_21() { oracle_21() {
$PRIVILEGED_CLI $CONTAINER_CLI rm -f oracle || true $PRIVILEGED_CLI $CONTAINER_CLI rm -f oracle || true
disable_userland_proxy disable_userland_proxy
@ -765,7 +784,7 @@ oracle_23() {
hana() { hana() {
temp_dir=$(mktemp -d) temp_dir=$(mktemp -d)
echo '{"master_password" : "H1bernate_test"}' >$temp_dir/password.json echo '{"master_password" : "H1bernate_test"}' >$temp_dir/password.json
chmod 777 -R $temp_dir chmod -R 777 $temp_dir
$PRIVILEGED_CLI $CONTAINER_CLI rm -f hana || true $PRIVILEGED_CLI $CONTAINER_CLI rm -f hana || true
$PRIVILEGED_CLI $CONTAINER_CLI run -d --name hana -p 39013:39013 -p 39017:39017 -p 39041-39045:39041-39045 -p 1128-1129:1128-1129 -p 59013-59014:59013-59014 \ $PRIVILEGED_CLI $CONTAINER_CLI run -d --name hana -p 39013:39013 -p 39017:39017 -p 39041-39045:39041-39045 -p 1128-1129:1128-1129 -p 59013-59014:59013-59014 \
--memory=8g \ --memory=8g \
@ -775,7 +794,7 @@ hana() {
--sysctl kernel.shmmni=4096 \ --sysctl kernel.shmmni=4096 \
--sysctl kernel.shmall=8388608 \ --sysctl kernel.shmall=8388608 \
-v $temp_dir:/config:Z \ -v $temp_dir:/config:Z \
${DB_IMAGE_HANA:-docker.io/saplabs/hanaexpress:2.00.072.00.20231123.1} \ ${DB_IMAGE_HANA:-docker.io/saplabs/hanaexpress:2.00.076.00.20240701.1} \
--passwords-url file:///config/password.json \ --passwords-url file:///config/password.json \
--agree-to-sap-license --agree-to-sap-license
# Give the container some time to start # Give the container some time to start

View File

@ -29,6 +29,8 @@ import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator; import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.type.BasicPluralType; import org.hibernate.type.BasicPluralType;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.SqlTypes; import org.hibernate.type.SqlTypes;
@ -589,7 +591,14 @@ public class OracleAggregateSupport extends AggregateSupportImpl {
// We use NO_UNTYPED here so that expressions which require type inference are casted explicitly, // We use NO_UNTYPED here so that expressions which require type inference are casted explicitly,
// since we don't know how the custom write expression looks like where this is embedded, // since we don't know how the custom write expression looks like where this is embedded,
// so we have to be pessimistic and avoid ambiguities // so we have to be pessimistic and avoid ambiguities
translator.render( expression.getValueExpression( selectableMapping ), SqlAstNodeRenderingMode.NO_UNTYPED ); final Expression valueExpression = expression.getValueExpression( selectableMapping );
if ( valueExpression instanceof Literal literal && literal.getLiteralValue() == null ) {
// Except for the null literal. That is just rendered as-is
sb.append( "null" );
}
else {
translator.render( valueExpression, SqlAstNodeRenderingMode.NO_UNTYPED );
}
sb.append( customWriteExpressionEnd ); sb.append( customWriteExpressionEnd );
} }
} }

View File

@ -19,11 +19,11 @@ import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.SqmTypedNode; import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.sql.ast.tree.from.FunctionTableGroup; import org.hibernate.sql.ast.tree.from.FunctionTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
@ -232,6 +232,6 @@ public class H2GenerateSeriesFunction extends NumberSeriesGenerateSeriesFunction
} }
private static boolean needsEmulation(Expression expression) { private static boolean needsEmulation(Expression expression) {
return !( expression instanceof Literal || expression instanceof JdbcParameter); return !( expression instanceof Literal || AbstractSqlAstTranslator.isParameter( expression ) );
} }
} }

View File

@ -11,9 +11,9 @@ import org.hibernate.QueryException;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.query.ReturnableType; import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.JsonExistsErrorBehavior; import org.hibernate.sql.ast.tree.expression.JsonExistsErrorBehavior;
import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause; import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
@ -61,7 +61,7 @@ public class CockroachDBJsonExistsFunction extends JsonExistsFunction {
boolean isJsonType, boolean isJsonType,
@Nullable JsonPathPassingClause jsonPathPassingClause, @Nullable JsonPathPassingClause jsonPathPassingClause,
SqlAstTranslator<?> walker) { SqlAstTranslator<?> walker) {
final boolean needsCast = !isJsonType && jsonDocument instanceof JdbcParameter; final boolean needsCast = !isJsonType && AbstractSqlAstTranslator.isParameter( jsonDocument );
if ( needsCast ) { if ( needsCast ) {
sqlAppender.appendSql( "cast(" ); sqlAppender.appendSql( "cast(" );
} }

View File

@ -11,9 +11,9 @@ import org.hibernate.QueryException;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.query.ReturnableType; import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause; import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause;
import org.hibernate.sql.ast.tree.expression.JsonQueryEmptyBehavior; import org.hibernate.sql.ast.tree.expression.JsonQueryEmptyBehavior;
import org.hibernate.sql.ast.tree.expression.JsonQueryErrorBehavior; import org.hibernate.sql.ast.tree.expression.JsonQueryErrorBehavior;
@ -76,7 +76,7 @@ public class CockroachDBJsonQueryFunction extends JsonQueryFunction {
boolean isJsonType, boolean isJsonType,
@Nullable JsonPathPassingClause jsonPathPassingClause, @Nullable JsonPathPassingClause jsonPathPassingClause,
SqlAstTranslator<?> walker) { SqlAstTranslator<?> walker) {
final boolean needsCast = !isJsonType && jsonDocumentExpression instanceof JdbcParameter; final boolean needsCast = !isJsonType && AbstractSqlAstTranslator.isParameter( jsonDocumentExpression );
if ( needsCast ) { if ( needsCast ) {
sqlAppender.appendSql( "cast(" ); sqlAppender.appendSql( "cast(" );
} }

View File

@ -10,10 +10,10 @@ import org.hibernate.QueryException;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.ReturnableType; import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
/** /**
@ -34,7 +34,7 @@ public class CockroachDBJsonRemoveFunction extends AbstractJsonRemoveFunction {
final Expression json = (Expression) arguments.get( 0 ); final Expression json = (Expression) arguments.get( 0 );
final Expression jsonPath = (Expression) arguments.get( 1 ); final Expression jsonPath = (Expression) arguments.get( 1 );
sqlAppender.appendSql( "json_remove_path(" ); sqlAppender.appendSql( "json_remove_path(" );
final boolean needsCast = !isJsonType( json ) && json instanceof JdbcParameter; final boolean needsCast = !isJsonType( json ) && AbstractSqlAstTranslator.isParameter( json );
if ( needsCast ) { if ( needsCast ) {
sqlAppender.appendSql( "cast(" ); sqlAppender.appendSql( "cast(" );
} }

View File

@ -10,10 +10,10 @@ import org.hibernate.QueryException;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.query.ReturnableType; import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.expression.CastTarget; import org.hibernate.sql.ast.tree.expression.CastTarget;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause; import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause;
import org.hibernate.sql.ast.tree.expression.JsonValueEmptyBehavior; import org.hibernate.sql.ast.tree.expression.JsonValueEmptyBehavior;
import org.hibernate.sql.ast.tree.expression.JsonValueErrorBehavior; import org.hibernate.sql.ast.tree.expression.JsonValueErrorBehavior;
@ -63,7 +63,7 @@ public class CockroachDBJsonValueFunction extends JsonValueFunction {
if ( castTarget != null ) { if ( castTarget != null ) {
sqlAppender.appendSql( "cast(" ); sqlAppender.appendSql( "cast(" );
} }
final boolean needsCast = !isJsonType && jsonDocument instanceof JdbcParameter; final boolean needsCast = !isJsonType && AbstractSqlAstTranslator.isParameter( jsonDocument );
if ( needsCast ) { if ( needsCast ) {
sqlAppender.appendSql( "cast(" ); sqlAppender.appendSql( "cast(" );
} }

View File

@ -53,6 +53,7 @@ import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.ast.tree.predicate.PredicateContainer;
import org.hibernate.sql.ast.tree.select.QuerySpec; import org.hibernate.sql.ast.tree.select.QuerySpec;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.SqlTypes; import org.hibernate.type.SqlTypes;
@ -153,12 +154,31 @@ public class H2JsonTableFunction extends JsonTableFunction {
final TableGroup parentTableGroup = querySpec.getFromClause().queryTableGroups( final TableGroup parentTableGroup = querySpec.getFromClause().queryTableGroups(
tg -> tg.findTableGroupJoin( functionTableGroup ) == null ? null : tg tg -> tg.findTableGroupJoin( functionTableGroup ) == null ? null : tg
); );
final TableGroupJoin join = parentTableGroup.findTableGroupJoin( functionTableGroup ); final PredicateContainer predicateContainer;
if ( parentTableGroup != null ) {
predicateContainer = parentTableGroup.findTableGroupJoin( functionTableGroup );
}
else {
predicateContainer = querySpec;
}
final BasicType<Integer> integerType = converter.getCreationContext() final BasicType<Integer> integerType = converter.getCreationContext()
.getSessionFactory() .getSessionFactory()
.getNodeBuilder() .getNodeBuilder()
.getIntegerType(); .getIntegerType();
final Expression lhs = new ArrayLengthExpression( arguments.jsonDocument(), integerType ); final Expression jsonDocument;
if ( arguments.jsonDocument().getColumnReference() == null ) {
jsonDocument = new ColumnReference(
functionTableGroup.getPrimaryTableReference().getIdentificationVariable() + "_",
"d",
false,
null,
arguments.jsonDocument().getExpressionType().getSingleJdbcMapping()
);
}
else {
jsonDocument = arguments.jsonDocument();
}
final Expression lhs = new ArrayLengthExpression( jsonDocument, integerType );
final Expression rhs = new ColumnReference( final Expression rhs = new ColumnReference(
functionTableGroup.getPrimaryTableReference().getIdentificationVariable(), functionTableGroup.getPrimaryTableReference().getIdentificationVariable(),
// The default column name for the system_range function // The default column name for the system_range function
@ -167,7 +187,7 @@ public class H2JsonTableFunction extends JsonTableFunction {
null, null,
integerType integerType
); );
join.applyPredicate( predicateContainer.applyPredicate(
new ComparisonPredicate( lhs, ComparisonOperator.GREATER_THAN_OR_EQUAL, rhs ) ); new ComparisonPredicate( lhs, ComparisonOperator.GREATER_THAN_OR_EQUAL, rhs ) );
} }
final int lastArrayIndex = getLastArrayIndex( arguments.columnsClause(), 0 ); final int lastArrayIndex = getLastArrayIndex( arguments.columnsClause(), 0 );
@ -176,6 +196,19 @@ public class H2JsonTableFunction extends JsonTableFunction {
// for every nested path for arrays // for every nested path for arrays
final String tableIdentifierVariable = functionTableGroup.getPrimaryTableReference() final String tableIdentifierVariable = functionTableGroup.getPrimaryTableReference()
.getIdentificationVariable(); .getIdentificationVariable();
final Expression jsonDocument;
if ( arguments.jsonDocument().getColumnReference() == null ) {
jsonDocument = new ColumnReference(
tableIdentifierVariable + "_",
"d",
false,
null,
arguments.jsonDocument().getExpressionType().getSingleJdbcMapping()
);
}
else {
jsonDocument = arguments.jsonDocument();
}
final TableGroup tableGroup = new FunctionTableGroup( final TableGroup tableGroup = new FunctionTableGroup(
functionTableGroup.getNavigablePath().append( "{synthetic}" ), functionTableGroup.getNavigablePath().append( "{synthetic}" ),
null, null,
@ -184,6 +217,7 @@ public class H2JsonTableFunction extends JsonTableFunction {
new NestedPathFunctionRenderer( new NestedPathFunctionRenderer(
tableIdentifierVariable, tableIdentifierVariable,
arguments, arguments,
jsonDocument,
maximumArraySize, maximumArraySize,
lastArrayIndex lastArrayIndex
), ),
@ -207,7 +241,7 @@ public class H2JsonTableFunction extends JsonTableFunction {
// The join predicate compares the length of the last array expression against system_range() index. // The join predicate compares the length of the last array expression against system_range() index.
// Since a table function expression can't render its own `on` clause, this split of logic is necessary // Since a table function expression can't render its own `on` clause, this split of logic is necessary
final Expression lhs = new ArrayLengthExpression( final Expression lhs = new ArrayLengthExpression(
determineLastArrayExpression( tableIdentifierVariable, arguments ), determineLastArrayExpression( tableIdentifierVariable, arguments, jsonDocument ),
integerType integerType
); );
final Expression rhs = new ColumnReference( final Expression rhs = new ColumnReference(
@ -226,10 +260,10 @@ public class H2JsonTableFunction extends JsonTableFunction {
return querySpec; return querySpec;
} }
private static Expression determineLastArrayExpression(String tableIdentifierVariable, JsonTableArguments arguments) { private static Expression determineLastArrayExpression(String tableIdentifierVariable, JsonTableArguments arguments, Expression jsonDocument) {
final ArrayExpressionEntry arrayExpressionEntry = determineLastArrayExpression( final ArrayExpressionEntry arrayExpressionEntry = determineLastArrayExpression(
tableIdentifierVariable, tableIdentifierVariable,
determineJsonElement( tableIdentifierVariable, arguments ), determineJsonElement( tableIdentifierVariable, arguments, jsonDocument ),
arguments.columnsClause(), arguments.columnsClause(),
new ArrayExpressionEntry( 0, null ) new ArrayExpressionEntry( 0, null )
); );
@ -253,7 +287,7 @@ public class H2JsonTableFunction extends JsonTableFunction {
final ArrayExpressionEntry nextArrayExpression; final ArrayExpressionEntry nextArrayExpression;
if ( isArray ) { if ( isArray ) {
final int nextArrayIndex = currentArrayEntry.arrayIndex() + 1; final int nextArrayIndex = currentArrayEntry.arrayIndex() + 1;
jsonElement = new ArrayAccessExpression( jsonQueryResult, tableIdentifierVariable + "_" + nextArrayIndex + "_.x" ); jsonElement = new ArrayAccessExpression( jsonQueryResult, ordinalityExpression( tableIdentifierVariable, nextArrayIndex ) );
nextArrayExpression = new ArrayExpressionEntry( nextArrayIndex, jsonQueryResult ); nextArrayExpression = new ArrayExpressionEntry( nextArrayIndex, jsonQueryResult );
} }
else { else {
@ -271,10 +305,9 @@ public class H2JsonTableFunction extends JsonTableFunction {
return currentArrayEntry; return currentArrayEntry;
} }
private static Expression determineJsonElement(String tableIdentifierVariable, JsonTableArguments arguments) { private static Expression determineJsonElement(String tableIdentifierVariable, JsonTableArguments arguments, Expression jsonDocument) {
// Applies the json path and array index access to obtain the "current" processing element // Applies the json path and array index access to obtain the "current" processing element
final Expression jsonDocument = arguments.jsonDocument();
final boolean isArray; final boolean isArray;
final Expression jsonQueryResult; final Expression jsonQueryResult;
if ( arguments.jsonPath() != null ) { if ( arguments.jsonPath() != null ) {
@ -309,19 +342,21 @@ public class H2JsonTableFunction extends JsonTableFunction {
private static class NestedPathFunctionRenderer implements FunctionRenderer { private static class NestedPathFunctionRenderer implements FunctionRenderer {
private final String tableIdentifierVariable; private final String tableIdentifierVariable;
private final JsonTableArguments arguments; private final JsonTableArguments arguments;
private final Expression jsonDocument;
private final int maximumArraySize; private final int maximumArraySize;
private final int lastArrayIndex; private final int lastArrayIndex;
public NestedPathFunctionRenderer(String tableIdentifierVariable, JsonTableArguments arguments, int maximumArraySize, int lastArrayIndex) { public NestedPathFunctionRenderer(String tableIdentifierVariable, JsonTableArguments arguments, Expression jsonDocument, int maximumArraySize, int lastArrayIndex) {
this.tableIdentifierVariable = tableIdentifierVariable; this.tableIdentifierVariable = tableIdentifierVariable;
this.arguments = arguments; this.arguments = arguments;
this.jsonDocument = jsonDocument;
this.maximumArraySize = maximumArraySize; this.maximumArraySize = maximumArraySize;
this.lastArrayIndex = lastArrayIndex; this.lastArrayIndex = lastArrayIndex;
} }
@Override @Override
public void render(SqlAppender sqlAppender, List<? extends SqlAstNode> sqlAstArguments, ReturnableType<?> returnType, SqlAstTranslator<?> walker) { public void render(SqlAppender sqlAppender, List<? extends SqlAstNode> sqlAstArguments, ReturnableType<?> returnType, SqlAstTranslator<?> walker) {
final Expression jsonElement = determineJsonElement( tableIdentifierVariable, arguments ); final Expression jsonElement = determineJsonElement( tableIdentifierVariable, arguments, jsonDocument );
renderNestedColumnJoins( sqlAppender, tableIdentifierVariable, jsonElement, arguments.columnsClause(), 0, lastArrayIndex, walker ); renderNestedColumnJoins( sqlAppender, tableIdentifierVariable, jsonElement, arguments.columnsClause(), 0, lastArrayIndex, walker );
} }
@ -352,17 +387,15 @@ public class H2JsonTableFunction extends JsonTableFunction {
sqlAppender.appendSql( nextArrayIndex ); sqlAppender.appendSql( nextArrayIndex );
sqlAppender.appendSql( '_' ); sqlAppender.appendSql( '_' );
final String ordinalityExpression = ordinalityExpression( tableIdentifierVariable, nextArrayIndex );
// The join condition for the last array will be rendered via TableGroupJoin // The join condition for the last array will be rendered via TableGroupJoin
if ( nextArrayIndex != lastArrayIndex ) { if ( nextArrayIndex != lastArrayIndex ) {
sqlAppender.appendSql( " on coalesce(array_length(" ); sqlAppender.appendSql( " on coalesce(array_length(" );
jsonQueryResult.accept( walker ); jsonQueryResult.accept( walker );
sqlAppender.append( "),0)>=" ); sqlAppender.append( "),0)>=" );
sqlAppender.appendSql( tableIdentifierVariable ); sqlAppender.appendSql( ordinalityExpression );
sqlAppender.appendSql( '_' );
sqlAppender.appendSql( nextArrayIndex );
sqlAppender.appendSql( "_.x" );
} }
jsonElement = new ArrayAccessExpression( jsonQueryResult, tableIdentifierVariable + "_" + nextArrayIndex + "_.x" ); jsonElement = new ArrayAccessExpression( jsonQueryResult, ordinalityExpression );
} }
else { else {
jsonElement = jsonQueryResult; jsonElement = jsonQueryResult;
@ -383,6 +416,12 @@ public class H2JsonTableFunction extends JsonTableFunction {
} }
} }
@Override
public boolean rendersIdentifierVariable(List<SqlAstNode> arguments, SessionFactoryImplementor sessionFactory) {
// To make our lives simpler when supporting non-column JSON document arguments
return true;
}
@Override @Override
protected void renderJsonTable( protected void renderJsonTable(
SqlAppender sqlAppender, SqlAppender sqlAppender,
@ -397,6 +436,9 @@ public class H2JsonTableFunction extends JsonTableFunction {
final Expression jsonPathExpression = arguments.jsonPath(); final Expression jsonPathExpression = arguments.jsonPath();
final boolean isArray = isArrayAccess( jsonPathExpression, walker ); final boolean isArray = isArrayAccess( jsonPathExpression, walker );
if ( arguments.jsonDocument().getColumnReference() == null ) {
sqlAppender.append( '(' );
}
if ( isArray ) { if ( isArray ) {
sqlAppender.append( "system_range(1," ); sqlAppender.append( "system_range(1," );
sqlAppender.append( Integer.toString( maximumArraySize ) ); sqlAppender.append( Integer.toString( maximumArraySize ) );
@ -405,6 +447,17 @@ public class H2JsonTableFunction extends JsonTableFunction {
else { else {
sqlAppender.append( "system_range(1,1) " ); sqlAppender.append( "system_range(1,1) " );
} }
sqlAppender.append( tableIdentifierVariable );
if ( arguments.jsonDocument().getColumnReference() == null ) {
sqlAppender.append( " join (values (" );
arguments.jsonDocument().accept( walker );
if ( !arguments.isJsonType() ) {
sqlAppender.append( " format json" );
}
sqlAppender.append( ")) " );
sqlAppender.append( tableIdentifierVariable );
sqlAppender.append( "_(d) on 1=1)" );
}
} }
private static boolean isArrayAccess(@Nullable Expression jsonPath, SqlAstTranslator<?> walker) { private static boolean isArrayAccess(@Nullable Expression jsonPath, SqlAstTranslator<?> walker) {
@ -526,6 +579,13 @@ public class H2JsonTableFunction extends JsonTableFunction {
} }
} }
private static String ordinalityExpression(String tableIdentifierVariable, int clauseLevel) {
if ( clauseLevel == 0 ) {
return tableIdentifierVariable + ".x";
}
return tableIdentifierVariable + "_" + clauseLevel + "_.x";
}
/** /**
* This type resolver essentially implements all the JSON path handling and casting via column read expressions * This type resolver essentially implements all the JSON path handling and casting via column read expressions
* instead of rendering to the {@code from} clause like other {@code json_table()} implementations. * instead of rendering to the {@code from} clause like other {@code json_table()} implementations.
@ -545,10 +605,15 @@ public class H2JsonTableFunction extends JsonTableFunction {
boolean withOrdinality, boolean withOrdinality,
SqmToSqlAstConverter converter) { SqmToSqlAstConverter converter) {
final JsonTableArguments arguments = JsonTableArguments.extract( sqlAstNodes ); final JsonTableArguments arguments = JsonTableArguments.extract( sqlAstNodes );
final ColumnReference columnReference = arguments.jsonDocument().getColumnReference(); final Expression jsonDocument = arguments.jsonDocument();
assert columnReference != null; final String documentPath;
final ColumnReference columnReference = jsonDocument.getColumnReference();
final String documentPath = columnReference.getExpressionText(); if ( columnReference != null ) {
documentPath = columnReference.getExpressionText();
}
else {
documentPath = tableIdentifierVariable + "_." + "d";
}
final String parentPath; final String parentPath;
final boolean isArray; final boolean isArray;
@ -620,7 +685,7 @@ public class H2JsonTableFunction extends JsonTableFunction {
final String readExpression; final String readExpression;
if ( isArray ) { if ( isArray ) {
nextClauseLevel = clauseLevel + 1; nextClauseLevel = clauseLevel + 1;
readExpression = "array_get(" + parentPath + "," + tableIdentifierVariable + "_" + nextClauseLevel + "_.x)"; readExpression = "array_get(" + parentPath + "," + ordinalityExpression( tableIdentifierVariable, nextClauseLevel ) + ")";
} }
else { else {
nextClauseLevel = clauseLevel; nextClauseLevel = clauseLevel;
@ -633,7 +698,7 @@ public class H2JsonTableFunction extends JsonTableFunction {
addSelectableMapping( addSelectableMapping(
selectableMappings, selectableMappings,
definition.name(), definition.name(),
tableIdentifierVariable + "_" + clauseLevel + "_.x", ordinalityExpression( tableIdentifierVariable, clauseLevel ),
converter.getCreationContext().getTypeConfiguration().getBasicTypeForJavaType( Long.class ) converter.getCreationContext().getTypeConfiguration().getBasicTypeForJavaType( Long.class )
); );
} }

View File

@ -10,10 +10,10 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.QueryException; import org.hibernate.QueryException;
import org.hibernate.query.ReturnableType; import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause; import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause;
import org.hibernate.sql.ast.tree.expression.JsonQueryEmptyBehavior; import org.hibernate.sql.ast.tree.expression.JsonQueryEmptyBehavior;
import org.hibernate.sql.ast.tree.expression.JsonQueryErrorBehavior; import org.hibernate.sql.ast.tree.expression.JsonQueryErrorBehavior;
@ -65,7 +65,7 @@ public class PostgreSQLJsonQueryFunction extends JsonQueryFunction {
else { else {
sqlAppender.appendSql( "(select t.v from jsonb_path_query(" ); sqlAppender.appendSql( "(select t.v from jsonb_path_query(" );
} }
final boolean needsCast = !isJsonType && jsonDocument instanceof JdbcParameter; final boolean needsCast = !isJsonType && AbstractSqlAstTranslator.isParameter( jsonDocument );
if ( needsCast ) { if ( needsCast ) {
sqlAppender.appendSql( "cast(" ); sqlAppender.appendSql( "cast(" );
} }

View File

@ -10,10 +10,10 @@ import org.hibernate.QueryException;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.ReturnableType; import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
/** /**
@ -33,7 +33,7 @@ public class PostgreSQLJsonRemoveFunction extends AbstractJsonRemoveFunction {
SqlAstTranslator<?> translator) { SqlAstTranslator<?> translator) {
final Expression json = (Expression) arguments.get( 0 ); final Expression json = (Expression) arguments.get( 0 );
final Expression jsonPath = (Expression) arguments.get( 1 ); final Expression jsonPath = (Expression) arguments.get( 1 );
final boolean needsCast = !isJsonType( json ) && json instanceof JdbcParameter; final boolean needsCast = !isJsonType( json ) && AbstractSqlAstTranslator.isParameter( json );
if ( needsCast ) { if ( needsCast ) {
sqlAppender.appendSql( "cast(" ); sqlAppender.appendSql( "cast(" );
} }

View File

@ -10,10 +10,10 @@ import org.hibernate.QueryException;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.ReturnableType; import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
@ -36,7 +36,7 @@ public class PostgreSQLJsonReplaceFunction extends AbstractJsonReplaceFunction {
final Expression jsonPath = (Expression) arguments.get( 1 ); final Expression jsonPath = (Expression) arguments.get( 1 );
final SqlAstNode value = arguments.get( 2 ); final SqlAstNode value = arguments.get( 2 );
sqlAppender.appendSql( "jsonb_set(" ); sqlAppender.appendSql( "jsonb_set(" );
final boolean needsCast = !isJsonType( json ) && json instanceof JdbcParameter; final boolean needsCast = !isJsonType( json ) && AbstractSqlAstTranslator.isParameter( json );
if ( needsCast ) { if ( needsCast ) {
sqlAppender.appendSql( "cast(" ); sqlAppender.appendSql( "cast(" );
} }

View File

@ -10,10 +10,10 @@ import org.hibernate.QueryException;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.ReturnableType; import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.Literal; import org.hibernate.sql.ast.tree.expression.Literal;
import org.hibernate.type.spi.TypeConfiguration; import org.hibernate.type.spi.TypeConfiguration;
@ -36,7 +36,7 @@ public class PostgreSQLJsonSetFunction extends AbstractJsonSetFunction {
final Expression jsonPath = (Expression) arguments.get( 1 ); final Expression jsonPath = (Expression) arguments.get( 1 );
final SqlAstNode value = arguments.get( 2 ); final SqlAstNode value = arguments.get( 2 );
sqlAppender.appendSql( "jsonb_set(" ); sqlAppender.appendSql( "jsonb_set(" );
final boolean needsCast = !isJsonType( json ) && json instanceof JdbcParameter; final boolean needsCast = !isJsonType( json ) && AbstractSqlAstTranslator.isParameter( json );
if ( needsCast ) { if ( needsCast ) {
sqlAppender.appendSql( "cast(" ); sqlAppender.appendSql( "cast(" );
} }

View File

@ -9,10 +9,10 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.derived.AnonymousTupleTableGroupProducer; import org.hibernate.query.derived.AnonymousTupleTableGroupProducer;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.JsonExistsErrorBehavior; import org.hibernate.sql.ast.tree.expression.JsonExistsErrorBehavior;
import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause; import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause;
import org.hibernate.sql.ast.tree.expression.JsonQueryEmptyBehavior; import org.hibernate.sql.ast.tree.expression.JsonQueryEmptyBehavior;
@ -59,7 +59,7 @@ public class PostgreSQLJsonTableFunction extends JsonTableFunction {
sqlAppender.appendSql( " from jsonb_path_query(" ); sqlAppender.appendSql( " from jsonb_path_query(" );
final boolean needsCast = !arguments.isJsonType() && arguments.jsonDocument() instanceof JdbcParameter; final boolean needsCast = !arguments.isJsonType() && AbstractSqlAstTranslator.isParameter( arguments.jsonDocument() );
if ( needsCast ) { if ( needsCast ) {
sqlAppender.appendSql( "cast(" ); sqlAppender.appendSql( "cast(" );
} }

View File

@ -10,11 +10,11 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.QueryException; import org.hibernate.QueryException;
import org.hibernate.query.ReturnableType; import org.hibernate.query.ReturnableType;
import org.hibernate.sql.ast.SqlAstTranslator; import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.ast.tree.expression.CastTarget; import org.hibernate.sql.ast.tree.expression.CastTarget;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause; import org.hibernate.sql.ast.tree.expression.JsonPathPassingClause;
import org.hibernate.sql.ast.tree.expression.JsonValueEmptyBehavior; import org.hibernate.sql.ast.tree.expression.JsonValueEmptyBehavior;
import org.hibernate.sql.ast.tree.expression.JsonValueErrorBehavior; import org.hibernate.sql.ast.tree.expression.JsonValueErrorBehavior;
@ -60,7 +60,7 @@ public class PostgreSQLJsonValueFunction extends JsonValueFunction {
sqlAppender.appendSql( "cast(" ); sqlAppender.appendSql( "cast(" );
} }
sqlAppender.appendSql( "jsonb_path_query_first(" ); sqlAppender.appendSql( "jsonb_path_query_first(" );
final boolean needsCast = !isJsonType && jsonDocument instanceof JdbcParameter; final boolean needsCast = !isJsonType && AbstractSqlAstTranslator.isParameter( jsonDocument );
if ( needsCast ) { if ( needsCast ) {
sqlAppender.appendSql( "cast(" ); sqlAppender.appendSql( "cast(" );
} }

View File

@ -43,22 +43,31 @@ public class SQLServerJsonTableFunction extends JsonTableFunction {
arguments.jsonDocument().accept( walker ); arguments.jsonDocument().accept( walker );
if ( arguments.jsonPath() != null ) { if ( arguments.jsonPath() != null ) {
sqlAppender.appendSql( ',' ); sqlAppender.appendSql( ',' );
// Default behavior is NULL ON ERROR final String rawJsonPath;
final String prefix = arguments.errorBehavior() == JsonTableErrorBehavior.ERROR ? "strict " : "";
final String jsonPathString;
if ( arguments.passingClause() != null ) { if ( arguments.passingClause() != null ) {
jsonPathString = prefix + JsonPathHelper.inlinedJsonPathIncludingPassingClause( arguments.jsonPath(), rawJsonPath = JsonPathHelper.inlinedJsonPathIncludingPassingClause(
arguments.passingClause(), walker ); arguments.jsonPath(),
arguments.passingClause(),
walker
);
} }
else { else {
jsonPathString = prefix + walker.getLiteralValue( arguments.jsonPath() ); rawJsonPath = walker.getLiteralValue( arguments.jsonPath() );
} }
if ( jsonPathString.endsWith( "[*]" ) ) { final String jsonPath;
sqlAppender.appendSingleQuoteEscapedString( jsonPathString.substring( 0, jsonPathString.length() - 3 ) ); if ( arguments.errorBehavior() == JsonTableErrorBehavior.ERROR ) {
// Default behavior is NULL ON ERROR
jsonPath = "strict " + rawJsonPath;
} }
else { else {
sqlAppender.appendSingleQuoteEscapedString( jsonPathString ); jsonPath = rawJsonPath;
} }
sqlAppender.appendSingleQuoteEscapedString(
// openjson unwraps arrays automatically and doesn't support this syntax, so remove it
jsonPath.endsWith( "[*]" )
? jsonPath.substring( 0, jsonPath.length() - 3 )
: jsonPath
);
} }
else if ( arguments.errorBehavior() == JsonTableErrorBehavior.ERROR ) { else if ( arguments.errorBehavior() == JsonTableErrorBehavior.ERROR ) {
// Default behavior is NULL ON ERROR // Default behavior is NULL ON ERROR