HHH-10994 - Relax requirement of 'as' in the alias phrase for the MSSQL LimitHandler implementation.

(cherry picked from commit 714ba62)
This commit is contained in:
Chris Cranford 2016-07-27 14:10:07 -05:00
parent a06cc89cdb
commit 2dc5236a85
2 changed files with 127 additions and 13 deletions

View File

@ -28,13 +28,16 @@ public class SQLServer2005LimitHandler extends AbstractLimitHandler {
private static final String DISTINCT = "distinct";
private static final String ORDER_BY = "order by";
private static final Pattern SELECT_PATTERN = buildShallowIndexPattern( SELECT + "(.)*" );
private static final Pattern FROM_PATTERN = buildShallowIndexPattern( FROM );
private static final Pattern DISTINCT_PATTERN = buildShallowIndexPattern( DISTINCT );
private static final Pattern ORDER_BY_PATTERN = buildShallowIndexPattern( ORDER_BY );
private static final Pattern COMMA_PATTERN = buildShallowIndexPattern( "," );
final String SELECT_DISTINCT_SPACE = "select distinct ";
final String SELECT_SPACE = "select ";
private static final Pattern ALIAS_PATTERN = Pattern.compile( "(?i)\\sas\\s(.)+$" );
private static final Pattern SELECT_PATTERN = buildShallowIndexPattern( SELECT + "(.*)", true );
private static final Pattern FROM_PATTERN = buildShallowIndexPattern( FROM, true );
private static final Pattern DISTINCT_PATTERN = buildShallowIndexPattern( DISTINCT, true );
private static final Pattern ORDER_BY_PATTERN = buildShallowIndexPattern( ORDER_BY, true );
private static final Pattern COMMA_PATTERN = buildShallowIndexPattern( ",", false );
private static final Pattern ALIAS_PATTERN =
Pattern.compile( "\\S+\\s*(\\s(?i)as\\s)\\s*(\\S+)*\\s*$|\\s+(\\S+)$" );
// Flag indicating whether TOP(?) expression has been added to the original query.
private boolean topAdded;
@ -145,7 +148,7 @@ public class SQLServer2005LimitHandler extends AbstractLimitHandler {
*/
protected String fillAliasInSelectClause(StringBuilder sb) {
final List<String> aliases = new LinkedList<String>();
final int startPos = shallowIndexOfPattern( sb, SELECT_PATTERN, 0 );
final int startPos = getSelectColumnsStartPosition( sb );
int endPos = shallowIndexOfPattern( sb, FROM_PATTERN, startPos );
int nextComa = startPos;
@ -201,6 +204,35 @@ public class SQLServer2005LimitHandler extends AbstractLimitHandler {
return selectsMultipleColumns ? "*" : StringHelper.join( ", ", aliases.iterator() );
}
/**
* Get the start position for where the column list begins.
*
* @param sb the string builder sql.
* @return the start position where the column list begins.
*/
private int getSelectColumnsStartPosition(StringBuilder sb) {
final int startPos = getSelectStartPosition( sb );
// adjustment for 'select distinct ' and 'select '.
final String sql = sb.toString().substring( startPos ).toLowerCase();
if ( sql.startsWith( SELECT_DISTINCT_SPACE ) ) {
return ( startPos + SELECT_DISTINCT_SPACE.length() );
}
else if ( sql.startsWith( SELECT_SPACE ) ) {
return ( startPos + SELECT_SPACE.length() );
}
return startPos;
}
/**
* Get the select start position.
*
* @param sb the string builder sql.
* @return the position where {@code select} is found.
*/
private int getSelectStartPosition(StringBuilder sb) {
return shallowIndexOfPattern( sb, SELECT_PATTERN, 0 );
}
/**
* @param expression Select expression.
*
@ -220,11 +252,17 @@ public class SQLServer2005LimitHandler extends AbstractLimitHandler {
* @return Column alias.
*/
private String getAlias(String expression) {
// remove any function arguments, if any exist.
// 'cast(tab1.col1 as varchar(255)) as col1' -> 'cast as col1'
// 'cast(tab1.col1 as varchar(255)) col1 -> 'cast col1'
// 'cast(tab1.col1 as varchar(255))' -> 'cast'
expression = expression.replaceFirst( "(\\((.)*\\))", "" ).trim();
// This will match any text provided with:
// columnName [[as] alias]
final Matcher matcher = ALIAS_PATTERN.matcher( expression );
if ( matcher.find() ) {
// Taking advantage of Java regular expressions greedy behavior while extracting the last AS keyword.
// Note that AS keyword can appear in CAST operator, e.g. 'cast(tab1.col1 as varchar(255)) as col1'.
return matcher.group( 0 ).replaceFirst( "(?i)(.)*\\sas\\s", "" ).trim();
if ( matcher.find() && matcher.groupCount() > 1 ) {
return matcher.group( 1 ) != null ? matcher.group( 2 ) : matcher.group( 3 );
}
return null;
}
@ -292,9 +330,16 @@ public class SQLServer2005LimitHandler extends AbstractLimitHandler {
* based on the search pattern that is not enclosed in parenthesis.
*
* @param pattern String search pattern.
* @param wordBoundardy whether to apply a word boundary restriction.
* @return Compiled {@link Pattern}.
*/
private static Pattern buildShallowIndexPattern(String pattern) {
return Pattern.compile( "(\\b" + pattern + ")(?![^\\(]*\\))", Pattern.CASE_INSENSITIVE );
private static Pattern buildShallowIndexPattern(String pattern, boolean wordBoundardy) {
return Pattern.compile(
"(" +
( wordBoundardy ? "\\b" : "" ) +
pattern +
")(?![^\\(]*\\))",
Pattern.CASE_INSENSITIVE
);
}
}

View File

@ -24,6 +24,7 @@ import static org.junit.Assert.assertEquals;
*
* @author Valotasion Yoryos
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
* @author Chris Cranford
*/
public class SQLServer2005DialectTestCase extends BaseUnitTestCase {
private SQLServer2005Dialect dialect;
@ -114,6 +115,18 @@ public class SQLServer2005DialectTestCase extends BaseUnitTestCase {
);
}
@Test
@TestForIssue(jiraKey = "HHH-10994")
public void testGetLimitStringAliasGenerationWithAliasesNoAs() {
final String aliasedSQLNoAs = "select column1 c1, column c2, column c3, column c4 from table1";
assertEquals(
"WITH query AS (SELECT inner_query.*, ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __hibernate_row_nr__ FROM ( " +
"select column1 c1, column c2, column c3, column c4 from table1 ) inner_query ) " +
"SELECT c1, c2, c3, c4 FROM query WHERE __hibernate_row_nr__ >= ? AND __hibernate_row_nr__ < ?",
dialect.getLimitHandler().processSql( aliasedSQLNoAs, toRowSelection( 3, 5 ) )
);
}
@Test
@TestForIssue(jiraKey = "HHH-7019")
public void testGetLimitStringWithSubselect() {
@ -164,6 +177,32 @@ public class SQLServer2005DialectTestCase extends BaseUnitTestCase {
);
}
@Test
@TestForIssue(jiraKey = "HHH-10994")
public void testGetLimitStringDistinctWithinAggregationWithoutAlias() {
final String distinctInAggregateSQL = "select aggregate_function(distinct p.n) from table849752 p order by f1";
assertEquals(
"WITH query AS (SELECT inner_query.*, ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __hibernate_row_nr__ FROM ( " +
"select TOP(?) aggregate_function(distinct p.n) as page0_ from table849752 p order by f1 ) inner_query ) " +
"SELECT page0_ FROM query WHERE __hibernate_row_nr__ >= ? AND __hibernate_row_nr__ < ?",
dialect.getLimitHandler().processSql( distinctInAggregateSQL, toRowSelection( 2, 5 ) )
);
}
@Test
@TestForIssue(jiraKey = "HHH-10994")
public void testGetLimitStringDistinctWithinAggregationWithAliasNoAs() {
final String distinctInAggregateSQL = "select aggregate_function(distinct p.n) f1 from table849752 p order by f1";
assertEquals(
"WITH query AS (SELECT inner_query.*, ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __hibernate_row_nr__ FROM ( " +
"select TOP(?) aggregate_function(distinct p.n) f1 from table849752 p order by f1 ) inner_query ) " +
"SELECT f1 FROM query WHERE __hibernate_row_nr__ >= ? AND __hibernate_row_nr__ < ?",
dialect.getLimitHandler().processSql( distinctInAggregateSQL, toRowSelection( 2, 5 ) )
);
}
@Test
@TestForIssue(jiraKey = "HHH-7370")
public void testGetLimitStringWithMaxOnly() {
@ -201,6 +240,36 @@ public class SQLServer2005DialectTestCase extends BaseUnitTestCase {
);
}
@Test
@TestForIssue(jiraKey = "HHH-10994")
public void testGetLimitStringWithCastOperatorWithAliasNoAs() {
final String query = "select cast(lc302_doku6_.redniBrojStavke as varchar(255)) f1, lc302_doku6_.dokumentiID f2 " +
"from LC302_Dokumenti lc302_doku6_ order by lc302_doku6_.dokumentiID DESC";
assertEquals(
"WITH query AS (SELECT inner_query.*, ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __hibernate_row_nr__ FROM ( " +
"select TOP(?) cast(lc302_doku6_.redniBrojStavke as varchar(255)) f1, lc302_doku6_.dokumentiID f2 " +
"from LC302_Dokumenti lc302_doku6_ order by lc302_doku6_.dokumentiID DESC ) inner_query ) " +
"SELECT f1, f2 FROM query WHERE __hibernate_row_nr__ >= ? AND __hibernate_row_nr__ < ?",
dialect.getLimitHandler().processSql( query, toRowSelection( 1, 3 ) )
);
}
@Test
@TestForIssue(jiraKey = "HHH-10994")
public void testGetLimitStringWithCastOperatorWithoutAliases() {
final String query = "select cast(lc302_doku6_.redniBrojStavke as varchar(255)), lc302_doku6_.dokumentiID " +
"from LC302_Dokumenti lc302_doku6_ order by lc302_doku6_.dokumentiID DESC";
assertEquals(
"WITH query AS (SELECT inner_query.*, ROW_NUMBER() OVER (ORDER BY CURRENT_TIMESTAMP) as __hibernate_row_nr__ FROM ( " +
"select TOP(?) cast(lc302_doku6_.redniBrojStavke as varchar(255)) as page0_, lc302_doku6_.dokumentiID as page1_ " +
"from LC302_Dokumenti lc302_doku6_ order by lc302_doku6_.dokumentiID DESC ) inner_query ) " +
"SELECT page0_, page1_ FROM query WHERE __hibernate_row_nr__ >= ? AND __hibernate_row_nr__ < ?",
dialect.getLimitHandler().processSql( query, toRowSelection( 1, 3 ) )
);
}
@Test
@TestForIssue(jiraKey = "HHH-8007")
public void testGetLimitStringSelectingMultipleColumnsFromSeveralTables() {