HHH-14033 SQL script parsing problem with multi-line comments
- Better handling of multi-line comments - Restructured some internal classes to consolidate packages - Added "system"-style SchemaToolingLogging
This commit is contained in:
parent
00989d28d8
commit
c1254cc205
|
@ -209,6 +209,9 @@ xjc {
|
|||
}
|
||||
}
|
||||
|
||||
generateGrammarSource {
|
||||
arguments += "-traceParser"
|
||||
}
|
||||
|
||||
//sourceSets.main.sourceGeneratorsTask.dependsOn xjc
|
||||
//sourceSets.main.sourceGeneratorsTask.dependsOn generateGrammarSource
|
||||
|
|
|
@ -0,0 +1,154 @@
|
|||
header
|
||||
{
|
||||
package org.hibernate.tool.schema.ast;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import org.hibernate.hql.internal.ast.ErrorReporter;
|
||||
}
|
||||
/**
|
||||
* Lexer and parser used to extract single statements from import SQL script. Supports instructions/comments and quoted
|
||||
* strings spread over multiple lines. Each statement must end with semicolon.
|
||||
*
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
class GeneratedSqlScriptParser extends Parser;
|
||||
|
||||
options {
|
||||
buildAST = false;
|
||||
k=3;
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Semantic actions
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
{
|
||||
protected void out(String stmt) {
|
||||
// by default, nothing to do
|
||||
}
|
||||
|
||||
protected void out(Token token) {
|
||||
// by default, nothing to do
|
||||
}
|
||||
|
||||
protected void statementStarted() {
|
||||
// by default, nothing to do
|
||||
}
|
||||
|
||||
protected void statementEnded() {
|
||||
// by default, nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Parser rules
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
script
|
||||
: (statement)+ EOF
|
||||
;
|
||||
|
||||
statement
|
||||
: { statementStarted(); } (statementPart)* DELIMITER { statementEnded(); }
|
||||
;
|
||||
|
||||
statementPart
|
||||
: quotedString
|
||||
| nonSkippedChar
|
||||
;
|
||||
|
||||
quotedString
|
||||
: q:QUOTED_TEXT {
|
||||
out( q );
|
||||
}
|
||||
;
|
||||
|
||||
nonSkippedChar
|
||||
: c:CHAR {
|
||||
out( c );
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Lexer rules
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
class SqlScriptLexer extends Lexer;
|
||||
|
||||
options {
|
||||
k = 2;
|
||||
charVocabulary = '\u0000'..'\uFFFE';
|
||||
}
|
||||
|
||||
DELIMITER : ';' ;
|
||||
|
||||
// NOTE : The `ESCqs` part in the match is meant to an escaped quote (two single-quotes) and
|
||||
// add it to the recognized text. The `(ESCqs) => ESCqs` syntax is a syntactic predicate.
|
||||
// We basically give precedence to the two single-quotes as a group as opposed to the first of them
|
||||
// matching the terminal single-quote. Both single-quotes end up in the quoted text
|
||||
QUOTED_TEXT
|
||||
: '`' ( ~('`') )* '`'
|
||||
| '\'' ( (ESCqs) => ESCqs | ~('\'') )* '\''
|
||||
// : '\'' ( ~('\'') )* '\''
|
||||
;
|
||||
|
||||
protected
|
||||
ESCqs : '\'' '\'' ;
|
||||
|
||||
CHAR
|
||||
: ( ' ' | '\t' ) => ( ' ' | '\t' )
|
||||
| ~( ';' | '\n' | '\r' )
|
||||
;
|
||||
|
||||
NEWLINE
|
||||
: ( '\r' | '\n' | '\r''\n' ) {
|
||||
newline();
|
||||
// skip the entire match from the lexer stream
|
||||
$setType( Token.SKIP );
|
||||
}
|
||||
;
|
||||
|
||||
LINE_COMMENT
|
||||
// match `//` or `--` followed by anything other than \n or \r until NEWLINE
|
||||
: ("//" | "--") ( ~('\n'|'\r') )* {
|
||||
// skip the entire match from the lexer stream
|
||||
$setType( Token.SKIP );
|
||||
}
|
||||
;
|
||||
|
||||
/**
|
||||
* Note : this comes from the great Terrance Parr (author of Antlr) -
|
||||
*
|
||||
* https://theantlrguy.atlassian.net/wiki/spaces/ANTLR3/pages/2687360/How+do+I+match+multi-line+comments
|
||||
*/
|
||||
BLOCK_COMMENT
|
||||
: "/*"
|
||||
( /* '\r' '\n' can be matched in one alternative or by matching
|
||||
'\r' in one iteration and '\n' in another. I am trying to
|
||||
handle any flavor of newline that comes in, but the language
|
||||
that allows both "\r\n" and "\r" and "\n" to all be valid
|
||||
newline is ambiguous. Consequently, the resulting grammar
|
||||
must be ambiguous. I'm shutting this warning off.
|
||||
*/
|
||||
options {
|
||||
generateAmbigWarnings=false;
|
||||
}
|
||||
: { LA(2)!='/' }? '*'
|
||||
| '\r' '\n' {newline();}
|
||||
| '\r' {newline();}
|
||||
| '\n' {newline();}
|
||||
| ~('*'|'\n'|'\r')
|
||||
)*
|
||||
"*/"
|
||||
{$setType(Token.SKIP);}
|
||||
;
|
|
@ -1,152 +0,0 @@
|
|||
header
|
||||
{
|
||||
package org.hibernate.hql.internal.antlr;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import org.hibernate.hql.internal.ast.ErrorReporter;
|
||||
}
|
||||
/**
|
||||
* Lexer and parser used to extract single statements from import SQL script. Supports instructions/comments and quoted
|
||||
* strings spread over multiple lines. Each statement must end with semicolon.
|
||||
*
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
class SqlStatementParser extends Parser;
|
||||
|
||||
options {
|
||||
buildAST = false;
|
||||
}
|
||||
|
||||
{
|
||||
private ErrorHandler errorHandler = new ErrorHandler();
|
||||
|
||||
@Override
|
||||
public void reportError(RecognitionException e) {
|
||||
errorHandler.reportError( e );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportError(String s) {
|
||||
errorHandler.reportError( s );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportWarning(String s) {
|
||||
errorHandler.reportWarning( s );
|
||||
}
|
||||
|
||||
public void throwExceptionIfErrorOccurred() {
|
||||
if ( errorHandler.hasErrors() ) {
|
||||
String errorMessage = errorHandler.getErrorMessage();
|
||||
if(errorMessage.contains("expecting STMT_END")) {
|
||||
throw new StatementParserException( "Import script Sql statements must terminate with a ';' char" );
|
||||
}
|
||||
throw new StatementParserException( errorHandler.getErrorMessage() );
|
||||
}
|
||||
}
|
||||
|
||||
/** List of all SQL statements. */
|
||||
private List<String> statementList = new LinkedList<String>();
|
||||
|
||||
/** Currently processing SQL statement. */
|
||||
private StringBuilder current = new StringBuilder();
|
||||
|
||||
protected void out(String stmt) {
|
||||
current.append( stmt );
|
||||
}
|
||||
|
||||
protected void out(Token token) {
|
||||
out( token.getText() );
|
||||
}
|
||||
|
||||
public List<String> getStatementList() {
|
||||
return statementList;
|
||||
}
|
||||
|
||||
protected void statementEnd() {
|
||||
statementList.add( current.toString().trim() );
|
||||
current = new StringBuilder();
|
||||
}
|
||||
|
||||
public class StatementParserException extends RuntimeException {
|
||||
public StatementParserException(String message) {
|
||||
super( message );
|
||||
}
|
||||
}
|
||||
|
||||
private class ErrorHandler implements ErrorReporter {
|
||||
private List<String> errorList = new LinkedList<String>();
|
||||
|
||||
@Override
|
||||
public void reportError(RecognitionException e) {
|
||||
reportError( e.toString() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportError(String s) {
|
||||
errorList.add( s );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportWarning(String s) {
|
||||
}
|
||||
|
||||
public boolean hasErrors() {
|
||||
return !errorList.isEmpty();
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for ( Iterator iterator = errorList.iterator(); iterator.hasNext(); ) {
|
||||
buf.append( (String) iterator.next() );
|
||||
if ( iterator.hasNext() ) {
|
||||
buf.append( "\n" );
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
script
|
||||
: ( statement )*
|
||||
;
|
||||
|
||||
statement
|
||||
: ( s:NOT_STMT_END { out( s ); } | q:QUOTED_STRING { out( q ); } )* STMT_END { statementEnd(); }
|
||||
;
|
||||
|
||||
class SqlStatementLexer extends Lexer;
|
||||
|
||||
options {
|
||||
k = 2;
|
||||
charVocabulary = '\u0000'..'\uFFFE';
|
||||
}
|
||||
|
||||
STMT_END
|
||||
: ';' ( '\t' | ' ' | '\r' | '\n' )*
|
||||
;
|
||||
|
||||
NOT_STMT_END
|
||||
: ~( ';' )
|
||||
;
|
||||
|
||||
QUOTED_STRING
|
||||
: '\'' ( (ESCqs)=> ESCqs | ~'\'' )* '\''
|
||||
;
|
||||
|
||||
protected
|
||||
ESCqs
|
||||
: '\'' '\''
|
||||
;
|
||||
|
||||
LINE_COMMENT
|
||||
: ( "//" | "--" ) ( ~('\n'|'\r') )* { $setType(Token.SKIP); }
|
||||
;
|
||||
|
||||
MULTILINE_COMMENT
|
||||
: "/*" ( options {greedy=false;} : . )* "*/" { $setType(Token.SKIP); }
|
||||
;
|
|
@ -55,6 +55,8 @@ public class GraphParser extends GeneratedGraphParser {
|
|||
this.sessionFactory = sessionFactory;
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Semantic actions
|
||||
|
||||
@Override
|
||||
protected void startAttribute(Token attributeNameToken) {
|
||||
|
@ -164,4 +166,18 @@ public class GraphParser extends GeneratedGraphParser {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// trace logging hooks
|
||||
|
||||
@Override
|
||||
public void traceIn(String s) throws TokenStreamException {
|
||||
// nothing to do - this parser already does a good job with logging trace info
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traceOut(String s) throws TokenStreamException {
|
||||
// nothing to do - this parser already does a good job with logging trace info
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,7 +62,8 @@ public final class HqlParser extends HqlBaseParser {
|
|||
}
|
||||
|
||||
|
||||
// handle trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// trace logging hooks
|
||||
|
||||
private int traceDepth;
|
||||
|
||||
|
@ -90,6 +91,9 @@ public final class HqlParser extends HqlBaseParser {
|
|||
LOG.trace( prefix + ruleName );
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// error handling hooks
|
||||
|
||||
@Override
|
||||
public void reportError(RecognitionException e) {
|
||||
parseErrorHandler.reportError( e ); // Use the delegate.
|
||||
|
@ -110,6 +114,9 @@ public final class HqlParser extends HqlBaseParser {
|
|||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Semantic actions
|
||||
|
||||
/**
|
||||
* Overrides the base behavior to retry keywords as identifiers.
|
||||
*
|
||||
|
|
|
@ -45,6 +45,9 @@ public class OrderByFragmentParser extends GeneratedOrderByFragmentParser {
|
|||
return columnReferences;
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// semantic actions
|
||||
|
||||
@Override
|
||||
protected AST quotedIdentifier(AST ident) {
|
||||
/*
|
||||
|
@ -286,26 +289,37 @@ public class OrderByFragmentParser extends GeneratedOrderByFragmentParser {
|
|||
}
|
||||
|
||||
|
||||
// trace logging ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// trace logging hooks
|
||||
|
||||
private int traceDepth = 0;
|
||||
|
||||
|
||||
@Override
|
||||
public void traceIn(String ruleName) {
|
||||
if ( ! LOG.isTraceEnabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( inputState.guessing > 0 ) {
|
||||
return;
|
||||
}
|
||||
String prefix = StringHelper.repeat( '-', ( traceDepth++ * 2 ) ) + "-> ";
|
||||
|
||||
final String prefix = StringHelper.repeat( '-', ( traceDepth++ * 2 ) ) + "-> ";
|
||||
LOG.trace( prefix + ruleName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traceOut(String ruleName) {
|
||||
if ( ! LOG.isTraceEnabled() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( inputState.guessing > 0 ) {
|
||||
return;
|
||||
}
|
||||
String prefix = "<-" + StringHelper.repeat( '-', ( --traceDepth * 2 ) ) + " ";
|
||||
|
||||
final String prefix = "<-" + StringHelper.repeat( '-', ( --traceDepth * 2 ) ) + " ";
|
||||
LOG.trace( prefix + ruleName );
|
||||
}
|
||||
|
||||
|
|
|
@ -9,28 +9,20 @@ package org.hibernate.tool.hbm2ddl;
|
|||
import java.io.Reader;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.hql.internal.antlr.SqlStatementLexer;
|
||||
import org.hibernate.hql.internal.antlr.SqlStatementParser;
|
||||
import org.hibernate.tool.schema.ast.SqlScriptParser;
|
||||
|
||||
/**
|
||||
* Class responsible for extracting SQL statements from import script. Supports instructions/comments and quoted
|
||||
* strings spread over multiple lines. Each statement must end with semicolon.
|
||||
*
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class MultipleLinesSqlCommandExtractor implements ImportSqlCommandExtractor {
|
||||
@Override
|
||||
public String[] extractCommands(Reader reader) {
|
||||
final SqlStatementLexer lexer = new SqlStatementLexer( reader );
|
||||
final SqlStatementParser parser = new SqlStatementParser( lexer );
|
||||
try {
|
||||
parser.script(); // Parse script.
|
||||
parser.throwExceptionIfErrorOccurred();
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
throw new ImportScriptException( "Error during import script parsing.", e );
|
||||
}
|
||||
List<String> statementList = parser.getStatementList();
|
||||
return statementList.toArray( new String[statementList.size()] );
|
||||
final List<String> commands = SqlScriptParser.extractCommands( reader );
|
||||
|
||||
return commands.toArray( new String[0] );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.tool.schema;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SchemaToolingLogging {
|
||||
public static final String LOGGER_NAME = "org.hibernate.orm.tooling.schema";
|
||||
public static final Logger LOGGER = Logger.getLogger( LOGGER_NAME );
|
||||
|
||||
public static final String AST_LOGGER_NAME = LOGGER_NAME + ".AST";
|
||||
public static final Logger AST_LOGGER = Logger.getLogger( AST_LOGGER_NAME );
|
||||
|
||||
public static final boolean TRACE_ENABLED = LOGGER.isTraceEnabled();
|
||||
public static final boolean DEBUG_ENABLED = LOGGER.isDebugEnabled();
|
||||
|
||||
public static final boolean AST_TRACE_ENABLED = AST_LOGGER.isTraceEnabled();
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.tool.schema.ast;
|
||||
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.hql.internal.ast.util.ASTUtil;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.tool.schema.SchemaToolingLogging;
|
||||
|
||||
import antlr.RecognitionException;
|
||||
import antlr.Token;
|
||||
import antlr.TokenStream;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SqlScriptParser extends GeneratedSqlScriptParser {
|
||||
private static String[] TOKEN_NAMES = ASTUtil.generateTokenNameCache( GeneratedSqlScriptParserTokenTypes .class );
|
||||
|
||||
public static List<String> extractCommands(Reader reader) {
|
||||
final List<String> statementList = new ArrayList<>();
|
||||
|
||||
final SqlScriptLexer lexer = new SqlScriptLexer( reader );
|
||||
final SqlScriptParser parser = new SqlScriptParser( statementList::add, lexer );
|
||||
|
||||
parser.parseScript();
|
||||
|
||||
return statementList;
|
||||
}
|
||||
|
||||
private final List<String> errorList = new LinkedList<>();
|
||||
private final Consumer<String> commandConsumer;
|
||||
|
||||
private StringBuilder currentStatementBuffer;
|
||||
|
||||
public SqlScriptParser(Consumer<String> commandConsumer, TokenStream lexer) {
|
||||
super( lexer );
|
||||
this.commandConsumer = commandConsumer;
|
||||
|
||||
}
|
||||
|
||||
private void parseScript() {
|
||||
try {
|
||||
// trigger the top-level grammar rule
|
||||
script();
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
throw new SqlScriptParserException( "Error during import script parsing.", e );
|
||||
}
|
||||
|
||||
failIfAnyErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Semantic action outputting text to the current statement buffer
|
||||
*/
|
||||
@Override
|
||||
protected void out(String text) {
|
||||
currentStatementBuffer.append( text );
|
||||
}
|
||||
|
||||
/**
|
||||
* Semantic action outputting a token to the current statement buffer
|
||||
*/
|
||||
@Override
|
||||
protected void out(Token token) {
|
||||
currentStatementBuffer.append( token.getText() );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void statementStarted() {
|
||||
if ( currentStatementBuffer != null ) {
|
||||
SchemaToolingLogging.LOGGER.debugf( "`#currentStatementBuffer` was not null at `#statementStart`" );
|
||||
}
|
||||
currentStatementBuffer = new StringBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Semantic action signifying the end of a statement (delimiter recognized)
|
||||
*/
|
||||
@Override
|
||||
protected void statementEnded() {
|
||||
final String statementText = currentStatementBuffer.toString().trim();
|
||||
SchemaToolingLogging.LOGGER.debugf( "Import statement : %s", statementText );
|
||||
commandConsumer.accept( statementText );
|
||||
|
||||
currentStatementBuffer = null;
|
||||
}
|
||||
|
||||
private void failIfAnyErrors() {
|
||||
if ( errorList.isEmpty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new SqlScriptParserException( buildErrorMessage() );
|
||||
}
|
||||
|
||||
public String buildErrorMessage() {
|
||||
final StringBuilder buf = new StringBuilder();
|
||||
for ( int i = 0; i < errorList.size(); i++ ) {
|
||||
buf.append( errorList.get( i ) );
|
||||
if ( i < errorList.size() - 1 ) {
|
||||
buf.append( System.lineSeparator() );
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// error handling hooks
|
||||
|
||||
@Override
|
||||
public void reportError(RecognitionException e) {
|
||||
final String textBase = "RecognitionException(@" + e.getLine() + ":" + e.getColumn() + ")";
|
||||
|
||||
String message = e.toString();
|
||||
if ( message.contains( "expecting DELIMITER" ) ) {
|
||||
message = "Import script Sql statements must terminate with a ';' char";
|
||||
}
|
||||
|
||||
errorList.add( textBase + " : " + message );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportError(String message) {
|
||||
if ( message.contains( "expecting DELIMITER" ) ) {
|
||||
message = "Import script Sql statements must terminate with a ';' char";
|
||||
}
|
||||
|
||||
errorList.add( message );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportWarning(String message) {
|
||||
SchemaToolingLogging.LOGGER.debugf( "SqlScriptParser recognition warning : " + message );
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// trace logging hooks
|
||||
|
||||
private final int depthIndent = 2;
|
||||
private int traceDepth;
|
||||
|
||||
@Override
|
||||
public void traceIn(String ruleName) {
|
||||
if ( ! SchemaToolingLogging.AST_TRACE_ENABLED ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( inputState.guessing > 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String prefix = StringHelper.repeat( '-', ( traceDepth++ * depthIndent ) );
|
||||
SchemaToolingLogging.AST_LOGGER.tracef( "%s-> %s", prefix, ruleName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traceOut(String ruleName) {
|
||||
if ( ! SchemaToolingLogging.AST_TRACE_ENABLED ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( inputState.guessing > 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String prefix = StringHelper.repeat( '-', ( --traceDepth * depthIndent ) );
|
||||
SchemaToolingLogging.AST_LOGGER.tracef( "<-%s %s", prefix, ruleName );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.tool.schema.ast;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SqlScriptParserException extends HibernateException {
|
||||
public SqlScriptParserException(String message) {
|
||||
super( message );
|
||||
}
|
||||
|
||||
public SqlScriptParserException(String message, Throwable cause) {
|
||||
super( message, cause );
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.test.fileimport;
|
||||
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.tool.hbm2ddl.ImportSqlCommandExtractor;
|
||||
import org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
|
||||
/**
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
*/
|
||||
@TestForIssue( jiraKey = "HHH-2403" )
|
||||
@RequiresDialect(value = H2Dialect.class,
|
||||
jiraKey = "HHH-6286",
|
||||
comment = "Only running the tests against H2, because the sql statements in the import file are not generic. " +
|
||||
"This test should actually not test directly against the db")
|
||||
public class CommandExtractorServiceTest extends MultiLineImportFileTest {
|
||||
@Override
|
||||
public void configure(Configuration cfg) {
|
||||
cfg.setProperty( Environment.HBM2DDL_IMPORT_FILES, "/org/hibernate/test/fileimport/multi-line-statements.sql" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareBasicRegistryBuilder(StandardServiceRegistryBuilder serviceRegistryBuilder) {
|
||||
super.prepareBasicRegistryBuilder( serviceRegistryBuilder );
|
||||
serviceRegistryBuilder.addService( ImportSqlCommandExtractor.class, new MultipleLinesSqlCommandExtractor() );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.test.fileimport;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.CoreMatchers.startsWith;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class MultiLineImportExtractorTest {
|
||||
public static final String IMPORT_FILE = "org/hibernate/test/fileimport/multi-line-statements.sql";
|
||||
|
||||
private final MultipleLinesSqlCommandExtractor extractor = new MultipleLinesSqlCommandExtractor();
|
||||
|
||||
@Test
|
||||
public void testExtraction() throws IOException {
|
||||
final ClassLoader classLoader = ClassLoader.getSystemClassLoader();
|
||||
|
||||
try ( final InputStream stream = classLoader.getResourceAsStream( IMPORT_FILE ) ) {
|
||||
assertThat( stream, notNullValue() );
|
||||
try ( final InputStreamReader reader = new InputStreamReader( stream ) ) {
|
||||
final String[] commands = extractor.extractCommands( reader );
|
||||
assertThat( commands, notNullValue() );
|
||||
assertThat( commands.length, is( 6 ) );
|
||||
|
||||
assertThat( commands[0], startsWith( "CREATE TABLE test_data" ) );
|
||||
|
||||
assertThat( commands[1], is( "INSERT INTO test_data VALUES (1, 'sample')" ) );
|
||||
|
||||
assertThat( commands[2], is( "DELETE FROM test_data" ) );
|
||||
|
||||
assertThat( commands[3], startsWith( "INSERT INTO test_data VALUES (2," ) );
|
||||
assertThat( commands[3], containsString( "-- line 2" ) );
|
||||
|
||||
assertThat( commands[4], startsWith( "INSERT INTO test_data VALUES (3" ) );
|
||||
assertThat( commands[4], not( containsString( "third record" ) ) );
|
||||
|
||||
assertThat( commands[5], containsString( "INSERT INTO test_data (id, text)" ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,46 +6,22 @@
|
|||
*/
|
||||
package org.hibernate.test.fileimport;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.hql.internal.antlr.SqlStatementParser;
|
||||
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
|
||||
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
|
||||
import org.hibernate.tool.hbm2ddl.ImportScriptException;
|
||||
import org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor;
|
||||
import org.hibernate.tool.schema.SourceType;
|
||||
import org.hibernate.tool.schema.TargetType;
|
||||
import org.hibernate.tool.schema.internal.ExceptionHandlerLoggedImpl;
|
||||
import org.hibernate.tool.schema.internal.SchemaCreatorImpl;
|
||||
import org.hibernate.tool.schema.spi.ExceptionHandler;
|
||||
import org.hibernate.tool.schema.spi.ExecutionOptions;
|
||||
import org.hibernate.tool.schema.spi.SchemaCreator;
|
||||
import org.hibernate.tool.schema.spi.ScriptSourceInput;
|
||||
import org.hibernate.tool.schema.spi.ScriptTargetOutput;
|
||||
import org.hibernate.tool.schema.spi.SourceDescriptor;
|
||||
import org.hibernate.tool.schema.spi.TargetDescriptor;
|
||||
import org.hibernate.tool.schema.ast.SqlScriptParserException;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.jta.TestingJtaBootstrap;
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
import org.hibernate.test.schemaupdate.CommentGenerationTest;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.endsWith;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
|
@ -56,120 +32,27 @@ import static org.junit.Assert.fail;
|
|||
jiraKey = "HHH-6286",
|
||||
comment = "Only running the tests against H2, because the sql statements in the import file are not generic. " +
|
||||
"This test should actually not test directly against the db")
|
||||
public class StatementsWithoutTerminalCharsImportFileTest extends BaseUnitTestCase implements ExecutionOptions {
|
||||
public class StatementsWithoutTerminalCharsImportFileTest extends BaseUnitTestCase {
|
||||
|
||||
private static final String IMPORT_FILE = "org/hibernate/test/fileimport/statements-without-terminal-chars.sql";
|
||||
|
||||
private StandardServiceRegistry ssr;
|
||||
private static final String EXPECTED_ERROR_MESSAGE = "Import script Sql statements must terminate with a ';' char";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ssr = new StandardServiceRegistryBuilder()
|
||||
.applySetting( Environment.HBM2DDL_AUTO, "none" )
|
||||
.applySetting( Environment.DIALECT, CommentGenerationTest.SupportCommentDialect.class.getName() )
|
||||
.applySetting(
|
||||
Environment.HBM2DDL_IMPORT_FILES,
|
||||
"/org/hibernate/test/fileimport/statements-without-terminal-chars.sql"
|
||||
).applySetting( AvailableSettings.HBM2DDL_HALT_ON_ERROR, "true" )
|
||||
.applySetting(
|
||||
Environment.HBM2DDL_IMPORT_FILES_SQL_EXTRACTOR,
|
||||
MultipleLinesSqlCommandExtractor.class.getName()
|
||||
)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportFile() {
|
||||
try {
|
||||
final SchemaCreator schemaCreator = new SchemaCreatorImpl( ssr );
|
||||
public void testImportFile() throws IOException {
|
||||
final ClassLoader classLoader = ClassLoader.getSystemClassLoader();
|
||||
final MultipleLinesSqlCommandExtractor extractor = new MultipleLinesSqlCommandExtractor();
|
||||
|
||||
schemaCreator.doCreation(
|
||||
buildMappings( ssr ),
|
||||
this,
|
||||
SourceDescriptorImpl.INSTANCE,
|
||||
TargetDescriptorImpl.INSTANCE
|
||||
);
|
||||
try ( final InputStream stream = classLoader.getResourceAsStream( IMPORT_FILE ) ) {
|
||||
assertThat( stream, notNullValue() );
|
||||
try (final InputStreamReader reader = new InputStreamReader( stream )) {
|
||||
extractor.extractCommands( reader );
|
||||
}
|
||||
|
||||
fail( "ImportScriptException expected" );
|
||||
}
|
||||
catch (ImportScriptException e) {
|
||||
final Throwable cause = e.getCause();
|
||||
|
||||
assertThat( cause, instanceOf( SqlStatementParser.StatementParserException.class ) );
|
||||
|
||||
assertThat( cause.getMessage(), is( EXPECTED_ERROR_MESSAGE ) );
|
||||
catch (SqlScriptParserException e) {
|
||||
assertThat( e.getMessage(), endsWith( EXPECTED_ERROR_MESSAGE ) );
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if ( ssr != null ) {
|
||||
StandardServiceRegistryBuilder.destroy( ssr );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map getConfigurationValues() {
|
||||
return ssr.getService( ConfigurationService.class ).getSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldManageNamespaces() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExceptionHandler getExceptionHandler() {
|
||||
return ExceptionHandlerLoggedImpl.INSTANCE;
|
||||
}
|
||||
|
||||
private static class SourceDescriptorImpl implements SourceDescriptor {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
public static final SourceDescriptorImpl INSTANCE = new SourceDescriptorImpl();
|
||||
|
||||
@Override
|
||||
public SourceType getSourceType() {
|
||||
return SourceType.METADATA;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptSourceInput getScriptSourceInput() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TargetDescriptorImpl implements TargetDescriptor {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
public static final TargetDescriptorImpl INSTANCE = new TargetDescriptorImpl();
|
||||
|
||||
@Override
|
||||
public EnumSet<TargetType> getTargetTypes() {
|
||||
return EnumSet.of( TargetType.DATABASE );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptTargetOutput getScriptTargetOutput() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Metadata buildMappings(StandardServiceRegistry registry) {
|
||||
return new MetadataSources( registry )
|
||||
.buildMetadata();
|
||||
}
|
||||
|
||||
protected StandardServiceRegistry buildJtaStandardServiceRegistry() {
|
||||
StandardServiceRegistry registry = TestingJtaBootstrap.prepare().build();
|
||||
assertThat(
|
||||
registry.getService( TransactionCoordinatorBuilder.class ),
|
||||
instanceOf( JtaTransactionCoordinatorBuilderImpl.class )
|
||||
);
|
||||
return registry;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,9 @@ log4j.rootLogger=info, stdout
|
|||
|
||||
log4j.logger.org.hibernate.orm.graph=debug
|
||||
|
||||
#log4j.logger.org.hibernate.orm.tooling.schema=trace
|
||||
## the AST logger gets very verbose at trace
|
||||
#log4j.logger.org.hibernate.orm.tooling.schema.AST=debug
|
||||
|
||||
log4j.logger.org.hibernate.tool.hbm2ddl=trace
|
||||
log4j.logger.org.hibernate.testing.cache=debug
|
||||
|
|
|
@ -30,3 +30,5 @@ INSERT INTO test_data (id, text)
|
|||
, NULL
|
||||
);
|
||||
|
||||
-- comment;
|
||||
-- comment;
|
||||
|
|
Loading…
Reference in New Issue