diff --git a/hibernate-core/hibernate-core.gradle b/hibernate-core/hibernate-core.gradle index a819188ff8..e088f74f31 100644 --- a/hibernate-core/hibernate-core.gradle +++ b/hibernate-core/hibernate-core.gradle @@ -209,6 +209,9 @@ xjc { } } +generateGrammarSource { + arguments += "-traceParser" +} //sourceSets.main.sourceGeneratorsTask.dependsOn xjc //sourceSets.main.sourceGeneratorsTask.dependsOn generateGrammarSource diff --git a/hibernate-core/src/main/antlr/sql-script.g b/hibernate-core/src/main/antlr/sql-script.g new file mode 100644 index 0000000000..9aba884a02 --- /dev/null +++ b/hibernate-core/src/main/antlr/sql-script.g @@ -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);} + ; \ No newline at end of file diff --git a/hibernate-core/src/main/antlr/sql-stmt.g b/hibernate-core/src/main/antlr/sql-stmt.g deleted file mode 100644 index 6abf0a9ffe..0000000000 --- a/hibernate-core/src/main/antlr/sql-stmt.g +++ /dev/null @@ -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 statementList = new LinkedList(); - - /** 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 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 errorList = new LinkedList(); - - @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); } - ; \ No newline at end of file diff --git a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java index 26dd66d045..9944874cf9 100644 --- a/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java +++ b/hibernate-core/src/main/java/org/hibernate/graph/internal/parse/GraphParser.java @@ -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 + } } diff --git a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlParser.java b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlParser.java index 7c3fd925f7..a8a6317057 100644 --- a/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlParser.java +++ b/hibernate-core/src/main/java/org/hibernate/hql/internal/ast/HqlParser.java @@ -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. * diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentParser.java b/hibernate-core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentParser.java index 07289b8331..358da261f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentParser.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ordering/antlr/OrderByFragmentParser.java @@ -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 ); } diff --git a/hibernate-core/src/main/java/org/hibernate/tool/hbm2ddl/MultipleLinesSqlCommandExtractor.java b/hibernate-core/src/main/java/org/hibernate/tool/hbm2ddl/MultipleLinesSqlCommandExtractor.java index ebcaf917ef..c7bd5b014d 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/hbm2ddl/MultipleLinesSqlCommandExtractor.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/hbm2ddl/MultipleLinesSqlCommandExtractor.java @@ -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 statementList = parser.getStatementList(); - return statementList.toArray( new String[statementList.size()] ); + final List commands = SqlScriptParser.extractCommands( reader ); + + return commands.toArray( new String[0] ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/SchemaToolingLogging.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/SchemaToolingLogging.java new file mode 100644 index 0000000000..5e9c348dec --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/SchemaToolingLogging.java @@ -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 . + */ +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(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/ast/SqlScriptParser.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/ast/SqlScriptParser.java new file mode 100644 index 0000000000..5aaca4facb --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/ast/SqlScriptParser.java @@ -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 . + */ +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 extractCommands(Reader reader) { + final List statementList = new ArrayList<>(); + + final SqlScriptLexer lexer = new SqlScriptLexer( reader ); + final SqlScriptParser parser = new SqlScriptParser( statementList::add, lexer ); + + parser.parseScript(); + + return statementList; + } + + private final List errorList = new LinkedList<>(); + private final Consumer commandConsumer; + + private StringBuilder currentStatementBuffer; + + public SqlScriptParser(Consumer 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 ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/ast/SqlScriptParserException.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/ast/SqlScriptParserException.java new file mode 100644 index 0000000000..d84e5b6f11 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/ast/SqlScriptParserException.java @@ -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 . + */ +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 ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/fileimport/CommandExtractorServiceTest.java b/hibernate-core/src/test/java/org/hibernate/test/fileimport/CommandExtractorServiceTest.java deleted file mode 100644 index cc3919fc82..0000000000 --- a/hibernate-core/src/test/java/org/hibernate/test/fileimport/CommandExtractorServiceTest.java +++ /dev/null @@ -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 . - */ -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() ); - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/test/fileimport/MultiLineImportExtractorTest.java b/hibernate-core/src/test/java/org/hibernate/test/fileimport/MultiLineImportExtractorTest.java new file mode 100644 index 0000000000..6df672a746 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/fileimport/MultiLineImportExtractorTest.java @@ -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 . + */ +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)" ) ); + } + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/fileimport/StatementsWithoutTerminalCharsImportFileTest.java b/hibernate-core/src/test/java/org/hibernate/test/fileimport/StatementsWithoutTerminalCharsImportFileTest.java index bb98c56d8a..6dc4ce9a1e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/fileimport/StatementsWithoutTerminalCharsImportFileTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/fileimport/StatementsWithoutTerminalCharsImportFileTest.java @@ -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 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; - } - } diff --git a/hibernate-core/src/test/resources/log4j.properties b/hibernate-core/src/test/resources/log4j.properties index a9af54dc93..179eb931df 100644 --- a/hibernate-core/src/test/resources/log4j.properties +++ b/hibernate-core/src/test/resources/log4j.properties @@ -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 diff --git a/hibernate-core/src/test/resources/org/hibernate/test/fileimport/multi-line-statements.sql b/hibernate-core/src/test/resources/org/hibernate/test/fileimport/multi-line-statements.sql index c48294d473..27a0fabd2f 100644 --- a/hibernate-core/src/test/resources/org/hibernate/test/fileimport/multi-line-statements.sql +++ b/hibernate-core/src/test/resources/org/hibernate/test/fileimport/multi-line-statements.sql @@ -30,3 +30,5 @@ INSERT INTO test_data (id, text) , NULL ); +-- comment; +-- comment;