Skip to content

Commit

Permalink
HHH-17639 Make recursive CTE cycle detection emulation independent of…
Browse files Browse the repository at this point in the history
… collation
  • Loading branch information
beikov committed Jan 15, 2024
1 parent 899bf7b commit 8250f13
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -250,4 +250,13 @@ public void visitCastTarget(CastTarget castTarget) {
super.visitCastTarget( castTarget );
}
}

@Override
protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) {
// MariaDB can't cope with NUL characters in the position function, so we use a like predicate instead
haystack.accept( this );
appendSql( " like concat('%',replace(replace(replace(" );
needle.accept( this );
appendSql( ",'~','~~'),'?','~?'),'%','~%'),'%') escape '~'" );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -258,4 +258,13 @@ public void visitCastTarget(CastTarget castTarget) {
super.visitCastTarget( castTarget );
}
}

@Override
protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) {
// MySQL can't cope with NUL characters in the position function, so we use a like predicate instead
haystack.accept( this );
appendSql( " like concat('%',replace(replace(replace(" );
needle.accept( this );
appendSql( ",'~','~~'),'?','~?'),'%','~%'),'%') escape '~'" );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -470,4 +470,15 @@ enum OffsetFetchClauseMode {
TOP_ONLY,
EMULATED;
}

@Override
protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) {
// SQL Server ignores NUL characters in string on case-insensitive collations, so we force a binary collation.
// This is needed for the emulation of cycle detection in recursive queries
appendSql( "charindex(" );
needle.accept( this );
appendSql( " collate Latin1_General_100_BIN2," );
haystack.accept( this );
append( ")>0" );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -240,4 +240,13 @@ public void visitCastTarget(CastTarget castTarget) {
}
}

@Override
protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) {
// MariaDB can't cope with NUL characters in the position function, so we use a like predicate instead
haystack.accept( this );
appendSql( " like concat('%',replace(replace(replace(" );
needle.accept( this );
appendSql( ",'~','~~'),'?','~?'),'%','~%'),'%') escape '~'" );
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -294,4 +294,13 @@ public void visitCastTarget(CastTarget castTarget) {
super.visitCastTarget( castTarget );
}
}

@Override
protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) {
// MySQL can't cope with NUL characters in the position function, so we use a like predicate instead
haystack.accept( this );
appendSql( " like concat('%',replace(replace(replace(" );
needle.accept( this );
appendSql( ",'~','~~'),'?','~?'),'%','~%'),'%') escape '~'" );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.query.sqm.FetchClauseType;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression;
import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.SqlSelection;
Expand Down Expand Up @@ -451,4 +453,15 @@ protected void renderMergeStatement(OptionalTableUpdate optionalTableUpdate) {
super.renderMergeStatement( optionalTableUpdate );
appendSql( ";" );
}

@Override
protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) {
// SQL Server ignores NUL characters in string on case-insensitive collations, so we force a binary collation.
// This is needed for the emulation of cycle detection in recursive queries
appendSql( "charindex(" );
needle.accept( this );
appendSql( " collate Latin1_General_100_BIN2," );
haystack.accept( this );
append( ")>0" );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,4 +201,13 @@ public void visitCastTarget(CastTarget castTarget) {
super.visitCastTarget( castTarget );
}
}

@Override
protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) {
// TiDB can't cope with NUL characters in the position function, so we use a like predicate instead
haystack.accept( this );
appendSql( " like concat('%',replace(replace(replace(" );
needle.accept( this );
appendSql( ",'~','~~'),'?','~?'),'%','~%'),'%') escape '~'" );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2660,7 +2660,6 @@ private void emulateCycleClauseWithString(SelectClause selectClause) {
null,
stringType
);
arguments.add( new QueryLiteral<>( "%", stringType ) );
for ( CteColumn cycleColumn : currentCteStatement.getCycleColumns() ) {
final int selectionIndex = currentCteStatement.getCteTable()
.getCteColumns()
Expand All @@ -2683,14 +2682,20 @@ private void emulateCycleClauseWithString(SelectClause selectClause) {
arguments.add( nullSeparator );
}
arguments.add( nullSeparator );
arguments.add( new QueryLiteral<>( "%", stringType ) );

if ( !supportsRecursiveCycleClause() ) {
// Cycle mark
appendSql( "case when " );
visitColumnReference( cyclePathColumnReference );
appendSql( " like " );
concat.render( this, arguments, stringType, this );
renderStringContainsExactlyPredicate(
cyclePathColumnReference,
new SelfRenderingFunctionSqlAstExpression(
"concat",
concat,
arguments,
stringType,
stringType
)
);
appendSql( " then " );
currentCteStatement.getCycleValue().accept( this );
appendSql( " else " );
Expand All @@ -2699,9 +2704,8 @@ private void emulateCycleClauseWithString(SelectClause selectClause) {
appendSql( COMMA_SEPARATOR );
}

// Remove the wildcard literals
arguments.remove( arguments.size() - 1 );
arguments.set( 0, cyclePathColumnReference );
// Add the previous path
arguments.add( 0, cyclePathColumnReference );
// Cycle path
concat.render( this, arguments, stringType, this );
}
Expand Down Expand Up @@ -2750,6 +2754,18 @@ private void emulateCycleClauseWithString(SelectClause selectClause) {
}
}

protected void renderStringContainsExactlyPredicate(Expression haystack, Expression needle) {
final AbstractSqmSelfRenderingFunctionDescriptor position = findSelfRenderingFunction( "position", 2 );
new SelfRenderingFunctionSqlAstExpression(
"position",
position,
List.of( needle, haystack ),
getStringType(),
getStringType()
).accept( this );
append( ">0" );
}

/**
* Wraps the given expression so that it produces a string, which should have the same ordering as the original value.
* Here are the mappings for various data types:
Expand Down

0 comments on commit 8250f13

Please sign in to comment.