Retain newlines between command parts in sql script parsing as spaces like in Hibernate 5. Also, improve the parsing efficiency by not defining rules and thus creating contexts for every token type

This commit is contained in:
Christian Beikov 2021-05-05 11:55:49 +02:00
parent 181ac6e0ff
commit c79e9effe9
5 changed files with 83 additions and 107 deletions

View File

@ -15,53 +15,38 @@ LINE_COMMENT
;
MULTILINE_COMMENT
: '/*' .*? '*/' -> skip
: '/*'
(
{ getInputStream().LA(2)!='/' }? '*'
| '\r' '\n'
| '\r'
| '\n'
| ~('*'|'\n'|'\r')
)*
'*/' -> skip
;
CHAR
: ~( ';' | '\n' | '\r' | ' ' | '\t')
;
SPACE
: ' '
;
TAB
: '\t'
;
NEWLINE
: ('\r'? '\n' | '\r') -> skip
: ('\r'? '\n' | '\r')
;
STMT_END
: ';' ( '\t' | ' ' | '\r' | '\n' )*
;
NOT_STMT_END
: ~[;]
;
DELIMITER:
';'
;
QUOTED_TEXT
: '`' .*? '`'
;
//WORD
// : ~[;]
// ;
//
//QUOTED_TEXT
// : '\'' ( ESCAPE_SEQUENCE | ~('\\'|'\'') )* '\''
// ;
//
//fragment
//ESCAPE_SEQUENCE
// : '\\' ('b'|'t'|'n'|'f'|'r'|'\\"'|'\''|'\\')
// | UNICODE_ESCAPE
// | OCTAL_ESCAPE
// ;
//
//fragment
//OCTAL_ESCAPE
// : '\\' ('0'..'3') ('0'..'7') ('0'..'7')
// | '\\' ('0'..'7') ('0'..'7')
// | '\\' ('0'..'7')
// ;
//
//fragment
//UNICODE_ESCAPE
// : '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
// ;
//
//fragment
//HEX_DIGIT
// : ('0'..'9'|'a'..'f'|'A'..'F')
// ;
: '`' ( ~('`') )*? '`'
| '\'' ( ('\'''\'') | ~('\'') )*? '\''
;

View File

@ -15,27 +15,18 @@ package org.hibernate.grammars.importsql;
}
script
: commandBlock+ EOF
: (NEWLINE | SPACE | TAB)* ( commandBlock (NEWLINE | SPACE | TAB)* )+ EOF
;
commandBlock
: command STMT_END
: command DELIMITER
;
command
: commandPart*
;
commandPart
: notStmtEnd
| quotedText
;
notStmtEnd
: NOT_STMT_END+
;
quotedText
: QUOTED_TEXT
: ( CHAR | QUOTED_TEXT ) // The first part must be a non-whitespace
(
( CHAR | QUOTED_TEXT | SPACE | TAB ) // Following chars may include spaces
NEWLINE* // And also newlines in betweeen
)*
;

View File

@ -96,7 +96,7 @@ public class MultiLineSqlScriptExtracter implements SqlScriptCommandExtractor {
int charPositionInLine,
String msg,
RecognitionException e) {
if ( msg.contains( "missing STMT_END" ) ) {
if ( msg.contains( "missing ';'" ) ) {
throw new SqlScriptException( "Import script Sql statements must terminate with a ';' char" );
}
super.syntaxError( recognizer, offendingSymbol, line, charPositionInLine, msg, e );

View File

@ -7,14 +7,20 @@
package org.hibernate.tool.schema.internal.script;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.hibernate.dialect.Dialect;
import org.hibernate.grammars.importsql.SqlScriptLexer;
import org.hibernate.grammars.importsql.SqlScriptParser;
import org.hibernate.grammars.importsql.SqlScriptParserBaseVisitor;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;
/**
* @author Steve Ebersole
* @author Christian Beikov
*/
public class SqlScriptVisitor extends SqlScriptParserBaseVisitor<Object> {
private final Dialect dialect;
@ -24,48 +30,42 @@ public class SqlScriptVisitor extends SqlScriptParserBaseVisitor<Object> {
}
@Override
public ArrayList<String> visitScript(SqlScriptParser.ScriptContext ctx) {
final ArrayList<String> commands = new ArrayList<>();
for ( int i = 0; i < ctx.commandBlock().size(); i++ ) {
commands.add( visitCommandBlock( ctx.commandBlock().get( i ) ) );
public List<String> visitScript(SqlScriptParser.ScriptContext ctx) {
final List<ParseTree> children = ctx.children;
if ( children == null ) {
return Collections.emptyList();
}
final ArrayList<String> commands = new ArrayList<>( children.size() );
final StringBuilder commandBuffer = new StringBuilder();
for ( int i = 0; i < children.size(); i++ ) {
final ParseTree parseTree = children.get( i );
if ( parseTree instanceof SqlScriptParser.CommandBlockContext ) {
commandBuffer.setLength( 0 );
final SqlScriptParser.CommandBlockContext blockContext = (SqlScriptParser.CommandBlockContext) parseTree;
final List<ParseTree> terminalNodes = blockContext.command().children;
for ( int j = 0; j < terminalNodes.size(); j++ ) {
final TerminalNode terminalNode = (TerminalNode) terminalNodes.get( j );
switch ( terminalNode.getSymbol().getType() ) {
case SqlScriptLexer.CHAR:
case SqlScriptLexer.SPACE:
case SqlScriptLexer.TAB:
commandBuffer.append( terminalNode.getText() );
break;
case SqlScriptLexer.QUOTED_TEXT:
commandBuffer.append( dialect.quote( terminalNode.getText() ) );
break;
case SqlScriptLexer.NEWLINE:
commandBuffer.append( ' ' );
break;
default:
throw new IllegalArgumentException( "Unsupported token: " + terminalNode );
}
}
commands.add( commandBuffer.toString() );
}
}
return commands;
}
@Override
public String visitCommandBlock(SqlScriptParser.CommandBlockContext ctx) {
return visitCommand( ctx.command() );
}
@Override
public String visitCommand(SqlScriptParser.CommandContext ctx) {
final List<SqlScriptParser.CommandPartContext> commandParts = ctx.commandPart();
final StringBuilder commandBuffer = new StringBuilder();
String separator = "";
for ( int i = 0; i < commandParts.size(); i++ ) {
commandBuffer.append( separator );
final SqlScriptParser.CommandPartContext commandPart = commandParts.get( i );
if ( commandPart.notStmtEnd() != null ) {
commandBuffer.append( commandPart.notStmtEnd().getText() );
}
else if ( commandPart.quotedText() != null ) {
commandBuffer.append( visitQuotedText( commandPart.quotedText() ) );
}
separator = " ";
}
return commandBuffer.toString();
}
@Override
public Object visitQuotedText(SqlScriptParser.QuotedTextContext ctx) {
return dialect.quote( ctx.QUOTED_TEXT().getText() );
}
}

View File

@ -119,6 +119,14 @@ public class SchemaManagementToolCoordinator {
exceptionHandler
);
if ( scriptActionMap != null ) {
scriptActionMap.forEach(
(action, contributors) -> {
performScriptAction( action, metadata, tool, serviceRegistry, executionOptions );
}
);
}
if ( databaseActionMap != null ) {
databaseActionMap.forEach(
(action, contributors) -> {
@ -149,14 +157,6 @@ public class SchemaManagementToolCoordinator {
}
);
}
if ( scriptActionMap != null ) {
scriptActionMap.forEach(
(action, contributors) -> {
performScriptAction( action, metadata, tool, serviceRegistry, executionOptions );
}
);
}
}
public static ExecutionOptions buildExecutionOptions(