diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/BasicFormatterImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/BasicFormatterImpl.java index 965e998107..fc155896a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/BasicFormatterImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/BasicFormatterImpl.java @@ -21,23 +21,8 @@ */ public class BasicFormatterImpl implements Formatter { - private static final Set BEGIN_CLAUSES = Set.of( - "left", "right", "inner", "outer", "group", "order" - ); - private static final Set END_CLAUSES = Set.of( - "where", "set", "having", "from", "by", "join", "into", "union" - ); - private static final Set LOGICAL = Set.of( - "and", "or", "when", "else", "end" - ); - private static final Set QUANTIFIERS = Set.of( - "in", "all", "exists", "some", "any" - ); - private static final Set DML = Set.of( - "insert", "update", "delete" - ); - private static final Set MISC = Set.of( - "select", "on" + private static final Set NON_FUNCTION_NAMES = Set.of( + "select", "from", "on", "set", "and", "or", "where", "having", "by" ); private static final String INDENT_STRING = " "; @@ -54,11 +39,12 @@ private static class FormatProcess { boolean afterByOrSetOrFromOrSelect; boolean afterOn; boolean afterBetween; + boolean afterExtract; boolean afterInsert; int inFunction; int parensSinceSelect; - private LinkedList parenCounts = new LinkedList<>(); - private LinkedList afterByOrFromOrSelects = new LinkedList<>(); + private final LinkedList parenCounts = new LinkedList<>(); + private final LinkedList afterByOrFromOrSelects = new LinkedList<>(); int indent = 1; @@ -86,87 +72,135 @@ public String perform() { token = tokens.nextToken(); lcToken = token.toLowerCase(Locale.ROOT); - if ( "'".equals( token ) ) { - String t; - do { - t = tokens.nextToken(); - token += t; - } - // cannot handle single quotes - while ( !"'".equals( t ) && tokens.hasMoreTokens() ); - } - else if ( "\"".equals( token ) ) { - String t; - do { - t = tokens.nextToken(); - token += t; - } - while ( !"\"".equals( t ) && tokens.hasMoreTokens() ); - } - // SQL Server uses "[" and "]" to escape reserved words - // see SQLServerDialect.openQuote and SQLServerDialect.closeQuote - else if ( "[".equals( token ) ) { - String t; - do { - t = tokens.nextToken(); - token += t; - } - while ( !"]".equals( t ) && tokens.hasMoreTokens()); - } + switch (lcToken) { - if ( afterByOrSetOrFromOrSelect && ",".equals( token ) ) { - commaAfterByOrFromOrSelect(); - } - else if ( afterOn && ",".equals( token ) ) { - commaAfterOn(); - } + case "'": + case "\"": + String t; + do { + t = tokens.nextToken(); + token += t; + } + while ( !lcToken.equals( t ) && tokens.hasMoreTokens() ); + lcToken = token; + misc(); + break; + // SQL Server uses "[" and "]" to escape reserved words + // see SQLServerDialect.openQuote and SQLServerDialect.closeQuote + case "[": + String tt; + do { + tt = tokens.nextToken(); + token += tt; + } + while ( !"]".equals( tt ) && tokens.hasMoreTokens() ); + lcToken = token; + misc(); + break; - else if ( "(".equals( token ) ) { - openParen(); - } - else if ( ")".equals( token ) ) { - closeParen(); - } + case ",": + if ( afterByOrSetOrFromOrSelect && inFunction==0 ) { + commaAfterByOrFromOrSelect(); + } + else if ( afterOn && inFunction==0 ) { + commaAfterOn(); + } + else { + misc(); + } + break; - else if ( BEGIN_CLAUSES.contains( lcToken ) ) { - beginNewClause(); - } + case "(": + openParen(); + break; + case ")": + closeParen(); + break; - else if ( END_CLAUSES.contains( lcToken ) ) { - endNewClause(); - } + case "select": + select(); + break; + case "insert": + case "update": + case "delete": + updateOrInsertOrDelete(); + break; - else if ( "select".equals( lcToken ) ) { - select(); - } + case "values": + values(); + break; - else if ( DML.contains( lcToken ) ) { - updateOrInsertOrDelete(); - } + case "on": + on(); + break; - else if ( "values".equals( lcToken ) ) { - values(); - } + case "between": + afterBetween = true; + misc(); + break; + case "trim": + case "extract": + afterExtract = true; + misc(); + break; - else if ( "on".equals( lcToken ) ) { - on(); - } + //TODO: detect when 'left', 'right' are function names + case "left": + case "right": + case "full": + case "inner": + case "outer": + case "cross": + case "group": + case "order": + beginNewClause(); + break; - else if ( afterBetween && lcToken.equals( "and" ) ) { - misc(); - afterBetween = false; - } + case "from": + if ( afterExtract ) { + misc(); + afterExtract = false; + break; + } + //else fall through: + case "where": + case "set": + case "having": + case "by": + case "join": + case "into": + case "union": + case "intersect": + endNewClause(); + break; - else if ( LOGICAL.contains( lcToken ) ) { - logical(); - } + case "case": + beginCase(); + break; + case "end": + endCase(); + break; - else if ( isWhitespace( token ) ) { - white(); - } + case "and": + if ( afterBetween ) { + misc(); + afterBetween = false; + break; + } + //else fall through: + case "or": + case "when": + case "else": + logical(); + break; + default: + if ( isWhitespace( token ) ) { + white(); + } - else { - misc(); + else { + misc(); + } } if ( !isWhitespace( token ) ) { @@ -191,14 +225,16 @@ private void commaAfterByOrFromOrSelect() { } private void logical() { - if ( "end".equals( lcToken ) ) { - indent--; - } newline(); out(); beginLine = false; } + private void endCase() { + indent--; + logical(); + } + private void on() { indent++; afterOn = true; @@ -207,20 +243,20 @@ private void on() { beginLine = false; } + private void beginCase() { + out(); + beginLine = false; + indent++; + } + private void misc() { out(); - if ( "between".equals( lcToken ) ) { - afterBetween = true; - } - if ( afterInsert ) { + if ( afterInsert && inFunction==0 ) { newline(); afterInsert = false; } else { beginLine = false; - if ( "case".equals( lcToken ) ) { - indent++; - } } } @@ -231,14 +267,20 @@ private void white() { } private void updateOrInsertOrDelete() { - out(); - indent++; - beginLine = false; - if ( "update".equals( lcToken ) ) { - newline(); + if ( indent>1 ) { + //probably just the insert SQL function + out(); } - if ( "insert".equals( lcToken ) ) { - afterInsert = true; + else { + out(); + indent++; + beginLine = false; + if ( "update".equals( lcToken ) ) { + newline(); + } + if ( "insert".equals( lcToken ) ) { + afterInsert = true; + } } } @@ -266,7 +308,7 @@ private void endNewClause() { newline(); } out(); - if ( !"union".equals( lcToken ) ) { + if ( !"union".equals( lcToken ) && !"intersect".equals( lcToken ) ) { indent++; } newline(); @@ -291,11 +333,16 @@ private void beginNewClause() { } private void values() { - indent--; - newline(); - out(); - indent++; - newline(); + if ( parensSinceSelect == 0 ) { + indent--; + newline(); + out(); + indent++; + newline(); + } + else { + out(); + } } private void closeParen() { @@ -345,12 +392,7 @@ private static boolean isFunctionName(String tok) { final char begin = tok.charAt( 0 ); final boolean isIdentifier = Character.isJavaIdentifierStart( begin ) || '"' == begin; - return isIdentifier && - !LOGICAL.contains( tok ) && - !END_CLAUSES.contains( tok ) && - !QUANTIFIERS.contains( tok ) && - !DML.contains( tok ) && - !MISC.contains( tok ); + return isIdentifier && !NON_FUNCTION_NAMES.contains( tok ); } private static boolean isWhitespace(String token) { @@ -358,10 +400,8 @@ private static boolean isWhitespace(String token) { } private void newline() { - result.append( System.lineSeparator() ); - for ( int i = 0; i < indent; i++ ) { - result.append( INDENT_STRING ); - } + result.append( System.lineSeparator() ) + .append( INDENT_STRING.repeat(indent) ); beginLine = true; } }