HHH-18604 Fix some issues with old SQL Server versions
This commit is contained in:
parent
791152d858
commit
ec502138b1
|
@ -403,20 +403,20 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
|||
if ( getVersion().isSameOrAfter( 13 ) ) {
|
||||
functionFactory.jsonValue_sqlserver();
|
||||
functionFactory.jsonQuery_sqlserver();
|
||||
functionFactory.jsonExists_sqlserver();
|
||||
functionFactory.jsonObject_sqlserver();
|
||||
functionFactory.jsonArray_sqlserver();
|
||||
functionFactory.jsonExists_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
functionFactory.jsonObject_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
functionFactory.jsonArray_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
functionFactory.jsonSet_sqlserver();
|
||||
functionFactory.jsonRemove_sqlserver();
|
||||
functionFactory.jsonReplace_sqlserver();
|
||||
functionFactory.jsonInsert_sqlserver();
|
||||
functionFactory.jsonArrayAppend_sqlserver();
|
||||
functionFactory.jsonReplace_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
functionFactory.jsonInsert_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
functionFactory.jsonArrayAppend_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
functionFactory.jsonArrayInsert_sqlserver();
|
||||
}
|
||||
if ( getVersion().isSameOrAfter( 14 ) ) {
|
||||
functionFactory.listagg_stringAggWithinGroup( "varchar(max)" );
|
||||
functionFactory.jsonArrayAgg_sqlserver();
|
||||
functionFactory.jsonObjectAgg_sqlserver();
|
||||
functionFactory.jsonArrayAgg_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
functionFactory.jsonObjectAgg_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
}
|
||||
if ( getVersion().isSameOrAfter( 16 ) ) {
|
||||
functionFactory.leastGreatest();
|
||||
|
|
|
@ -421,20 +421,20 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
if ( getVersion().isSameOrAfter( 13 ) ) {
|
||||
functionFactory.jsonValue_sqlserver();
|
||||
functionFactory.jsonQuery_sqlserver();
|
||||
functionFactory.jsonExists_sqlserver();
|
||||
functionFactory.jsonObject_sqlserver();
|
||||
functionFactory.jsonArray_sqlserver();
|
||||
functionFactory.jsonExists_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
functionFactory.jsonObject_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
functionFactory.jsonArray_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
functionFactory.jsonSet_sqlserver();
|
||||
functionFactory.jsonRemove_sqlserver();
|
||||
functionFactory.jsonReplace_sqlserver();
|
||||
functionFactory.jsonInsert_sqlserver();
|
||||
functionFactory.jsonArrayAppend_sqlserver();
|
||||
functionFactory.jsonReplace_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
functionFactory.jsonInsert_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
functionFactory.jsonArrayAppend_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
functionFactory.jsonArrayInsert_sqlserver();
|
||||
}
|
||||
if ( getVersion().isSameOrAfter( 14 ) ) {
|
||||
functionFactory.listagg_stringAggWithinGroup( "varchar(max)" );
|
||||
functionFactory.jsonArrayAgg_sqlserver();
|
||||
functionFactory.jsonObjectAgg_sqlserver();
|
||||
functionFactory.jsonArrayAgg_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
functionFactory.jsonObjectAgg_sqlserver( getVersion().isSameOrAfter( 16 ) );
|
||||
}
|
||||
if ( getVersion().isSameOrAfter( 16 ) ) {
|
||||
functionFactory.leastGreatest();
|
||||
|
|
|
@ -3557,8 +3557,8 @@ public class CommonFunctionFactory {
|
|||
/**
|
||||
* SQL Server json_exists() function
|
||||
*/
|
||||
public void jsonExists_sqlserver() {
|
||||
functionRegistry.register( "json_exists", new SQLServerJsonExistsFunction( typeConfiguration ) );
|
||||
public void jsonExists_sqlserver(boolean supportsExtendedJson) {
|
||||
functionRegistry.register( "json_exists", new SQLServerJsonExistsFunction( supportsExtendedJson, typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3613,8 +3613,8 @@ public class CommonFunctionFactory {
|
|||
/**
|
||||
* SQL Server json_object() function
|
||||
*/
|
||||
public void jsonObject_sqlserver() {
|
||||
functionRegistry.register( "json_object", new SQLServerJsonObjectFunction( typeConfiguration ) );
|
||||
public void jsonObject_sqlserver(boolean supportsExtendedJson) {
|
||||
functionRegistry.register( "json_object", new SQLServerJsonObjectFunction( supportsExtendedJson, typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3669,8 +3669,8 @@ public class CommonFunctionFactory {
|
|||
/**
|
||||
* SQL Server json_array() function
|
||||
*/
|
||||
public void jsonArray_sqlserver() {
|
||||
functionRegistry.register( "json_array", new SQLServerJsonArrayFunction( typeConfiguration ) );
|
||||
public void jsonArray_sqlserver(boolean supportsExtendedJson) {
|
||||
functionRegistry.register( "json_array", new SQLServerJsonArrayFunction( supportsExtendedJson, typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3739,8 +3739,8 @@ public class CommonFunctionFactory {
|
|||
/**
|
||||
* SQL Server json_arrayagg() function
|
||||
*/
|
||||
public void jsonArrayAgg_sqlserver() {
|
||||
functionRegistry.register( "json_arrayagg", new SQLServerJsonArrayAggFunction( typeConfiguration ) );
|
||||
public void jsonArrayAgg_sqlserver(boolean supportsExtendedJson) {
|
||||
functionRegistry.register( "json_arrayagg", new SQLServerJsonArrayAggFunction( supportsExtendedJson, typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3809,8 +3809,8 @@ public class CommonFunctionFactory {
|
|||
/**
|
||||
* SQL Server json_objectagg() function
|
||||
*/
|
||||
public void jsonObjectAgg_sqlserver() {
|
||||
functionRegistry.register( "json_objectagg", new SQLServerJsonObjectAggFunction( typeConfiguration ) );
|
||||
public void jsonObjectAgg_sqlserver(boolean supportsExtendedJson) {
|
||||
functionRegistry.register( "json_objectagg", new SQLServerJsonObjectAggFunction( supportsExtendedJson, typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3943,8 +3943,8 @@ public class CommonFunctionFactory {
|
|||
/**
|
||||
* SQL server json_replace() function
|
||||
*/
|
||||
public void jsonReplace_sqlserver() {
|
||||
functionRegistry.register( "json_replace", new SQLServerJsonReplaceFunction( typeConfiguration ) );
|
||||
public void jsonReplace_sqlserver(boolean supportsExtendedJson) {
|
||||
functionRegistry.register( "json_replace", new SQLServerJsonReplaceFunction( supportsExtendedJson, typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3981,8 +3981,8 @@ public class CommonFunctionFactory {
|
|||
/**
|
||||
* SQL server json_insert() function
|
||||
*/
|
||||
public void jsonInsert_sqlserver() {
|
||||
functionRegistry.register( "json_insert", new SQLServerJsonInsertFunction( typeConfiguration ) );
|
||||
public void jsonInsert_sqlserver(boolean supportsExtendedJson) {
|
||||
functionRegistry.register( "json_insert", new SQLServerJsonInsertFunction( supportsExtendedJson, typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4056,8 +4056,8 @@ public class CommonFunctionFactory {
|
|||
/**
|
||||
* SQL server json_array_append() function
|
||||
*/
|
||||
public void jsonArrayAppend_sqlserver() {
|
||||
functionRegistry.register( "json_array_append", new SQLServerJsonArrayAppendFunction( typeConfiguration ) );
|
||||
public void jsonArrayAppend_sqlserver(boolean supportsExtendedJson) {
|
||||
functionRegistry.register( "json_array_append", new SQLServerJsonArrayAppendFunction( supportsExtendedJson, typeConfiguration ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,6 +9,7 @@ import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
|||
import org.hibernate.query.sqm.CastType;
|
||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
|
||||
@Internal
|
||||
public class ExpressionTypeHelper {
|
||||
|
@ -34,6 +35,12 @@ public class ExpressionTypeHelper {
|
|||
&& expressionType.getSingleJdbcMapping().getJdbcType().isJson();
|
||||
}
|
||||
|
||||
public static JdbcType getSingleJdbcType(SqlAstNode node) {
|
||||
final Expression expression = (Expression) node;
|
||||
final JdbcMappingContainer expressionType = expression.getExpressionType();
|
||||
return expressionType.getSingleJdbcMapping().getJdbcType();
|
||||
}
|
||||
|
||||
public static boolean isBoolean(CastType castType) {
|
||||
switch ( castType ) {
|
||||
case BOOLEAN:
|
||||
|
|
|
@ -204,6 +204,51 @@ public class JsonPathHelper {
|
|||
return i + 1;
|
||||
}
|
||||
|
||||
public static void inlinePassingClause(
|
||||
List<JsonPathElement> jsonPathElements,
|
||||
JsonPathPassingClause passingClause,
|
||||
SqlAstTranslator<?> walker) {
|
||||
for ( int i = 0; i < jsonPathElements.size(); i++ ) {
|
||||
final JsonPathElement jsonPathElement = jsonPathElements.get( i );
|
||||
if ( jsonPathElement instanceof JsonParameterIndexAccess parameterIndexAccess ) {
|
||||
final Expression expression = passingClause.getPassingExpressions()
|
||||
.get( parameterIndexAccess.parameterName() );
|
||||
if ( expression == null ) {
|
||||
throw new QueryException( "JSON path [" + toJsonPath( jsonPathElements ) + "] uses parameter [" + parameterIndexAccess.parameterName() + "] that is not passed" );
|
||||
}
|
||||
jsonPathElements.set( i, new JsonIndexAccess( walker.getLiteralValue( expression ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static String toJsonPath(List<JsonPathElement> pathElements) {
|
||||
return toJsonPath( pathElements, 0, pathElements.size() );
|
||||
}
|
||||
|
||||
public static String toJsonPath(List<JsonPathElement> pathElements, int start, int end) {
|
||||
final StringBuilder jsonPath = new StringBuilder();
|
||||
jsonPath.append( "$" );
|
||||
for ( int i = start; i < end; i++ ) {
|
||||
final JsonPathElement jsonPathElement = pathElements.get( i );
|
||||
if ( jsonPathElement instanceof JsonAttribute pathAttribute ) {
|
||||
jsonPath.append( '.' );
|
||||
jsonPath.append( pathAttribute.attribute() );
|
||||
}
|
||||
else if ( jsonPathElement instanceof JsonParameterIndexAccess parameterIndexAccess ) {
|
||||
jsonPath.append( "[$" );
|
||||
jsonPath.append( parameterIndexAccess.parameterName() );
|
||||
jsonPath.append( "]" );
|
||||
}
|
||||
else {
|
||||
assert jsonPathElement instanceof JsonIndexAccess;
|
||||
jsonPath.append( "[" );
|
||||
jsonPath.append( ( (JsonIndexAccess) jsonPathElement ).index() );
|
||||
jsonPath.append( "]" );
|
||||
}
|
||||
}
|
||||
return jsonPath.toString();
|
||||
}
|
||||
|
||||
public sealed interface JsonPathElement {}
|
||||
public record JsonAttribute(String attribute) implements JsonPathElement {}
|
||||
public record JsonIndexAccess(int index) implements JsonPathElement {}
|
||||
|
|
|
@ -17,6 +17,8 @@ import org.hibernate.sql.ast.tree.expression.Expression;
|
|||
import org.hibernate.sql.ast.tree.expression.JsonNullBehavior;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
|
@ -24,8 +26,11 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
*/
|
||||
public class SQLServerJsonArrayAggFunction extends JsonArrayAggFunction {
|
||||
|
||||
public SQLServerJsonArrayAggFunction(TypeConfiguration typeConfiguration) {
|
||||
private final boolean supportsExtendedJson;
|
||||
|
||||
public SQLServerJsonArrayAggFunction(boolean supportsExtendedJson, TypeConfiguration typeConfiguration) {
|
||||
super( false, typeConfiguration );
|
||||
this.supportsExtendedJson = supportsExtendedJson;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,10 +95,38 @@ public class SQLServerJsonArrayAggFunction extends JsonArrayAggFunction {
|
|||
Expression arg,
|
||||
JsonNullBehavior nullBehavior,
|
||||
SqlAstTranslator<?> translator) {
|
||||
sqlAppender.appendSql( "substring(json_array(" );
|
||||
arg.accept( translator );
|
||||
sqlAppender.appendSql( " null on null),2,len(json_array(" );
|
||||
arg.accept( translator );
|
||||
sqlAppender.appendSql( " null on null))-2)" );
|
||||
if ( supportsExtendedJson ) {
|
||||
sqlAppender.appendSql( "substring(json_array(" );
|
||||
arg.accept( translator );
|
||||
sqlAppender.appendSql( " null on null),2,len(json_array(" );
|
||||
arg.accept( translator );
|
||||
sqlAppender.appendSql( "))-2)" );
|
||||
}
|
||||
else {
|
||||
sqlAppender.appendSql( "substring(json_modify('[]','append $'," );
|
||||
final boolean needsConversion = needsConversion( arg );
|
||||
if ( needsConversion ) {
|
||||
sqlAppender.appendSql( "convert(nvarchar(max)," );
|
||||
}
|
||||
arg.accept( translator );
|
||||
if ( needsConversion ) {
|
||||
sqlAppender.appendSql( ')' );
|
||||
}
|
||||
sqlAppender.appendSql( "),2,len(json_modify('[]','append $'," );
|
||||
if ( needsConversion ) {
|
||||
sqlAppender.appendSql( "convert(nvarchar(max)," );
|
||||
}
|
||||
arg.accept( translator );
|
||||
if ( needsConversion ) {
|
||||
sqlAppender.appendSql( ')' );
|
||||
}
|
||||
sqlAppender.appendSql( "))-2)" );
|
||||
}
|
||||
}
|
||||
|
||||
static boolean needsConversion(Expression arg) {
|
||||
final JdbcType jdbcType = ExpressionTypeHelper.getSingleJdbcType( arg );
|
||||
// json_modify() doesn't seem to like UUID values
|
||||
return jdbcType.getDdlTypeCode() == SqlTypes.UUID;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,11 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
*/
|
||||
public class SQLServerJsonArrayAppendFunction extends AbstractJsonArrayAppendFunction {
|
||||
|
||||
public SQLServerJsonArrayAppendFunction(TypeConfiguration typeConfiguration) {
|
||||
private final boolean supportsExtendedJson;
|
||||
|
||||
public SQLServerJsonArrayAppendFunction(boolean supportsExtendedJson, TypeConfiguration typeConfiguration) {
|
||||
super( typeConfiguration );
|
||||
this.supportsExtendedJson = supportsExtendedJson;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -33,7 +36,31 @@ public class SQLServerJsonArrayAppendFunction extends AbstractJsonArrayAppendFun
|
|||
final SqlAstNode value = arguments.get( 2 );
|
||||
sqlAppender.appendSql( "(select coalesce(" );
|
||||
sqlAppender.appendSql("case when json_modify(json_query(t.d,t.p),'append $',t.v) is not null then json_modify(t.d,t.p,json_modify(json_query(t.d,t.p),'append $',t.v)) end,");
|
||||
sqlAppender.appendSql("json_modify(t.d,t.p,json_query('['+coalesce(json_value(t.d,t.p),case when json_path_exists(t.d,t.p)=1 then 'null' end)+stuff(json_array(t.v),1,1,','))),");
|
||||
sqlAppender.appendSql("json_modify(t.d,t.p,json_query('['+coalesce(json_value(t.d,t.p),case when ");
|
||||
if ( supportsExtendedJson ) {
|
||||
sqlAppender.appendSql( "json_path_exists(t.d,t.p)=1" );
|
||||
}
|
||||
else {
|
||||
final List<JsonPathHelper.JsonPathElement> pathElements =
|
||||
JsonPathHelper.parseJsonPathElements( translator.getLiteralValue( jsonPath ) );
|
||||
final JsonPathHelper.JsonPathElement lastPathElement = pathElements.get( pathElements.size() - 1 );
|
||||
final String prefix = JsonPathHelper.toJsonPath( pathElements, 0, pathElements.size() - 1 );
|
||||
final String terminalKey;
|
||||
if ( lastPathElement instanceof JsonPathHelper.JsonIndexAccess indexAccess ) {
|
||||
terminalKey = String.valueOf( indexAccess.index() );
|
||||
}
|
||||
else {
|
||||
assert lastPathElement instanceof JsonPathHelper.JsonAttribute;
|
||||
terminalKey = ( (JsonPathHelper.JsonAttribute) lastPathElement ).attribute();
|
||||
}
|
||||
|
||||
sqlAppender.appendSql( "(select 1 from openjson(t.d," );
|
||||
sqlAppender.appendSingleQuoteEscapedString( prefix );
|
||||
sqlAppender.appendSql( ") t where t.[key]=" );
|
||||
sqlAppender.appendSingleQuoteEscapedString( terminalKey );
|
||||
sqlAppender.appendSql( ")=1" );
|
||||
}
|
||||
sqlAppender.appendSql( " then 'null' end)+stuff(json_modify('[]','append $',t.v),1,1,','))),");
|
||||
sqlAppender.appendSql( "t.d) from (values (" );
|
||||
json.accept( translator );
|
||||
sqlAppender.appendSql( ',' );
|
||||
|
@ -49,6 +76,11 @@ public class SQLServerJsonArrayAppendFunction extends AbstractJsonArrayAppendFun
|
|||
value.accept( translator );
|
||||
sqlAppender.appendSql( " as bit)" );
|
||||
}
|
||||
else if ( ExpressionTypeHelper.isJson( value ) ) {
|
||||
sqlAppender.appendSql( "json_query(" );
|
||||
value.accept( translator );
|
||||
sqlAppender.appendSql( ')' );
|
||||
}
|
||||
else {
|
||||
value.accept( translator );
|
||||
}
|
||||
|
|
|
@ -4,9 +4,13 @@
|
|||
*/
|
||||
package org.hibernate.dialect.function.json;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.query.ReturnableType;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||
import org.hibernate.sql.ast.tree.expression.JsonNullBehavior;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
|
@ -14,8 +18,66 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
*/
|
||||
public class SQLServerJsonArrayFunction extends JsonArrayFunction {
|
||||
|
||||
public SQLServerJsonArrayFunction(TypeConfiguration typeConfiguration) {
|
||||
private final boolean supportsExtendedJson;
|
||||
|
||||
public SQLServerJsonArrayFunction(boolean supportsExtendedJson, TypeConfiguration typeConfiguration) {
|
||||
super( typeConfiguration );
|
||||
this.supportsExtendedJson = supportsExtendedJson;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
SqlAppender sqlAppender,
|
||||
List<? extends SqlAstNode> sqlAstArguments,
|
||||
ReturnableType<?> returnType,
|
||||
SqlAstTranslator<?> walker) {
|
||||
if ( supportsExtendedJson ) {
|
||||
super.render( sqlAppender, sqlAstArguments, returnType, walker );
|
||||
}
|
||||
else {
|
||||
if ( sqlAstArguments.isEmpty() ) {
|
||||
sqlAppender.appendSql( "'[]'" );
|
||||
}
|
||||
else {
|
||||
final SqlAstNode lastArgument = sqlAstArguments.get( sqlAstArguments.size() - 1 );
|
||||
final JsonNullBehavior nullBehavior;
|
||||
final int argumentsCount;
|
||||
if ( lastArgument instanceof JsonNullBehavior ) {
|
||||
nullBehavior = (JsonNullBehavior) lastArgument;
|
||||
argumentsCount = sqlAstArguments.size() - 1;
|
||||
}
|
||||
else {
|
||||
nullBehavior = JsonNullBehavior.ABSENT;
|
||||
argumentsCount = sqlAstArguments.size();
|
||||
}
|
||||
if ( nullBehavior == JsonNullBehavior.ABSENT ) {
|
||||
sqlAppender.appendSql( "(select '['+string_agg(substring(t.d,2,len(t.d)-2),',')" );
|
||||
sqlAppender.appendSql( "within group (order by t.k)+']' from (values" );
|
||||
char separator = ' ';
|
||||
for ( int i = 0; i < argumentsCount; i++ ) {
|
||||
sqlAppender.appendSql( separator );
|
||||
sqlAppender.appendSql( '(' );
|
||||
sqlAppender.appendSql( i );
|
||||
sqlAppender.appendSql( ",json_modify('[]','append $'," );
|
||||
renderValue( sqlAppender, sqlAstArguments.get( i ), walker );
|
||||
sqlAppender.appendSql( "))" );
|
||||
separator = ',';
|
||||
}
|
||||
sqlAppender.appendSql( ") t(k,d))" );
|
||||
}
|
||||
else {
|
||||
for ( int i = 0; i < argumentsCount; i++ ) {
|
||||
sqlAppender.appendSql( "json_modify(" );
|
||||
}
|
||||
sqlAppender.appendSql( "'[]'" );
|
||||
for ( int i = 0; i < argumentsCount; i++ ) {
|
||||
sqlAppender.appendSql( ",'append $'," );
|
||||
renderValue( sqlAppender, sqlAstArguments.get( i ), walker );
|
||||
sqlAppender.appendSql( ')' );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -25,6 +87,11 @@ public class SQLServerJsonArrayFunction extends JsonArrayFunction {
|
|||
value.accept( walker );
|
||||
sqlAppender.appendSql( " as bit)" );
|
||||
}
|
||||
else if ( !supportsExtendedJson && ExpressionTypeHelper.isJson( value ) ) {
|
||||
sqlAppender.appendSql( "json_query(" );
|
||||
value.accept( walker );
|
||||
sqlAppender.appendSql( ')' );
|
||||
}
|
||||
else {
|
||||
value.accept( walker );
|
||||
}
|
||||
|
|
|
@ -79,10 +79,10 @@ public class SQLServerJsonArrayInsertFunction extends AbstractJsonArrayInsertFun
|
|||
SqlAppender sqlAppender,
|
||||
SqlAstNode arg,
|
||||
SqlAstTranslator<?> translator) {
|
||||
sqlAppender.appendSql( "substring(json_array(" );
|
||||
sqlAppender.appendSql( "substring(json_modify('[]','append $'," );
|
||||
arg.accept( translator );
|
||||
sqlAppender.appendSql( " null on null),2,len(json_array(" );
|
||||
sqlAppender.appendSql( "),2,len(json_modify('[]','append $'," );
|
||||
arg.accept( translator );
|
||||
sqlAppender.appendSql( " null on null))-2)" );
|
||||
sqlAppender.appendSql( "))-2)" );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
*/
|
||||
package org.hibernate.dialect.function.json;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.QueryException;
|
||||
import org.hibernate.query.ReturnableType;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
|
@ -17,13 +19,11 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
*/
|
||||
public class SQLServerJsonExistsFunction extends JsonExistsFunction {
|
||||
|
||||
public SQLServerJsonExistsFunction(TypeConfiguration typeConfiguration) {
|
||||
super( typeConfiguration, true, false );
|
||||
}
|
||||
private final boolean supportsExtendedJson;
|
||||
|
||||
@Override
|
||||
public boolean isPredicate() {
|
||||
return false;
|
||||
public SQLServerJsonExistsFunction(boolean supportsExtendedJson, TypeConfiguration typeConfiguration) {
|
||||
super( typeConfiguration, true, false );
|
||||
this.supportsExtendedJson = supportsExtendedJson;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -35,35 +35,14 @@ public class SQLServerJsonExistsFunction extends JsonExistsFunction {
|
|||
if ( arguments.errorBehavior() == JsonExistsErrorBehavior.TRUE ) {
|
||||
throw new QueryException( "Can't emulate json_exists(... true on error) on SQL Server" );
|
||||
}
|
||||
if ( arguments.errorBehavior() == JsonExistsErrorBehavior.ERROR ) {
|
||||
sqlAppender.append( '(' );
|
||||
}
|
||||
sqlAppender.appendSql( "json_path_exists(" );
|
||||
arguments.jsonDocument().accept( walker );
|
||||
sqlAppender.appendSql( ',' );
|
||||
final JsonPathPassingClause passingClause = arguments.passingClause();
|
||||
if ( passingClause != null ) {
|
||||
JsonPathHelper.appendInlinedJsonPathIncludingPassingClause(
|
||||
sqlAppender,
|
||||
"",
|
||||
arguments.jsonPath(),
|
||||
passingClause,
|
||||
walker
|
||||
);
|
||||
}
|
||||
else {
|
||||
walker.getSessionFactory().getJdbcServices().getDialect().appendLiteral(
|
||||
sqlAppender,
|
||||
walker.getLiteralValue( arguments.jsonPath() )
|
||||
);
|
||||
}
|
||||
sqlAppender.appendSql( ')' );
|
||||
if ( arguments.errorBehavior() == JsonExistsErrorBehavior.ERROR ) {
|
||||
// json_path_exists returns 0 if an invalid JSON is given,
|
||||
// so we have to run openjson to be sure the json is valid and potentially throw an error
|
||||
sqlAppender.appendSql( "=1 or (select v from openjson(" );
|
||||
if ( supportsExtendedJson ) {
|
||||
if ( arguments.errorBehavior() == JsonExistsErrorBehavior.ERROR ) {
|
||||
sqlAppender.append( '(' );
|
||||
}
|
||||
sqlAppender.appendSql( "json_path_exists(" );
|
||||
arguments.jsonDocument().accept( walker );
|
||||
sqlAppender.appendSql( ") with (v varchar(max) " );
|
||||
sqlAppender.appendSql( ',' );
|
||||
final JsonPathPassingClause passingClause = arguments.passingClause();
|
||||
if ( passingClause != null ) {
|
||||
JsonPathHelper.appendInlinedJsonPathIncludingPassingClause(
|
||||
sqlAppender,
|
||||
|
@ -79,7 +58,59 @@ public class SQLServerJsonExistsFunction extends JsonExistsFunction {
|
|||
walker.getLiteralValue( arguments.jsonPath() )
|
||||
);
|
||||
}
|
||||
sqlAppender.appendSql( ")) is null)" );
|
||||
sqlAppender.appendSql( ")=1" );
|
||||
if ( arguments.errorBehavior() == JsonExistsErrorBehavior.ERROR ) {
|
||||
// json_path_exists returns 0 if an invalid JSON is given,
|
||||
// so we have to run openjson to be sure the json is valid and potentially throw an error
|
||||
sqlAppender.appendSql( " or (select v from openjson(" );
|
||||
arguments.jsonDocument().accept( walker );
|
||||
sqlAppender.appendSql( ") with (v varchar(max) " );
|
||||
if ( passingClause != null ) {
|
||||
JsonPathHelper.appendInlinedJsonPathIncludingPassingClause(
|
||||
sqlAppender,
|
||||
"",
|
||||
arguments.jsonPath(),
|
||||
passingClause,
|
||||
walker
|
||||
);
|
||||
}
|
||||
else {
|
||||
walker.getSessionFactory().getJdbcServices().getDialect().appendLiteral(
|
||||
sqlAppender,
|
||||
walker.getLiteralValue( arguments.jsonPath() )
|
||||
);
|
||||
}
|
||||
sqlAppender.appendSql( ")) is null)" );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( arguments.errorBehavior() == JsonExistsErrorBehavior.FALSE ) {
|
||||
throw new QueryException( "Can't emulate json_exists(... false on error) on SQL Server" );
|
||||
}
|
||||
final String jsonPath = walker.getLiteralValue( arguments.jsonPath() );
|
||||
final JsonPathPassingClause passingClause = arguments.passingClause();
|
||||
final List<JsonPathHelper.JsonPathElement> pathElements = JsonPathHelper.parseJsonPathElements( jsonPath );
|
||||
if ( passingClause != null ) {
|
||||
JsonPathHelper.inlinePassingClause( pathElements, passingClause, walker );
|
||||
}
|
||||
final JsonPathHelper.JsonPathElement lastPathElement = pathElements.get( pathElements.size() - 1 );
|
||||
final String prefix = JsonPathHelper.toJsonPath( pathElements, 0, pathElements.size() - 1 );
|
||||
final String terminalKey;
|
||||
if ( lastPathElement instanceof JsonPathHelper.JsonIndexAccess indexAccess ) {
|
||||
terminalKey = String.valueOf( indexAccess.index() );
|
||||
}
|
||||
else {
|
||||
assert lastPathElement instanceof JsonPathHelper.JsonAttribute;
|
||||
terminalKey = ( (JsonPathHelper.JsonAttribute) lastPathElement ).attribute();
|
||||
}
|
||||
|
||||
sqlAppender.appendSql( "(select 1 from openjson(" );
|
||||
arguments.jsonDocument().accept( walker );
|
||||
sqlAppender.appendSql( ',' );
|
||||
sqlAppender.appendSingleQuoteEscapedString( prefix );
|
||||
sqlAppender.appendSql( ") t where t.[key]=" );
|
||||
sqlAppender.appendSingleQuoteEscapedString( terminalKey );
|
||||
sqlAppender.appendSql( ")=1" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,11 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
*/
|
||||
public class SQLServerJsonInsertFunction extends AbstractJsonInsertFunction {
|
||||
|
||||
public SQLServerJsonInsertFunction(TypeConfiguration typeConfiguration) {
|
||||
private final boolean supportsExtendedJson;
|
||||
|
||||
public SQLServerJsonInsertFunction(boolean supportsExtendedJson, TypeConfiguration typeConfiguration) {
|
||||
super( typeConfiguration );
|
||||
this.supportsExtendedJson = supportsExtendedJson;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -28,10 +31,35 @@ public class SQLServerJsonInsertFunction extends AbstractJsonInsertFunction {
|
|||
List<? extends SqlAstNode> arguments,
|
||||
ReturnableType<?> returnType,
|
||||
SqlAstTranslator<?> translator) {
|
||||
sqlAppender.appendSql( "(select case when coalesce(json_query(t.d,t.p),json_value(t.d,t.p)) is not null then t.d else json_modify(t.d,t.p," );
|
||||
final Expression json = (Expression) arguments.get( 0 );
|
||||
final Expression jsonPath = (Expression) arguments.get( 1 );
|
||||
final SqlAstNode value = arguments.get( 2 );
|
||||
|
||||
sqlAppender.appendSql( "(select case when " );
|
||||
if ( supportsExtendedJson ) {
|
||||
sqlAppender.appendSql( "json_path_exists(t.d,t.p)=1" );
|
||||
}
|
||||
else {
|
||||
final List<JsonPathHelper.JsonPathElement> pathElements =
|
||||
JsonPathHelper.parseJsonPathElements( translator.getLiteralValue( jsonPath ) );
|
||||
final JsonPathHelper.JsonPathElement lastPathElement = pathElements.get( pathElements.size() - 1 );
|
||||
final String prefix = JsonPathHelper.toJsonPath( pathElements, 0, pathElements.size() - 1 );
|
||||
final String terminalKey;
|
||||
if ( lastPathElement instanceof JsonPathHelper.JsonIndexAccess indexAccess ) {
|
||||
terminalKey = String.valueOf( indexAccess.index() );
|
||||
}
|
||||
else {
|
||||
assert lastPathElement instanceof JsonPathHelper.JsonAttribute;
|
||||
terminalKey = ( (JsonPathHelper.JsonAttribute) lastPathElement ).attribute();
|
||||
}
|
||||
|
||||
sqlAppender.appendSql( "(select 1 from openjson(t.d," );
|
||||
sqlAppender.appendSingleQuoteEscapedString( prefix );
|
||||
sqlAppender.appendSql( ") t where t.[key]=" );
|
||||
sqlAppender.appendSingleQuoteEscapedString( terminalKey );
|
||||
sqlAppender.appendSql( ")=1" );
|
||||
}
|
||||
sqlAppender.appendSql( " then t.d else json_modify(t.d,t.p," );
|
||||
renderValue( sqlAppender, value, translator );
|
||||
sqlAppender.appendSql( ") end from (values(");
|
||||
json.accept( translator );
|
||||
|
|
|
@ -15,13 +15,18 @@ import org.hibernate.sql.ast.tree.expression.JsonObjectAggUniqueKeysBehavior;
|
|||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
import static org.hibernate.dialect.function.json.SQLServerJsonArrayAggFunction.needsConversion;
|
||||
|
||||
/**
|
||||
* SQL Server json_objectagg function.
|
||||
*/
|
||||
public class SQLServerJsonObjectAggFunction extends JsonObjectAggFunction {
|
||||
|
||||
public SQLServerJsonObjectAggFunction(TypeConfiguration typeConfiguration) {
|
||||
private final boolean supportsExtendedJson;
|
||||
|
||||
public SQLServerJsonObjectAggFunction(boolean supportsExtendedJson, TypeConfiguration typeConfiguration) {
|
||||
super( ",", false, typeConfiguration );
|
||||
this.supportsExtendedJson = supportsExtendedJson;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -66,11 +71,33 @@ public class SQLServerJsonObjectAggFunction extends JsonObjectAggFunction {
|
|||
if ( nullBehavior != JsonNullBehavior.NULL ) {
|
||||
sqlAppender.appendSql( "nullif(" );
|
||||
}
|
||||
sqlAppender.appendSql( "substring(json_array(" );
|
||||
arg.accept( translator );
|
||||
sqlAppender.appendSql( " null on null),2,len(json_array(" );
|
||||
arg.accept( translator );
|
||||
sqlAppender.appendSql( " null on null))-2)" );
|
||||
if ( supportsExtendedJson ) {
|
||||
sqlAppender.appendSql( "substring(json_array(" );
|
||||
arg.accept( translator );
|
||||
sqlAppender.appendSql( " null on null),2,len(json_array(" );
|
||||
arg.accept( translator );
|
||||
sqlAppender.appendSql( " null on null))-2)" );
|
||||
}
|
||||
else {
|
||||
sqlAppender.appendSql( "substring(json_modify('[]','append $'," );
|
||||
final boolean needsConversion = needsConversion( arg );
|
||||
if ( needsConversion ) {
|
||||
sqlAppender.appendSql( "convert(nvarchar(max)," );
|
||||
}
|
||||
arg.accept( translator );
|
||||
if ( needsConversion ) {
|
||||
sqlAppender.appendSql( ')' );
|
||||
}
|
||||
sqlAppender.appendSql( "),2,len(json_modify('[]','append $'," );
|
||||
if ( needsConversion ) {
|
||||
sqlAppender.appendSql( "convert(nvarchar(max)," );
|
||||
}
|
||||
arg.accept( translator );
|
||||
if ( needsConversion ) {
|
||||
sqlAppender.appendSql( ')' );
|
||||
}
|
||||
sqlAppender.appendSql( "))-2)" );
|
||||
}
|
||||
if ( nullBehavior != JsonNullBehavior.NULL ) {
|
||||
sqlAppender.appendSql( ",'null')" );
|
||||
}
|
||||
|
|
|
@ -4,9 +4,13 @@
|
|||
*/
|
||||
package org.hibernate.dialect.function.json;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.query.ReturnableType;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||
import org.hibernate.sql.ast.tree.expression.JsonNullBehavior;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
/**
|
||||
|
@ -14,8 +18,67 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
*/
|
||||
public class SQLServerJsonObjectFunction extends JsonObjectFunction {
|
||||
|
||||
public SQLServerJsonObjectFunction(TypeConfiguration typeConfiguration) {
|
||||
private final boolean supportsExtendedJson;
|
||||
|
||||
public SQLServerJsonObjectFunction(boolean supportsExtendedJson, TypeConfiguration typeConfiguration) {
|
||||
super( typeConfiguration, true );
|
||||
this.supportsExtendedJson = supportsExtendedJson;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(
|
||||
SqlAppender sqlAppender,
|
||||
List<? extends SqlAstNode> sqlAstArguments,
|
||||
ReturnableType<?> returnType,
|
||||
SqlAstTranslator<?> walker) {
|
||||
if ( supportsExtendedJson ) {
|
||||
super.render( sqlAppender, sqlAstArguments, returnType, walker );
|
||||
}
|
||||
else {
|
||||
if ( sqlAstArguments.isEmpty() ) {
|
||||
sqlAppender.appendSql( "'{}'" );
|
||||
}
|
||||
else {
|
||||
final JsonNullBehavior nullBehavior;
|
||||
final int argumentsCount;
|
||||
if ( ( sqlAstArguments.size() & 1 ) == 1 ) {
|
||||
nullBehavior = (JsonNullBehavior) sqlAstArguments.get( sqlAstArguments.size() - 1 );
|
||||
argumentsCount = sqlAstArguments.size() - 1;
|
||||
}
|
||||
else {
|
||||
nullBehavior = JsonNullBehavior.NULL;
|
||||
argumentsCount = sqlAstArguments.size();
|
||||
}
|
||||
if ( nullBehavior == JsonNullBehavior.ABSENT ) {
|
||||
for ( int i = 0; i < argumentsCount; i += 2 ) {
|
||||
sqlAppender.appendSql( "json_modify(" );
|
||||
}
|
||||
sqlAppender.appendSql( "'{}'" );
|
||||
for ( int i = 0; i < argumentsCount; i += 2 ) {
|
||||
sqlAppender.appendSql( ",'$.'+" );
|
||||
sqlAstArguments.get( i ).accept( walker );
|
||||
sqlAppender.appendSql( ',' );
|
||||
renderValue( sqlAppender, sqlAstArguments.get( i + 1 ), walker );
|
||||
sqlAppender.appendSql( ')' );
|
||||
}
|
||||
}
|
||||
else {
|
||||
sqlAppender.appendSql( "(select '{'+string_agg(substring(t.k,2,len(t.k)-2)" );
|
||||
sqlAppender.appendSql( "+':'+substring(t.d,2,len(t.d)-2),',')+'}' from (values" );
|
||||
char separator = ' ';
|
||||
for ( int i = 0; i < argumentsCount; i += 2 ) {
|
||||
sqlAppender.appendSql( separator );
|
||||
sqlAppender.appendSql( "(json_modify('[]','append $'," );
|
||||
sqlAstArguments.get( i ).accept( walker );
|
||||
sqlAppender.appendSql( "),json_modify('[]','append $'," );
|
||||
renderValue( sqlAppender, sqlAstArguments.get( i + 1 ), walker );
|
||||
sqlAppender.appendSql( "))" );
|
||||
separator = ',';
|
||||
}
|
||||
sqlAppender.appendSql( ") t(k,d))" );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -25,6 +88,11 @@ public class SQLServerJsonObjectFunction extends JsonObjectFunction {
|
|||
value.accept( walker );
|
||||
sqlAppender.appendSql( " as bit)" );
|
||||
}
|
||||
else if ( !supportsExtendedJson && ExpressionTypeHelper.isJson( value ) ) {
|
||||
sqlAppender.appendSql( "json_query(" );
|
||||
value.accept( walker );
|
||||
sqlAppender.appendSql( ')' );
|
||||
}
|
||||
else {
|
||||
value.accept( walker );
|
||||
}
|
||||
|
|
|
@ -18,8 +18,11 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
*/
|
||||
public class SQLServerJsonReplaceFunction extends AbstractJsonReplaceFunction {
|
||||
|
||||
public SQLServerJsonReplaceFunction(TypeConfiguration typeConfiguration) {
|
||||
private final boolean supportsExtendedJson;
|
||||
|
||||
public SQLServerJsonReplaceFunction(boolean supportsExtendedJson, TypeConfiguration typeConfiguration) {
|
||||
super( typeConfiguration );
|
||||
this.supportsExtendedJson = supportsExtendedJson;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -28,12 +31,37 @@ public class SQLServerJsonReplaceFunction extends AbstractJsonReplaceFunction {
|
|||
List<? extends SqlAstNode> arguments,
|
||||
ReturnableType<?> returnType,
|
||||
SqlAstTranslator<?> translator) {
|
||||
sqlAppender.appendSql( "(select case when coalesce(json_query(t.d,t.p),json_value(t.d,t.p)) is null then t.d else json_modify(t.d,t.p," );
|
||||
final Expression json = (Expression) arguments.get( 0 );
|
||||
final Expression jsonPath = (Expression) arguments.get( 1 );
|
||||
final SqlAstNode value = arguments.get( 2 );
|
||||
|
||||
sqlAppender.appendSql( "(select case when " );
|
||||
if ( supportsExtendedJson ) {
|
||||
sqlAppender.appendSql( "json_path_exists(t.d,t.p)=1" );
|
||||
}
|
||||
else {
|
||||
final List<JsonPathHelper.JsonPathElement> pathElements =
|
||||
JsonPathHelper.parseJsonPathElements( translator.getLiteralValue( jsonPath ) );
|
||||
final JsonPathHelper.JsonPathElement lastPathElement = pathElements.get( pathElements.size() - 1 );
|
||||
final String prefix = JsonPathHelper.toJsonPath( pathElements, 0, pathElements.size() - 1 );
|
||||
final String terminalKey;
|
||||
if ( lastPathElement instanceof JsonPathHelper.JsonIndexAccess indexAccess ) {
|
||||
terminalKey = String.valueOf( indexAccess.index() );
|
||||
}
|
||||
else {
|
||||
assert lastPathElement instanceof JsonPathHelper.JsonAttribute;
|
||||
terminalKey = ( (JsonPathHelper.JsonAttribute) lastPathElement ).attribute();
|
||||
}
|
||||
|
||||
sqlAppender.appendSql( "(select 1 from openjson(t.d," );
|
||||
sqlAppender.appendSingleQuoteEscapedString( prefix );
|
||||
sqlAppender.appendSql( ") t where t.[key]=" );
|
||||
sqlAppender.appendSingleQuoteEscapedString( terminalKey );
|
||||
sqlAppender.appendSql( ")=1" );
|
||||
}
|
||||
sqlAppender.appendSql( " then json_modify(t.d,t.p," );
|
||||
renderValue( sqlAppender, value, translator );
|
||||
sqlAppender.appendSql( ") end from (values(");
|
||||
sqlAppender.appendSql( ") else t.d end from (values(");
|
||||
json.accept( translator );
|
||||
sqlAppender.appendSql( ',' );
|
||||
jsonPath.accept( translator );
|
||||
|
|
Loading…
Reference in New Issue