HHH-8090: Created testing framework for SQL output and implementation in core

This commit is contained in:
John Verhaeg 2013-05-15 10:35:20 -05:00
parent ca4516a4e9
commit 6f7c7d2e99
62 changed files with 2952 additions and 1312 deletions

View File

@ -27,8 +27,6 @@
import java.io.FileFilter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@ -38,14 +36,13 @@
import javax.xml.parsers.DocumentBuilderFactory;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.sqlparser.Column;
import org.hibernate.testing.sqlparser.Index;
import org.hibernate.testing.sqlparser.Insert;
import org.hibernate.testing.sqlparser.Name;
import org.hibernate.testing.sqlparser.Sequence;
import org.hibernate.testing.sqlparser.SqlParser;
import org.hibernate.testing.sqlparser.Statement;
import org.hibernate.testing.sqlparser.Table;
import org.hibernate.testing.sql.Reference;
import org.hibernate.testing.sql.SqlComparator;
import org.hibernate.testing.sql.SqlObject;
import org.hibernate.testing.sql.SqlParser;
import org.hibernate.testing.sql.SqlVisitor;
import org.hibernate.testing.sql.SqlWalker;
import org.hibernate.testing.sql.Statement;
import org.jboss.logging.Logger;
import org.junit.Assert;
import org.junit.BeforeClass;
@ -54,7 +51,9 @@
/**
* <p>
* The superclass for test classes that test the SQL output of all other test classes within the enclosing project.
* The superclass for test classes that test the SQL output of all other test classes within the enclosing project. SQL output
* will only be tested by subclasses of this class if the system property {@value #TEST_SQL_OUTPUT_PROPERTY} is set to
* {@code true}. If not set, these test subclasses will merely pass successfully without testing the output.
* </p><p>
* By merely creating a subclass of this class (with no content) within a project, all SQL output from all tests within that
* project will be verified against an <a href="#ExpectedResultsFile">expected results file</a> stored within the project's
@ -103,15 +102,12 @@
* <li>{@code DROP}-related statements are ignored.
* <li>Names may be different, but must still be semantically equivalent.
* <li>Column names and references within comma-separated lists in SQL (e.g., within {@code CREATE TABLE}, {@code SELECT}, etc.)
* may appear in any order.
* may appear in any order when order is semantically irrelevant.
* <li>DDL statements may appear in any order, except that {@code ALTER} and {@code CREATE INDEX} statements must appear after
* the corresponding {@code CREATE} statements for their referenced tables.
* <li>SQL statements may be output by either @BeforeClass methods or the associated test.
* <li>Comparisons between test results and expected test results are case-insensitive
* </ul>
* </p><p>
* Execution of subclasses of this class can be globally skipped by setting the system property
* {@value #SKIP_SQL_OUTPUT_TESTS_PROPERTY} to {@code true}
* </p>
*/
public abstract class BaseSqlOutputTest {
@ -129,34 +125,18 @@ public abstract class BaseSqlOutputTest {
private static final String FAILURE_EXPECTED_PATTERN = CUSTOM_RUNNER_PATTERN_PREFIX + FailureExpected.class.getSimpleName()
+ ".*";
private static final String BEFORE_CLASS_NAME = BeforeClass.class.getSimpleName();
private static final Comparator< Object > NAME_COMPARATOR = new Comparator< Object >() {
@Override
public int compare( Object object1, Object object2 ) {
Name name1;
Name name2;
if ( object1 instanceof Column ) {
name1 = ( ( Column ) object1 ).name;
name2 = ( ( Column ) object2 ).name;
} else {
name1 = ( ( Name ) object1 );
name2 = ( ( Name ) object2 );
}
return name1.unquoted().compareTo( name2.unquoted() );
}
};
private static final SqlComparator COMPARATOR = new SqlComparator();
private static final String MISSING_EXPECTED_SQL = "Missing expected SQL";
private static final String UNEXPECTED_SQL = "Unexpected SQL";
public static final String SKIP_SQL_OUTPUT_TESTS_PROPERTY = "skipSqlOutputTests";
public static final String TEST_SQL_OUTPUT_PROPERTY = "testSqlOutput";
private final SqlParser parser = new SqlParser();
private int failures;
@Test
public void testSqlOutput() throws Exception {
System.setProperty( SKIP_SQL_OUTPUT_TESTS_PROPERTY, "true" ); // TODO uncomment
if ( Boolean.getBoolean( SKIP_SQL_OUTPUT_TESTS_PROPERTY ) ) {
if ( !Boolean.getBoolean( TEST_SQL_OUTPUT_PROPERTY ) ) {
return;
}
File outputFolder = new File( "target/test-results" );
@ -192,10 +172,7 @@ private void addStatement( StringBuilder builder, List< Statement > statements,
sql = sql.substring( 1, sql.length() - 1 );
}
try {
Statement statement = parser.parse( sql );
if ( statement != null ) {
statements.add( statement );
}
statements.add( parser.parse( sql ) );
} catch ( Exception error ) {
fail( error.getMessage() + "\n\tlogged at " + timestamp + " in file: " + outputFile + "\n\tparsing SQL: "
+ sql );
@ -205,28 +182,11 @@ private void addStatement( StringBuilder builder, List< Statement > statements,
}
}
private void compareTest(
String qualifiedTest,
List< Statement > statements,
List< Statement > expectedStatements,
Map< String, String > namesByExpectedName ) throws Exception {
private void compareTest( List< Statement > statements, List< Statement > expectedStatements ) throws Exception {
if ( statements.get( 0 ) instanceof FailureExpectedStatement ) {
return;
}
for ( Iterator< Statement > iter = statements.iterator(); iter.hasNext(); ) {
Statement statement = iter.next();
for ( Iterator< Statement > expectedIter = expectedStatements.iterator(); expectedIter.hasNext(); ) {
Statement expectedStatement = expectedIter.next();
if ( equals( statement, expectedStatement, namesByExpectedName ) ) {
iter.remove();
expectedIter.remove();
if ( statement instanceof Table || statement instanceof Sequence || statement instanceof Index ) {
map( statement, expectedStatement, namesByExpectedName );
}
break;
}
}
}
COMPARATOR.compare( statements, expectedStatements );
}
private void compareTestClass(
@ -236,8 +196,7 @@ private void compareTestClass(
Map< String, List< Statement > > expectedStatementsByTest ) throws Exception {
String testClass = outputFile.getName();
testClass = testClass.substring( TEST_OUTPUT_FILE_PREFIX.length(), testClass.length() - TEST_OUTPUT_FILE_SUFFIX.length() );
Map< String, String > namesByExpectedName = new HashMap< String, String >();
namesByExpectedName.put( "?", "?" );
COMPARATOR.clear();
Map< String, List< Statement > > statementsByTestCopy = copy( statementsByTest );
Map< String, List< Statement > > expectedStatementsByTestCopy = copy( expectedStatementsByTest );
@ -250,39 +209,30 @@ private void compareTestClass(
beforeClassExpectedStatements = new ArrayList< Statement >();
}
if ( !beforeClassStatements.isEmpty() && !beforeClassExpectedStatements.isEmpty() ) {
compareTest(
testClass + ".@" + BEFORE_CLASS_NAME,
beforeClassStatements,
beforeClassExpectedStatements,
namesByExpectedName );
compareTest( beforeClassStatements, beforeClassExpectedStatements );
}
for ( Iterator< Entry< String, List< Statement > > > iter = statementsByTestCopy.entrySet().iterator(); iter.hasNext(); ) {
Entry< String, List< Statement > > entry = iter.next();
for ( Entry< String, List< Statement > > entry : statementsByTestCopy.entrySet() ) {
String test = entry.getKey();
List< Statement > statements = entry.getValue();
statements.addAll( beforeClassStatements );
final List< Statement > statements = entry.getValue();
if ( statements.get( 0 ) instanceof FailureExpectedStatement ) {
continue;
}
statements.addAll( 0, beforeClassStatements );
List< Statement > expectedStatements = expectedStatementsByTestCopy.get( test );
if ( expectedStatements == null ) {
expectedStatements = new ArrayList< Statement >();
}
expectedStatements.addAll( beforeClassExpectedStatements );
expectedStatements.addAll( 0, beforeClassExpectedStatements );
String qualifiedTest = testClass + '.' + test;
if ( !expectedStatements.isEmpty() ) {
compareTest( qualifiedTest, statements, expectedStatements, namesByExpectedName );
}
if ( statements.isEmpty() && expectedStatements.isEmpty() ) {
iter.remove();
expectedStatementsByTestCopy.remove( test );
compareTest( statements, expectedStatements );
}
// Remove unmatched statements that are due to references to other unmatched statements
pruneRedundantUnmatchedStatements( statements );
pruneRedundantUnmatchedStatements( expectedStatements );
failIfNotEmpty( testClass, qualifiedTest, statements, UNEXPECTED_SQL );
failIfNotEmpty( testClass, qualifiedTest, expectedStatements, MISSING_EXPECTED_SQL );
}
if ( !beforeClassStatements.isEmpty() || !beforeClassExpectedStatements.isEmpty() ) {
String qualifiedTest = testClass + ".@" + BEFORE_CLASS_NAME;
failIfNotEmpty( testClass, qualifiedTest, beforeClassStatements, UNEXPECTED_SQL );
failIfNotEmpty( testClass, qualifiedTest, beforeClassExpectedStatements, MISSING_EXPECTED_SQL );
return;
}
failIfNotEmpty( testClass, statementsByTestCopy, UNEXPECTED_SQL );
failIfNotEmpty( testClass, expectedStatementsByTestCopy, MISSING_EXPECTED_SQL );
}
private Map< String, List< Statement > > copy( Map< String, List< Statement > > statementsByTest ) {
@ -293,104 +243,15 @@ private Map< String, List< Statement > > copy( Map< String, List< Statement > >
return copy;
}
private boolean equals( Statement statement, Object expectedObject, Map< String, String > namesByExpectedName )
throws Exception {
if ( !( statement instanceof Table || statement instanceof Sequence || statement instanceof Index ) ) { // TODO remove
return true;
}
return equals( statement, expectedObject, namesByExpectedName, statement, null );
}
private boolean equals(
Object object,
Object expectedObject,
Map< String, String > namesByExpectedName,
Statement statement,
Field field ) throws Exception {
if ( object == null ) {
if ( expectedObject != null ) {
return false;
}
return true;
} else if ( expectedObject == null ) {
return false;
}
if ( object.getClass() != expectedObject.getClass() ) {
return false;
}
if ( object instanceof Name ) {
if ( statement instanceof Sequence || statement instanceof Table
|| ( statement instanceof Index && field.getName().equals( "name" ) ) ) {
return true;
}
return ( ( Name ) object ).unquoted().equalsIgnoreCase(
namesByExpectedName.get( ( ( Name ) expectedObject ).unquoted() ) );
}
if ( object.getClass().getPackage() == Statement.class.getPackage() ) {
Field[] fields = object.getClass().getFields();
Field[] expectedFields = expectedObject.getClass().getFields();
for ( int fldNdx = fields.length; --fldNdx >= 0; ) {
Field fld = fields[ fldNdx ];
if ( !equals(
fld.get( object ),
expectedFields[ fldNdx ].get( expectedObject ),
namesByExpectedName,
statement,
fld ) ) {
return false;
}
}
return true;
}
if ( object instanceof List ) {
List< Object > list = ( List< Object > ) object;
if ( list.size() != ( ( List< Object > ) expectedObject ).size() ) {
return false;
}
List< Object > expectedList = ( List< Object > ) expectedObject;
if ( ( statement instanceof Table || statement instanceof Insert ) && field.getName().equals( "columns" ) ) {
List< Object > listCopy = new ArrayList< Object >( list );
Collections.sort( listCopy, NAME_COMPARATOR );
List< Object > expectedListCopy = new ArrayList< Object >( expectedList );
Collections.sort( expectedListCopy, NAME_COMPARATOR );
for ( int listNdx = list.size(); --listNdx >= 0; ) {
if ( !equals( listCopy.get( listNdx ), expectedListCopy.get( listNdx ), namesByExpectedName, statement, field ) ) {
return false;
}
}
expectedList.clear();
expectedList.addAll( list );
} else {
for ( int listNdx = list.size(); --listNdx >= 0; ) {
if ( !equals( list.get( listNdx ), expectedList.get( listNdx ), namesByExpectedName, statement, field ) ) {
return false;
}
}
}
return true;
}
if ( object instanceof String ) {
// String name = Name.unquoted( object.toString() ); // TODO remove
// if ( !name.equals( "currval" ) && !name.equals( "bigint" ) && !name.equals( "integer" )
// && !name.equalsIgnoreCase( "varchar" ) && !name.equals( "<=" ) && !name.equals( "decimal" )
// && !name.equals( ">=" ) && !name.equals( "AND" ) && !name.equalsIgnoreCase( "char" )
// && !name.equals( "timestamp" ) && !name.equals( "boolean" ) && !name.equals( "blob" )
// && !name.equals( "time" ) && !name.equals( "date" ) && !name.equals( "longvarchar" )
// && !name.equals( "binary" ) && !name.equals( "double" ) && !name.equals( "clob" ) && !name.equals( "float" )
// && !name.equals( "smallint" ) && !name.equals( "tinyint" ) && !name.equals( "longvarbinary" ) ) {
// System.out.println();
// }
return object.toString().equalsIgnoreCase( expectedObject.toString() );
}
return object.equals( expectedObject );
}
private void fail( String message ) {
LOG.error( message );
failures++;
}
private void failIfNotEmpty( String testClass, String qualifiedTest, List< Statement > statements, String messagePrefix ) {
if ( statements.isEmpty() ) {
return;
}
StringBuilder builder = new StringBuilder( messagePrefix + " for " + qualifiedTest + ':' );
for ( Statement statement : statements ) {
builder.append( "\n\t" ).append( statement );
@ -398,56 +259,6 @@ private void failIfNotEmpty( String testClass, String qualifiedTest, List< State
fail( builder.toString() );
}
private void failIfNotEmpty( String testClass, Map< String, List< Statement >> statementsByTest, String messagePrefix ) {
for ( Entry< String, List< Statement > > entry : statementsByTest.entrySet() ) {
for ( Iterator< Statement > iter = entry.getValue().iterator(); iter.hasNext(); ) { // TODO remove
Statement statement = iter.next();
if ( !( statement instanceof Table || statement instanceof Sequence || statement instanceof Index ) ) {
iter.remove();
}
}
if ( entry.getValue().isEmpty() ) { // TODO remove
continue;
}
failIfNotEmpty( testClass, testClass + '.' + entry.getKey(), entry.getValue(), messagePrefix );
}
}
private void map( Statement statement, Object expectedObject, Map< String, String > namesByExpectedName ) throws Exception {
map( statement, expectedObject, namesByExpectedName, statement, null );
}
private void map(
Object object,
Object expectedObject,
Map< String, String > namesByExpectedName,
Statement statement,
Field field ) throws Exception {
if ( object == null ) {
return;
}
if ( object instanceof Name ) {
namesByExpectedName.put( ( ( Name ) expectedObject ).unquoted(), ( ( Name ) object ).unquoted() );
return;
}
if ( object.getClass().getPackage() == Statement.class.getPackage() ) {
Field[] fields = object.getClass().getFields();
Field[] expectedFields = expectedObject.getClass().getFields();
for ( int fldNdx = fields.length; --fldNdx >= 0; ) {
Field fld = fields[ fldNdx ];
map( fld.get( object ), expectedFields[ fldNdx ].get( expectedObject ), namesByExpectedName, statement, fld );
}
return;
}
if ( object instanceof List ) {
List< ? > list = ( List< ? > ) object;
List< ? > expectedList = ( List< ? > ) expectedObject;
for ( int listNdx = list.size(); --listNdx >= 0; ) {
map( list.get( listNdx ), expectedList.get( listNdx ), namesByExpectedName, statement, field );
}
}
}
private Map< String, List< Statement > > parse( File outputFile ) throws Exception {
Map< String, List< Statement > > statementsByTest = new HashMap< String, List< Statement > >();
parser.clear();
@ -514,6 +325,32 @@ private Map< String, List< Statement > > parse( File outputFile ) throws Excepti
return statementsByTest;
}
private void pruneRedundantUnmatchedStatements( final List< Statement > statements ) {
for ( final Iterator< Statement > iter = statements.iterator(); iter.hasNext(); ) {
final Statement statement = iter.next();
SqlWalker.INSTANCE.walk( new SqlVisitor() {
@Override
public boolean visit( Object object, SqlObject parent, Field field, int index ) {
if ( !( object instanceof Reference ) ) {
return true;
}
Reference ref = ( Reference ) object;
if ( ref.referent == null ) {
return true;
}
Statement refStatement = ref.statement();
Statement referentStatement = ref.referent.statement();
if ( refStatement != referentStatement && statements.contains( referentStatement ) ) {
iter.remove();
return false;
}
return true;
}
}, statement );
}
}
private void testClassSqlOutput( File outputFile ) throws Exception {
Map< String, List< Statement > > statementsByTest = parse( outputFile );
File expectedOutputFile = new File( "src/test/resources/expected-test-results/" + outputFile.getName() );

View File

@ -23,10 +23,14 @@
*/
package org.hibernate.testing.junit4;
import org.hibernate.testing.sqlparser.Statement;
import org.hibernate.testing.sql.Statement;
/**
*
*/
public class FailureExpectedStatement extends Statement {
FailureExpectedStatement() {
super( null );
}
}

View File

@ -0,0 +1,114 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
import java.util.Collection;
import java.util.Iterator;
/**
*
*/
public abstract class AbstractSqlObject implements SqlObject {
protected static void collectionToString( StringBuilder builder, Collection< ? > collection ) {
if ( collection.isEmpty() ) {
return;
}
Iterator< ? > iter = collection.iterator();
builder.append( iter.next() );
while ( iter.hasNext() ) {
builder.append( ", " ).append( iter.next() );
}
}
protected static void collectionToStringInParentheses( StringBuilder builder, Collection< ? > collection ) {
builder.append( '(' );
if ( !collection.isEmpty() ) {
builder.append( ' ' );
}
collectionToString( builder, collection );
if ( !collection.isEmpty() ) {
builder.append( ' ' );
}
builder.append( ')' );
}
private SqlObject parent;
AbstractSqlObject( SqlObject parent ) {
this.parent = parent;
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.SqlObject#namespaceScope()
*/
@Override
public final LocalScope localScope() {
return this instanceof LocalScope ? ( LocalScope ) this : parent.localScope();
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.SqlObject#localObjectInScope(java.lang.String, boolean)
*/
@Override
public SqlObject localObjectInScope( String name, boolean ignoreColumns ) {
LocalScope scope = localScope();
SqlObject obj = scope.localObject( name, ignoreColumns );
return obj == null && scope.parent() != null ? scope.parent().localObjectInScope( name, ignoreColumns ) : obj;
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.SqlObject#parent()
*/
@Override
public final SqlObject parent() {
return parent;
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.SqlObject#setParent(org.hibernate.testing.sql.SqlObject)
*/
@Override
public final void setParent( SqlObject parent ) {
this.parent = parent;
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.SqlObject#statement()
*/
@Override
public final Statement statement() {
return this instanceof Statement ? ( Statement ) this : parent.statement();
}
}

View File

@ -21,23 +21,17 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
/**
*
*/
public class Alias extends Statement {
public class Alias extends Name {
String name;
String sourceCode;
Reference reference;
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "CREATE ALIAS " + name + " AS " + sourceCode;
Alias( SqlObject parent, String text ) {
super( parent, text );
}
}

View File

@ -0,0 +1,21 @@
package org.hibernate.testing.sql;
public class Aliasable extends AbstractSqlObject {
public SqlObject expression;
public Alias alias;
Aliasable( SqlObject parent ) {
super( parent );
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return alias == null ? ( expression == null ? "" : expression.toString() ) : expression + " AS " + alias;
}
}

View File

@ -21,14 +21,15 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
/**
*
*/
public class Case extends Expression {
public class AlterTable extends DdlStatement {
public Object expression;
public Reference table;
public Constraint constraint;
/**
* {@inheritDoc}
@ -37,14 +38,8 @@ public class Case extends Expression {
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( "CASE" );
if ( expression != null ) {
builder.append( ' ' ).append( expression );
}
for ( Object when : operands ) {
builder.append( ' ' ).append( when );
}
builder.append( " END" );
StringBuilder builder = new StringBuilder( "ALTER TABLE " );
builder.append( table ).append( " ADD " ).append( constraint );
return builder.toString();
}
}

View File

@ -0,0 +1,56 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
/**
*
*/
public class Between extends Operation {
Between( SqlObject parent, String operator, int precedence ) {
super( parent, operator, precedence );
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if ( operands.isEmpty() ) {
builder.append( operator );
} else {
builder.append( operands.get( 0 ) ).append( ' ' ).append( operator );
if ( operands.size() > 1 ) {
builder.append( ' ' ).append( operands.get( 1 ) );
}
if ( operands.size() > 2 ) {
builder.append( " AND " ).append( operands.get( 2 ) );
}
}
return builder.toString();
}
}

View File

@ -0,0 +1,53 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
/**
*
*/
public class BinaryOperation extends Operation {
BinaryOperation( SqlObject parent, String operator, int precedence ) {
super( parent, operator, precedence );
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if ( operands.isEmpty() ) {
builder.append( operator );
} else {
builder.append( operands.get( 0 ) ).append( ' ' ).append( operator );
if ( operands.size() > 1 ) {
builder.append( ' ' ).append( operands.get( 1 ) );
}
}
return builder.toString();
}
}

View File

@ -1,6 +1,6 @@
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
public class Call extends Statement {
public class Call extends DdlStatement {
public Function function;

View File

@ -0,0 +1,66 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
import java.util.List;
/**
*
*/
public class Case extends UnaryOperation {
public SqlObject expression;
Case( SqlObject parent, String operator, int precedence ) {
super( parent, operator, precedence );
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.UnaryOperation#constructOperandList()
*/
@Override
protected List< SqlObject > constructOperandList() {
return new OptionallyOrderedSet< SqlObject >();
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( operator );
if ( expression != null ) {
builder.append( ' ' ).append( expression );
}
for ( SqlObject operand : operands ) {
builder.append( ' ' ).append( operand );
}
builder.append( " END" );
return builder.toString();
}
}

View File

@ -21,13 +21,17 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
/**
*
*/
public class Cast extends Function {
Cast( SqlObject parent, String name ) {
super( parent, name );
}
/**
* {@inheritDoc}
*
@ -35,13 +39,14 @@ public class Cast extends Function {
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( "CAST( " );
if ( !operands.isEmpty() ) {
builder.append( operands.get( 0 ) );
StringBuilder builder = new StringBuilder( name );
builder.append( "( " );
if ( !parameters.isEmpty() ) {
builder.append( parameters.get( 0 ) );
}
builder.append( " AS " );
if ( operands.size() > 1 ) {
builder.append( operands.get( 1 ) );
if ( parameters.size() > 1 ) {
builder.append( parameters.get( 1 ) );
}
builder.append( " )" );
return builder.toString();

View File

@ -21,22 +21,14 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
/**
*
*/
public class Check extends Function {
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sqlparser.Function#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( "CHECK" );
Statement.listToStringInParentheses( builder, operands );
return builder.toString();
Check( SqlObject parent, String name ) {
super( parent, name );
}
}

View File

@ -1,15 +1,39 @@
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
public class Column {
public class Column extends AbstractSqlObject implements NamedObject {
public Name name;
public String type;
public int length;
public int decimals;
public boolean notNull;
public Object check;
public Check check;
public boolean generatedByDefaultAsIdentity;
Column( SqlObject parent ) {
super( parent );
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.NamedObject#name()
*/
@Override
public Name name() {
return name;
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.NamedObject#setName(org.hibernate.testing.sql.Name)
*/
@Override
public void setName( Name name ) {
this.name = name;
}
/**
* {@inheritDoc}
*
@ -17,7 +41,10 @@ public class Column {
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( name.toString() );
StringBuilder builder = new StringBuilder();
if ( name != null ) {
builder.append( name );
}
builder.append( ' ' ).append( type );
if ( length > 0 ) {
builder.append( "( " ).append( length );
@ -33,7 +60,7 @@ public String toString() {
builder.append( " NOT NULL" );
}
if ( check != null ) {
builder.append( " CHECK " ).append( check );
builder.append( ' ' ).append( check );
}
return builder.toString();
}

View File

@ -0,0 +1,67 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
import java.util.Iterator;
import java.util.List;
/**
*
*/
public class CommutativeBinaryOperation extends BinaryOperation {
CommutativeBinaryOperation( SqlObject parent, String operator, int precedence ) {
super( parent, operator, precedence );
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.UnaryOperation#constructOperandList()
*/
@Override
protected List< SqlObject > constructOperandList() {
return new OptionallyOrderedSet< SqlObject >();
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if ( operands.isEmpty() ) {
builder.append( operator );
} else {
Iterator< SqlObject > iter = operands.iterator();
builder.append( iter.next() );
while ( iter.hasNext() ) {
builder.append( ' ' ).append( operator ).append( ' ' ).append( iter.next() );
}
}
return builder.toString();
}
}

View File

@ -0,0 +1,78 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
import java.util.List;
/**
*
*/
public abstract class Constraint extends AbstractSqlObject implements NamedObject {
public Name name;
public abstract List< Reference > columns();
Constraint( SqlObject parent ) {
super( parent );
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.NamedObject#name()
*/
@Override
public Name name() {
return name;
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.NamedObject#setName(org.hibernate.testing.sql.Name)
*/
@Override
public void setName( Name name ) {
this.name = name;
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if ( name != null ) {
builder.append( "CONSTRAINT " ).append( name ).append( ' ' );
}
builder.append( type() ).append( ' ' );
collectionToStringInParentheses( builder, columns() );
return builder.toString();
}
public abstract String type();
}

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
/**
*
@ -30,6 +30,10 @@ public class Count extends Function {
public boolean distinct;
Count( SqlObject parent, String name ) {
super( parent, name );
}
/**
* {@inheritDoc}
*
@ -37,12 +41,13 @@ public class Count extends Function {
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( "COUNT( " );
StringBuilder builder = new StringBuilder( name );
builder.append( "( " );
if ( distinct ) {
builder.append( "DISTINCT " );
}
if ( !operands.isEmpty() ) {
builder.append( operands.get( 0 ) );
if ( !parameters.isEmpty() ) {
builder.append( parameters.get( 0 ) );
}
builder.append( " )" );
return builder.toString();

View File

@ -0,0 +1,63 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
/**
*
*/
public class CreateAlias extends DdlStatement implements NamedObject {
public Name name;
public String sourceCode;
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.NamedObject#name()
*/
@Override
public Name name() {
return name;
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.NamedObject#setName(org.hibernate.testing.sql.Name)
*/
@Override
public void setName( Name name ) {
this.name = name;
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "CREATE ALIAS " + name + " AS " + sourceCode;
}
}

View File

@ -0,0 +1,70 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
import java.util.ArrayList;
import java.util.List;
/**
*
*/
public class CreateIndex extends DdlStatement implements NamedObject {
public Name name;
public Reference table;
public List< Reference > columns = new ArrayList< Reference >();
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.NamedObject#name()
*/
@Override
public Name name() {
return name;
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.NamedObject#setName(org.hibernate.testing.sql.Name)
*/
@Override
public void setName( Name name ) {
this.name = name;
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( "CREATE INDEX " );
builder.append( name ).append( " ON " ).append( table ).append( ' ' );
collectionToStringInParentheses( builder, columns );
return builder.toString();
}
}

View File

@ -1,11 +1,31 @@
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
public class Sequence extends Statement {
public class CreateSequence extends DdlStatement implements NamedObject {
public Name name;
public Integer start;
public int increment = 1;
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.NamedObject#name()
*/
@Override
public Name name() {
return name;
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.NamedObject#setName(org.hibernate.testing.sql.Name)
*/
@Override
public void setName( Name name ) {
this.name = name;
}
/**
* {@inheritDoc}
*

View File

@ -1,20 +1,39 @@
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Table extends Statement {
public class CreateTable extends DdlStatement implements NamedObject {
public Name name;
public boolean cached;
public String temporaryType;
public boolean temporary;
public boolean ifNotExists;
public Name name;
public List< Column > columns = new ArrayList< Column >();
public List< Object > constraints = new ArrayList< Object >();
public List< Column > columns = new OptionallyOrderedSet< Column >();
public List< SqlObject > constraints = new OptionallyOrderedSet< SqlObject >();
public String onCommit;
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.NamedObject#name()
*/
@Override
public Name name() {
return name;
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.NamedObject#setName(org.hibernate.testing.sql.Name)
*/
@Override
public void setName( Name name ) {
this.name = name;
}
/**
* {@inheritDoc}
*
@ -36,7 +55,7 @@ public String toString() {
builder.append( "IF NOT EXISTS " );
}
builder.append( "TABLE " ).append( name ).append( " ( " );
listToString( builder, columns );
collectionToString( builder, columns );
for ( Iterator< ? > iter = constraints.iterator(); iter.hasNext(); ) {
builder.append( ", " ).append( iter.next() );
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
/**
*
*/
public abstract class DdlStatement extends Statement {
DdlStatement() {
super( null );
}
}

View File

@ -1,9 +1,12 @@
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
public class Delete extends Statement {
public class Delete extends DmlStatement {
public Name table;
public Object where;
public SqlObject where;
Delete() {
super( null );
}
/**
* {@inheritDoc}
@ -13,7 +16,9 @@ public class Delete extends Statement {
@Override
public String toString() {
StringBuilder builder = new StringBuilder( "DELETE FROM " );
builder.append( table );
if ( !tables.isEmpty() ) {
builder.append( tables.get( 0 ) );
}
if ( where != null ) {
builder.append( " WHERE " ).append( where );
}

View File

@ -0,0 +1,38 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
import java.util.List;
/**
*
*/
public abstract class DmlStatement extends Statement {
List< Reference > tables = new OptionallyOrderedSet< Reference >();
DmlStatement( SqlObject parent ) {
super( parent );
}
}

View File

@ -21,13 +21,17 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
/**
*
*/
public class Extract extends Function {
Extract( SqlObject parent, String name ) {
super( parent, name );
}
/**
* {@inheritDoc}
*
@ -35,13 +39,14 @@ public class Extract extends Function {
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( "EXTRACT( " );
if ( !operands.isEmpty() ) {
builder.append( operands.get( 0 ) );
StringBuilder builder = new StringBuilder( name );
builder.append( "( " );
if ( !parameters.isEmpty() ) {
builder.append( parameters.get( 0 ) );
}
builder.append( " FROM " );
if ( operands.size() > 1 ) {
builder.append( operands.get( 1 ) );
if ( parameters.size() > 1 ) {
builder.append( parameters.get( 1 ) );
}
builder.append( " )" );
return builder.toString();

View File

@ -1,17 +1,24 @@
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
import java.util.List;
public class ForeignKey extends Constraint {
public Name references;
public List< Reference > columns = new OptionallyOrderedSet< Reference >();
public Reference references;
ForeignKey( SqlObject parent ) {
super( parent );
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sqlparser.Constraint#type()
* @see org.hibernate.testing.sqlparser.Constraint#columns()
*/
@Override
public String type() {
return "FOREIGN KEY";
public List< Reference > columns() {
return columns;
}
/**
@ -23,4 +30,14 @@ public String type() {
public String toString() {
return super.toString() + " REFERENCES " + references;
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sqlparser.Constraint#type()
*/
@Override
public String type() {
return "FOREIGN KEY";
}
}

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
import java.util.ArrayList;
import java.util.List;
@ -29,11 +29,15 @@
/**
*
*/
public class From {
public class From extends AbstractSqlObject {
public Aliasable aliasable;
public List< Join > joins = new ArrayList< Join >();
From( SqlObject parent ) {
super( parent );
}
/**
* {@inheritDoc}
*
@ -41,8 +45,10 @@ public class From {
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( "SELECT " );
builder.append( aliasable );
StringBuilder builder = new StringBuilder();
if ( aliasable != null ) {
builder.append( aliasable );
}
for ( Join join : joins ) {
builder.append( ' ' ).append( join );
}

View File

@ -0,0 +1,26 @@
package org.hibernate.testing.sql;
import java.util.List;
public class Function extends AbstractSqlObject {
public String name;
public List< SqlObject > parameters = new OptionallyOrderedSet< SqlObject >();
Function( SqlObject parent, String name ) {
super( parent );
this.name = name;
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( name );
collectionToStringInParentheses( builder, parameters );
return builder.toString();
}
}

View File

@ -1,15 +1,18 @@
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
import java.util.ArrayList;
import java.util.List;
public class Insert extends Statement {
public class Insert extends DmlStatement {
public Name table;
public List< Name > columns = new ArrayList< Name >();
public List< Object > values = new ArrayList< Object >();
public List< Reference > columns = new OptionallyOrderedSet< Reference >();
public List< SqlObject > values = new ArrayList< SqlObject >();
public Select select;
public Object where;
public SqlObject where;
Insert() {
super( null );
}
/**
* {@inheritDoc}
@ -19,14 +22,16 @@ public class Insert extends Statement {
@Override
public String toString() {
StringBuilder builder = new StringBuilder( "INSERT INTO " );
builder.append( table );
if ( !tables.isEmpty() ) {
builder.append( tables.get( 0 ) );
}
if ( !columns.isEmpty() ) {
builder.append( ' ' );
listToStringInParentheses( builder, columns );
collectionToStringInParentheses( builder, columns );
}
if ( !values.isEmpty() ) {
builder.append( " VALUES " );
listToStringInParentheses( builder, values );
collectionToStringInParentheses( builder, values );
}
if ( select != null ) {
builder.append( " ( " ).append( select ).append( " )" );

View File

@ -1,11 +1,15 @@
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
public class Join {
public class Join extends AbstractSqlObject {
public boolean left;
public String type;
public Aliasable table;
public Object on;
public Aliasable aliasable;
public SqlObject on;
Join( SqlObject parent ) {
super( parent );
}
/**
* {@inheritDoc}
@ -18,7 +22,7 @@ public String toString() {
if ( left ) {
builder.append( "LEFT " );
}
builder.append( type == null ? "" : type ).append( " JOIN " ).append( table ).append( " ON " ).append( on );
builder.append( type == null ? "" : type ).append( " JOIN " ).append( aliasable ).append( " ON " ).append( on );
return builder.toString();
}
}

View File

@ -21,12 +21,12 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
/**
*
*/
public class Name {
public class Literal extends AbstractSqlObject {
public static String unquoted( String text ) {
StringBuilder builder = new StringBuilder();
@ -53,7 +53,8 @@ public static String unquoted( String text ) {
public String text;
Name( String text ) {
Literal( SqlObject parent, String text ) {
super( parent );
this.text = text;
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
/**
*
*/
public interface LocalScope extends SqlObject {
SqlObject localObject( String unquotedName, boolean ignoreColumns );
void mapLocalObjectByName( String unquotedName, SqlObject object );
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
/**
*
*/
public class Name extends Literal {
Name( SqlObject parent, String text ) {
super( parent, text );
}
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
/**
*
*/
public interface NamedObject extends SqlObject {
Name name();
void setName( Name name );
}

View File

@ -21,13 +21,17 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
/**
*
*/
public class NextValueFor extends Function {
NextValueFor( SqlObject parent ) {
super( parent, "NEXT VALUE FOR" );
}
/**
* {@inheritDoc}
*
@ -35,6 +39,6 @@ public class NextValueFor extends Function {
*/
@Override
public String toString() {
return "NEXT VALUE FOR " + ( operands.isEmpty() ? "" : operands.get( 0 ) );
return name + ' ' + ( parameters.isEmpty() ? "" : parameters.get( 0 ) );
}
}

View File

@ -21,14 +21,16 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
/**
*
*/
public class OdbcFunction extends Expression {
public class OdbcFunction extends Function {
Function function;
OdbcFunction( SqlObject parent, String name ) {
super( parent, name );
}
/**
* {@inheritDoc}
@ -37,6 +39,6 @@ public class OdbcFunction extends Expression {
*/
@Override
public String toString() {
return "{ fn " + function + " }";
return "{ fn " + super.toString() + " }";
}
}

View File

@ -21,33 +21,36 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.List;
public abstract class Statement {
/**
*
*/
public abstract class Operation extends AbstractSqlObject {
protected static void listToString( StringBuilder builder, List< ? > list ) {
if ( list.isEmpty() ) {
return;
}
Iterator< ? > iter = list.iterator();
builder.append( iter.next() );
while ( iter.hasNext() ) {
builder.append( ", " ).append( iter.next() );
}
public String operator;
public int precedence;
public List< SqlObject > operands;
Operation( SqlObject parent, String operator, int precedence ) {
super( parent );
this.operator = operator;
this.precedence = precedence;
operands = constructOperandList();
}
protected static void listToStringInParentheses( StringBuilder builder, List< ? > list ) {
builder.append( '(' );
if ( !list.isEmpty() ) {
builder.append( ' ' );
}
listToString( builder, list );
if ( !list.isEmpty() ) {
builder.append( ' ' );
}
builder.append( ')' );
protected List< SqlObject > constructOperandList() {
return new ArrayList< SqlObject >();
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public abstract String toString();
}

View File

@ -0,0 +1,32 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
import java.util.ArrayList;
/**
*
*/
public class OptionallyOrderedSet< E > extends ArrayList< E > {
}

View File

@ -21,16 +21,20 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
/**
*
*/
public class OrderBy {
public class OrderBy extends AbstractSqlObject {
String column;
Reference column;
boolean descending;
OrderBy( SqlObject parent ) {
super( parent );
}
/**
* {@inheritDoc}
*
@ -38,7 +42,7 @@ public class OrderBy {
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( column );
StringBuilder builder = new StringBuilder( column.toString() );
if ( descending ) {
builder.append( " DESC" );
}

View File

@ -0,0 +1,44 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
/**
*
*/
public class Parentheses extends UnaryOperation {
Parentheses( SqlObject parent, String operator, int precedence ) {
super( parent, operator, precedence );
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "( " + ( operands.isEmpty() ? "" : operands.get( 0 ) ) + " )";
}
}

View File

@ -0,0 +1,67 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
import java.util.Iterator;
import java.util.List;
/**
*
*/
public class ParentheticalOptionallyOrderedSet extends Operation {
ParentheticalOptionallyOrderedSet( SqlObject parent, Operation parentheses ) {
super( parent, parentheses.operator, parentheses.precedence );
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.UnaryOperation#constructOperandList()
*/
@Override
protected List< SqlObject > constructOperandList() {
return new OptionallyOrderedSet< SqlObject >();
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( operator );
builder.append( ' ' );
if ( !operands.isEmpty() ) {
Iterator< SqlObject > iter = operands.iterator();
builder.append( iter.next() );
while ( iter.hasNext() ) {
builder.append( ", " ).append( iter.next() );
}
}
builder.append( " )" );
return builder.toString();
}
}

View File

@ -21,12 +21,16 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
/**
*
*/
public class PrimaryKey extends Constraint {
public class PrimaryKey extends UniqueConstraint {
PrimaryKey( SqlObject parent ) {
super( parent );
}
/**
* {@inheritDoc}

View File

@ -0,0 +1,36 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
/**
*
*/
public class Reference extends Literal {
public SqlObject referent;
Reference( SqlObject parent, String text ) {
super( parent, text );
}
}

View File

@ -1,23 +1,27 @@
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
import java.util.ArrayList;
import java.util.List;
public class Select extends Statement {
public class Select extends DmlStatement {
public boolean distinct;
public List< Object > columns = new ArrayList< Object >();
public List< From > froms = new ArrayList< From >();
public Object where;
public String groupBy;
public Object having;
public List< SqlObject > columns = new OptionallyOrderedSet< SqlObject >();
public List< From > froms = new OptionallyOrderedSet< From >();
public SqlObject where;
public Reference groupBy;
public SqlObject having;
public List< OrderBy > orderBy = new ArrayList< OrderBy >();
public Object limit;
public Object offset;
public SqlObject limit;
public SqlObject offset;
public boolean forUpdate;
public Select union;
public boolean unionAll;
Select( SqlObject parent ) {
super( parent );
}
/**
* {@inheritDoc}
*
@ -29,7 +33,7 @@ public String toString() {
if ( distinct ) {
builder.append( "DISTINCT " );
}
listToString( builder, columns );
collectionToString( builder, columns );
if ( !froms.isEmpty() ) {
builder.append( " FROM " );
for ( From from : froms ) {
@ -47,7 +51,7 @@ public String toString() {
}
if ( !orderBy.isEmpty() ) {
builder.append( " ORDER BY " );
listToString( builder, orderBy );
collectionToString( builder, orderBy );
}
if ( limit != null ) {
builder.append( " LIMIT " ).append( limit );

View File

@ -0,0 +1,352 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Comparator;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
*
*/
// TODO Provide mechanism to allow for expected failures (like DML against tables that don't exist)
public class SqlComparator {
static final Comparator< Object > COMPARATOR = new Comparator< Object >() {
@Override
public int compare( Object object1, Object object2 ) {
return object1.toString().compareToIgnoreCase( object2.toString() );
}
};
Map< String, String > namesByExpectedName = new HashMap< String, String >();
public void clear() {
namesByExpectedName.clear();
}
public void compare( List< Statement > statements, List< Statement > expectedStatements ) {
for ( Iterator< Statement > iter = statements.iterator(); iter.hasNext(); ) {
Statement statement = iter.next();
boolean matched = false;
if ( statement instanceof NamedObject ) {
for ( Iterator< Statement > expectedIter = expectedStatements.iterator(); expectedIter.hasNext(); ) {
Statement expectedStatement = expectedIter.next();
if ( statement.getClass() == expectedStatement.getClass()
&& ( ( NamedObject ) statement ).name().unquoted().equalsIgnoreCase(
( ( NamedObject ) expectedStatement ).name().unquoted() ) ) {
matched = compare( statement, expectedStatement, iter, expectedIter );
if ( matched ) {
break;
}
}
}
}
if ( !matched ) {
for ( Iterator< Statement > expectedIter = expectedStatements.iterator(); expectedIter.hasNext(); ) {
if ( compare( statement, expectedIter.next(), iter, expectedIter ) ) {
break;
}
}
}
}
}
private boolean compare(
Statement statement,
Statement expectedStatement,
Iterator< Statement > iterator,
Iterator< Statement > expectedIterator ) {
CompareVisitor compareVisitor = new CompareVisitor( expectedStatement );
SqlWalker.INSTANCE.walk( compareVisitor, statement );
if ( compareVisitor.comparison == 0 ) {
SqlWalker.INSTANCE.walk( new MapVisitor( expectedStatement ), statement );
iterator.remove();
expectedIterator.remove();
return true;
}
return false;
}
private class CompareVisitor extends Visitor {
int comparison;
CompareVisitor( Object expectedObject ) {
super( expectedObject );
}
boolean compareReferents( Reference reference, Reference expectedReference ) {
if ( reference.referent instanceof Alias ) {
if ( ( ( Alias ) reference.referent ).reference == null ) {
if ( ( ( Alias ) expectedReference.referent ).reference == null ) {
return true;
}
comparison = -1;
return false;
}
comparison = 1;
return false;
}
String text = ( ( NamedObject ) reference.referent ).name().unquoted();
String expectedText = ( ( NamedObject ) expectedReference.referent ).name().unquoted();
if ( reference.referent.getClass() == Column.class ) {
text = ( ( CreateTable ) reference.referent.parent() ).name().unquoted() + '.' + text;
expectedText = ( ( CreateTable ) expectedReference.referent.parent() ).name().unquoted() + '.' + expectedText;
}
String matchingText = namesByExpectedName.get( expectedText );
if ( matchingText == null ) {
// Check if both are self-references
if ( ( reference.referent == reference.statement() && expectedReference.referent == expectedReference.statement() )
|| ( reference.referent instanceof Column && expectedReference.referent instanceof Column ) ) {
return true;
}
comparison = 1;
return false;
}
comparison = text.compareToIgnoreCase( matchingText );
return comparison == 0;
}
private boolean compareTypes( Object object, Object expectedObject ) {
Class< ? > objClass = object.getClass();
Class< ? > expectedObjClass = expectedObject.getClass();
if ( objClass != expectedObjClass ) {
comparison = objClass.getSimpleName().compareTo( expectedObjClass.getSimpleName() );
return false;
}
return true;
}
private Boolean compareWithRespectToNull( Object object, Object expectedObject ) {
if ( object == null ) {
if ( expectedObject == null ) {
return true;
}
comparison = -1;
return false;
}
if ( expectedObject == null ) {
comparison = 1;
return false;
}
return null;
}
protected boolean visit( String text, String expectedText ) {
return true;
}
@Override
public boolean visit( Object object, SqlObject parent, Field field, int index ) {
Object expectedObj = expectedObject( field, index );
Boolean result = compareWithRespectToNull( object, expectedObj );
if ( result != null ) {
return result;
}
if ( !compareTypes( object, expectedObj ) ) {
return false;
}
if ( object instanceof List ) {
List< Object > list = ( List< Object > ) object;
List< Object > expectedList = ( List< Object > ) expectedObj;
comparison = list.size() - expectedList.size();
if ( comparison != 0 ) {
return false;
}
if ( list instanceof OptionallyOrderedSet ) {
Collections.sort( list, COMPARATOR );
Collections.sort( expectedList, COMPARATOR );
}
return true;
}
if ( object instanceof String ) {
if ( field.getName().equals( "text" ) ) {
if ( parent instanceof Name ) {
return visit( object.toString(), expectedObj.toString() );
}
if ( parent instanceof Literal ) {
comparison = Literal.unquoted( object.toString() ).compareTo( Literal.unquoted( expectedObj.toString() ) );
return comparison == 0;
}
}
comparison = object.toString().compareToIgnoreCase( expectedObj.toString() );
return comparison == 0;
}
if ( object instanceof Boolean ) {
comparison = ( ( Boolean ) object ).compareTo( ( Boolean ) expectedObj );
return comparison == 0;
}
if ( object instanceof Integer ) {
comparison = ( ( Integer ) object ).compareTo( ( Integer ) expectedObj );
return comparison == 0;
}
if ( object instanceof Reference ) {
Reference ref = ( Reference ) object;
Reference expectedRef = ( Reference ) expectedObj;
if ( ref.text.startsWith( "HT_" ) ) { // Temporary table
if ( expectedRef.referent == null ) {
comparison = 1;
} else if ( expectedRef.text.startsWith( "HT_" ) ) {
comparison = ref.text.compareToIgnoreCase( expectedRef.text );
} else {
comparison = -1;
}
return comparison == 0;
}
if ( !compareTypes( ref.referent, expectedRef.referent ) ) {
return false;
}
if ( ref.referent instanceof NamedObject ) {
compareReferents( ref, expectedRef );
} else if ( ref.referent instanceof Alias ) {
ref = ( ( Alias ) ref.referent ).reference;
expectedRef = ( ( Alias ) expectedRef.referent ).reference;
result = compareWithRespectToNull( ref, expectedRef );
if ( result != null ) {
return result;
}
if ( ref.referent != null || expectedRef.referent != null ) {
compareReferents( ref, expectedRef );
}
} else {
throw new RuntimeException( "Not yet implemented" );
}
return comparison == 0;
}
return true;
}
}
private class MapVisitor extends Visitor {
MapVisitor( Object expectedObject ) {
super( expectedObject );
}
@Override
public boolean visit( Object object, SqlObject parent, Field field, int index ) {
Object expectedObj = expectedObject( field, index );
if ( !( object instanceof Name ) ) {
return true;
}
Name name = ( Name ) object;
Name expectedName = ( Name ) expectedObj;
String text = name.unquoted();
String expectedText = expectedName.unquoted();
if ( name.parent() instanceof Column ) {
text = ( ( CreateTable ) name.parent().parent() ).name().unquoted() + '.' + text;
expectedText = ( ( CreateTable ) expectedName.parent().parent() ).name().unquoted() + '.' + expectedText;
}
// If entries don't match exactly, check if we can swap an existing entry as a better match
boolean trySwap = !text.equalsIgnoreCase( expectedText );
if ( trySwap && object instanceof Alias ) {
Alias alias = ( Alias ) object;
trySwap =
alias.reference != null
&& !alias.reference.unquoted().equalsIgnoreCase( ( ( Alias ) expectedObj ).reference.unquoted() );
}
if ( trySwap ) {
String oldText = namesByExpectedName.get( text );
if ( oldText != null ) {
namesByExpectedName.put( text, text );
namesByExpectedName.put( expectedText, oldText );
return true;
}
}
namesByExpectedName.put( expectedText, text );
return true;
}
}
private abstract class Visitor extends SqlVisitor {
private Deque< Object > expectedObjects = new LinkedList< Object >();
Visitor( Object expectedObject ) {
expectedObjects.push( expectedObject );
}
Object expectedObject( Field field, int index ) {
if ( index < 0 ) {
if ( field == null ) {
return expectedObjects.peek();
}
try {
return field.get( expectedObjects.peek() );
} catch ( IllegalAccessException error ) {
throw new RuntimeException( error );
}
}
return ( ( List< Object > ) expectedObjects.peek() ).get( index );
}
private boolean postVisit() {
expectedObjects.pop();
return true;
}
@Override
protected boolean postVisitElements( List< ? > list, SqlObject parent, Field field, int index ) {
return postVisit();
}
@Override
protected boolean postVisitFields( SqlObject object, SqlObject parent, Field field, int index ) {
return postVisit();
}
private boolean preVisit( Field field, int index ) {
if ( index < 0 ) {
if ( field != null ) {
try {
expectedObjects.push( field.get( expectedObjects.peek() ) );
} catch ( IllegalAccessException error ) {
throw new RuntimeException( error );
}
}
} else {
expectedObjects.push( ( ( List< ? > ) expectedObjects.peek() ).get( index ) );
}
return true;
}
@Override
protected boolean preVisitElements( List< ? > list, SqlObject parent, Field field, int index ) {
return preVisit( field, index );
}
@Override
protected boolean preVisitFields( SqlObject object, SqlObject parent, Field field, int index ) {
return preVisit( field, index );
}
}
}

View File

@ -0,0 +1,40 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
/**
*
*/
public interface SqlObject {
SqlObject localObjectInScope( String name, boolean ignoreColumns );
LocalScope localScope();
SqlObject parent();
void setParent( SqlObject parent );
Statement statement();
}

View File

@ -0,0 +1,934 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
/**
* Parses <strong><em>valid</em></strong> SQL DDL and DML (not DCL or TCL), including calls to ODBC functions
*/
public class SqlParser {
private transient StringTokenizer tokenizer;
private transient String token;
private transient String nextToken;
Map< String, NamedObject > globalObjectsByName = new HashMap< String, NamedObject >();
private void appendNextIfSuffix() {
if ( nextToken == null ) {
return;
}
if ( token.startsWith( "'" ) && nextToken.startsWith( "'" )
|| ( token.endsWith( "." ) && ( quoted( nextToken ) || nextMatches( "*" ) ) ) ) {
token += nextToken;
nextToken = next_();
appendNextIfSuffix();
}
}
public void clear() {
globalObjectsByName.clear();
}
private boolean endOfExpression() {
return matches(
";",
"AS",
"FROM",
"WHERE",
"LEFT",
"INNER",
"OUTER",
"CROSS",
"GROUP",
"HAVING",
"ORDER",
")",
",",
"FOR",
"WHEN",
"THEN",
"ELSE",
"END",
"LIMIT",
"OFFSET",
"UNION" );
}
private void mapGlobalObjectByName( NamedObject object ) {
String unquotedName = object.name().unquoted().toLowerCase();
globalObjectsByName.put( object instanceof Column
? ( ( CreateTable ) ( ( SqlObject ) object ).parent() ).name().unquoted().toLowerCase() + '.' + unquotedName
: unquotedName, object );
object.localScope().mapLocalObjectByName( unquotedName, object );
}
private boolean match( String token, String... matchingTokens ) {
for ( String matchingToken : matchingTokens ) {
if ( matchingToken.equalsIgnoreCase( token ) ) {
return true;
}
}
return false;
}
private boolean matches( String... tokens ) {
return match( token, tokens );
}
private Reference newGlobalReference( SqlObject parent, String text ) {
return setGlobalReferent( new Reference( parent, text ) );
}
private Reference newGlobalReference( SqlObject parent, Reference tableReference, String text ) {
Reference ref = new Reference( parent, text );
text = ref.unquoted();
if ( text.indexOf( '.' ) < 0 ) {
text = ( ( CreateTable ) tableReference.referent ).name().unquoted() + '.' + text;
}
return setGlobalReferent( ref, text );
}
private String next() {
token = token == null ? next_() : nextToken;
nextToken = ";".equals( nextToken ) ? null : next_();
appendNextIfSuffix();
while ( matches( "<", ">", "=", "!", "&", "|", ":" ) && nextMatches( "=", "<", ">", "|", "&" ) ) {
String oldToken = token;
token = oldToken + next();
}
return token;
}
private String next_() {
String token = tokenizer.nextToken().trim();
while ( token.isEmpty() && tokenizer.hasMoreTokens() ) {
token = tokenizer.nextToken().trim();
}
if ( quoted( token ) ) {
String quote = token;
StringBuilder builder = new StringBuilder( token );
do {
token = tokenizer.nextToken();
builder.append( token );
} while ( !quote.equals( token ) );
return builder.toString();
}
return token;
}
private void nextAndMatches( String... tokens ) {
for ( String token : tokens ) {
next();
if ( !matches( token ) ) {
throw new UnexpectedTokenException( "token (Expected " + nextToken + ")", token );
}
}
}
private boolean nextMatches( String... tokens ) {
return match( nextToken, tokens );
}
private Operation operation( SqlObject parent, boolean unary ) {
int precedence = 0;
if ( matches( ":=" ) ) {
return new BinaryOperation( parent, token, precedence );
}
precedence++;
if ( matches( "OR", "||" ) ) {
return new CommutativeBinaryOperation( parent, token, precedence );
}
precedence++;
if ( matches( "XOR" ) ) {
return new CommutativeBinaryOperation( parent, token, precedence );
}
precedence++;
if ( matches( "AND", "&&" ) ) {
return new CommutativeBinaryOperation( parent, token, precedence );
}
precedence++;
if ( matches( "NOT" ) ) {
return new UnaryOperation( parent, token, precedence );
}
precedence++;
if ( matches( "BETWEEN" ) ) {
return new Between( parent, token, precedence );
}
if ( matches( "CASE" ) ) {
return new Case( parent, token, precedence );
}
precedence++;
if ( matches( "=", "!=", "<>", "<=>", "LIKE", "IS" ) ) {
return new CommutativeBinaryOperation( parent, token, precedence );
}
if ( matches( "<", ">", "<=", ">=", "IN", "ESCAPE" ) ) {
return new BinaryOperation( parent, token, precedence );
}
if ( matches( "EXISTS", "ANY", "ALL", "SOME" ) ) {
return new UnaryOperation( parent, token, precedence );
}
precedence++;
if ( matches( "|" ) ) {
return new CommutativeBinaryOperation( parent, token, precedence );
}
precedence++;
if ( matches( "&" ) ) {
return new CommutativeBinaryOperation( parent, token, precedence );
}
precedence++;
if ( matches( "<<", ">>" ) ) {
return new BinaryOperation( parent, token, precedence );
}
precedence++;
if ( matches( "+" ) ) {
return new CommutativeBinaryOperation( parent, token, precedence );
}
if ( !unary && matches( "-" ) ) {
return new BinaryOperation( parent, token, precedence );
}
precedence++;
if ( matches( "*" ) ) {
return new CommutativeBinaryOperation( parent, token, precedence );
}
if ( matches( "/", "%", "MOD", "DIV" ) ) {
return new BinaryOperation( parent, token, precedence );
}
precedence++;
if ( matches( "^" ) ) {
return new CommutativeBinaryOperation( parent, token, precedence );
}
precedence++;
if ( matches( "~" ) ) {
return new UnaryOperation( parent, nextToken, precedence );
}
if ( unary && matches( "-", "~" ) ) {
return new UnaryOperation( parent, token, precedence );
}
precedence++;
if ( matches( "!" ) ) {
return new UnaryOperation( parent, token, precedence );
}
precedence++;
if ( matches( "(" ) ) {
return new Parentheses( parent, token, precedence );
}
return null;
}
/**
* @param sql
* @return the object model of the supplied SQL
* @throws UnexpectedTokenException if an unexpected token is encountered in the supplied SQL
*/
public Statement parse( String sql ) {
if ( !sql.endsWith( ";" ) ) {
sql += ";";
}
token = nextToken = null;
tokenizer = new StringTokenizer( sql, " \"'();,*/+-=<>&|^%!~:{}", true );
next();
Statement statement;
if ( matches( "CREATE" ) ) {
statement = parseCreate();
} else if ( matches( "ALTER" ) ) {
statement = parseAlterTable();
} else if ( matches( "SELECT" ) ) {
statement = parseSelect( null );
} else if ( matches( "CALL" ) ) {
statement = parseCall();
} else if ( matches( "INSERT" ) ) {
statement = parseInsert();
} else if ( matches( "DELETE" ) ) {
statement = parseDelete();
} else if ( matches( "UPDATE" ) ) {
statement = parseUpdate();
} else {
throw new UnexpectedTokenException( "statement", token );
}
// Resolve unknown references
SqlWalker.INSTANCE.walk( new SqlVisitor() {
private void convertToLiteral( Reference reference, SqlObject parent, Field field, int index ) {
try {
Object val = field.get( parent );
if ( val instanceof List ) {
List< SqlObject > list = ( List< SqlObject > ) val;
list.set( index, new Literal( parent, reference.text ) );
} else {
field.set( parent, new Literal( parent, reference.text ) );
}
} catch ( IllegalAccessException error ) {
throw new RuntimeException( error );
}
}
private boolean resolve( Reference reference, String name, boolean ignoreColumns, SqlObject scopeObject ) {
name = name.toLowerCase();
reference.referent = scopeObject.localObjectInScope( name, ignoreColumns );
if ( reference.referent != null ) {
return true;
}
reference.referent = globalObjectsByName.get( name );
if ( reference.referent != null ) {
return true;
}
Statement statement = scopeObject.statement();
if ( statement instanceof DmlStatement ) {
for ( Reference globalRef : ( ( DmlStatement ) statement ).tables ) {
if ( globalRef.unquoted().startsWith( "HT_" ) ) { // Temporary table
continue;
}
reference.referent =
globalObjectsByName.get( ( ( NamedObject ) globalRef.referent ).name().unquoted().toLowerCase()
+ '.' + name );
if ( reference.referent != null ) {
return true;
}
}
} else if ( statement instanceof AlterTable ) {
reference.referent =
globalObjectsByName.get( ( ( NamedObject ) ( ( AlterTable ) statement ).table.referent ).name().unquoted().toLowerCase()
+ '.' + name );
if ( reference.referent != null ) {
return true;
}
}
return false;
}
@Override
public boolean visit( Object object, SqlObject parent, Field field, int index ) {
if ( !( object instanceof Reference ) ) {
return true;
}
Reference ref = ( Reference ) object;
if ( ref.referent != null ) {
return true;
}
String name = ref.unquoted();
if ( name.startsWith( "HT_" ) ) { // Temporary table
return true;
}
int ndx = name.indexOf( '.' );
if ( ndx > 0 ) {
String qualifier = name.substring( 0, ndx ).toLowerCase();
Alias alias = ( Alias ) ref.localObjectInScope( qualifier, true );
CreateTable table = null;
if ( alias == null ) {
table = ( CreateTable ) globalObjectsByName.get( qualifier );
} else {
if ( alias.reference.referent instanceof Select ) {
if ( resolve( ref, name.substring( ndx + 1 ), false, alias.reference.referent ) ) {
return true;
}
convertToLiteral( ref, parent, field, index );
return true;
}
table = ( CreateTable ) alias.reference.referent;
}
if ( table != null ) {
name = table.name().unquoted() + '.' + name.substring( ndx + 1 );
setGlobalReferent( ref, name );
return true;
}
} else if ( resolve( ref, name, false, ref ) ) {
return true;
}
convertToLiteral( ref, parent, field, index );
return true;
}
},
statement );
return statement;
}
private void parseAlias( Aliasable aliasable ) {
aliasable.alias = new Alias( aliasable, token );
aliasable.alias.localScope().mapLocalObjectByName( aliasable.alias.unquoted(), aliasable.alias );
}
private Aliasable parseAliasable( SqlObject select ) {
Aliasable aliasable = new Aliasable( select );
aliasable.expression = parseExpression( aliasable );
if ( matches( "AS" ) ) {
next();
parseAlias( aliasable );
} else if ( endOfExpression() ) {
return aliasable;
} else {
parseAlias( aliasable );
}
if ( aliasable.expression instanceof Reference ) {
aliasable.alias.reference = ( Reference ) aliasable.expression;
}
next();
return aliasable;
}
private Aliasable parseAliasable( Select select, SqlObject parent ) {
Aliasable aliasable = parseAliasable( parent );
if ( aliasable.expression instanceof Reference ) {
select.tables.add( setGlobalReferent( ( Reference ) aliasable.expression ) );
if ( aliasable.alias != null ) {
setGlobalReferent( aliasable.alias.reference );
}
} else if ( aliasable.expression instanceof Parentheses && aliasable.alias != null ) {
SqlObject obj = ( ( Parentheses ) aliasable.expression ).operands.get( 0 );
if ( obj instanceof Select ) {
aliasable.alias.reference = new Reference( aliasable.alias, null );
aliasable.alias.reference.referent = obj;
}
}
return aliasable;
}
private Statement parseAlterTable() {
next();
if ( matches( "TABLE" ) ) {
AlterTable alter = new AlterTable();
alter.table = newGlobalReference( alter, next() );
nextAndMatches( "ADD", "CONSTRAINT" );
String name = next();
next();
if ( matches( "FOREIGN" ) ) {
ForeignKey key = new ForeignKey( alter );
alter.constraint = key;
setGlobalName( name, key );
parseKey( key.columns(), key );
nextAndMatches( "REFERENCES" );
key.references = newGlobalReference( key, next() );
} else if ( matches( "UNIQUE" ) ) {
alter.constraint = new Unique( alter );
setGlobalName( name, alter.constraint );
next();
parseColumns( alter.constraint.columns(), alter.constraint );
}
return alter;
}
throw new UnexpectedTokenException( "alter type", token );
}
private Call parseCall() {
Call call = new Call();
next();
if ( matches( "NEXT" ) ) {
nextAndMatches( "VALUE", "FOR" );
NextValueFor function = new NextValueFor( call );
call.function = function;
function.parameters.add( newGlobalReference( function, next() ) );
} else if ( nextMatches( "(" ) ) {
call.function = parseFunction( call );
} else {
throw new UnexpectedTokenException( "call procedure", token );
}
return call;
}
private Check parseCheck( SqlObject parent ) {
Check check = new Check( parent, token );
nextAndMatches( "(" );
next();
SqlObject expr = parseExpression( check );
check.parameters.add( expr );
next();
return check;
}
private void parseColumns( List< Reference > columns, SqlObject parent ) {
do {
Reference ref = new Reference( parent, next() );
ref.referent = parent.localObjectInScope( ref.unquoted(), false );
columns.add( ref );
next();
} while ( matches( "," ) );
}
private Statement parseCreate() {
next();
if ( matches( "TABLE", "CACHED", "LOCAL", "GLOBAL", "TEMPORARY" ) ) {
return parseCreateTable();
}
if ( matches( "SEQUENCE" ) ) {
return parseCreateSequence();
}
if ( matches( "INDEX" ) ) {
return parseCreateIndex();
}
if ( matches( "ALIAS" ) ) {
return parseCreateAlias();
}
throw new UnexpectedTokenException( "create type", token );
}
private CreateAlias parseCreateAlias() {
CreateAlias alias = new CreateAlias();
setGlobalName( next(), alias );
nextAndMatches( "AS" );
alias.sourceCode = next();
for ( next(); !matches( ";" ); next() ) {
alias.sourceCode = token;
}
return alias;
}
private CreateIndex parseCreateIndex() {
CreateIndex index = new CreateIndex();
index.setName( new Name( index, next() ) );
nextAndMatches( "ON" );
index.table = newGlobalReference( index, next() );
nextAndMatches( "(" );
do {
Reference ref = newGlobalReference( index, index.table, next() );
index.columns.add( ref );
next();
} while ( matches( "," ) );
next();
return index;
}
private CreateSequence parseCreateSequence() {
CreateSequence sequence = new CreateSequence();
setGlobalName( next(), sequence );
next();
if ( matches( "START" ) ) {
nextAndMatches( "WITH" );
sequence.start = Integer.parseInt( next() );
next();
}
if ( matches( "INCREMENT" ) ) {
nextAndMatches( "BY" );
sequence.increment = Integer.parseInt( next() );
}
return sequence;
}
private CreateTable parseCreateTable() {
CreateTable table = new CreateTable();
if ( matches( "CACHED" ) ) {
table.cached = true;
next();
}
if ( matches( "LOCAL", "GLOBAL" ) ) {
table.temporaryType = token;
nextAndMatches( "TEMPORARY" );
}
if ( matches( "TEMPORARY" ) ) {
table.temporary = true;
nextAndMatches( "TABLE" );
}
if ( matches( "TABLE" ) ) {
next();
}
if ( matches( "IF" ) ) {
table.ifNotExists = true;
nextAndMatches( "NOT", "EXISTS" );
next();
}
setGlobalName( token, table );
for ( nextAndMatches( "(" ); !matches( ")" ); ) {
next();
if ( matches( "PRIMARY" ) ) {
PrimaryKey key = new PrimaryKey( table );
table.constraints.add( key );
parseKey( key.columns(), key );
next();
} else if ( matches( "CHECK" ) ) {
Check check = parseCheck( table );
table.constraints.add( check );
} else {
Column column = new Column( table );
table.columns.add( column );
column.setName( new Name( column, token ) );
mapGlobalObjectByName( column );
column.type = next();
next();
if ( matches( "(" ) ) {
column.length = Integer.parseInt( next() );
next();
if ( matches( "," ) ) {
column.decimals = Integer.parseInt( next() );
nextAndMatches( ")" );
}
next();
}
if ( matches( "NOT" ) ) {
nextAndMatches( "NULL" );
column.notNull = true;
next();
}
if ( matches( "CHECK" ) ) {
column.check = parseCheck( column );
}
if ( matches( "GENERATED" ) ) {
nextAndMatches( "BY", "DEFAULT", "AS", "IDENTITY" );
column.generatedByDefaultAsIdentity = true;
next();
}
}
}
next();
if ( matches( "ON" ) ) {
nextAndMatches( "COMMIT" );
table.onCommit = next();
for ( next(); !matches( ";" ); next() ) {
table.onCommit += ' ' + token;
}
}
return table;
}
private Delete parseDelete() {
Delete delete = new Delete();
nextAndMatches( "FROM" );
delete.tables.add( newGlobalReference( delete, next() ) );
next();
if ( matches( "WHERE" ) ) {
next();
delete.where = parseExpression( delete );
}
return delete;
}
private SqlObject parseExpression( SqlObject parent ) {
return parseExpression( parent, null );
}
private SqlObject parseExpression( SqlObject parent, Operation previousOperation ) {
Operation operation = operation( parent, true );
SqlObject expr;
if ( operation instanceof UnaryOperation ) {
next();
if ( previousOperation != null && previousOperation.operator.equalsIgnoreCase( "IN" ) ) {
ParentheticalOptionallyOrderedSet set = new ParentheticalOptionallyOrderedSet( parent, operation );
while ( !matches( ")" ) ) {
if ( matches( "," ) ) {
next();
}
expr = parseExpression( set );
set.operands.add( expr );
}
next();
expr = set;
} else if ( operation instanceof Case ) {
Case caseOp = ( Case ) operation;
if ( !matches( "WHEN" ) ) {
caseOp.expression = parseExpression( caseOp );
next();
}
while ( matches( "WHEN" ) ) {
When when = new When( caseOp );
caseOp.operands.add( when );
next();
when.condition = parseExpression( when );
next();
when.then = parseExpression( when );
if ( matches( "ELSE" ) ) {
next();
when.elseExpression = parseExpression( when );
}
}
next();
expr = caseOp;
} else {
expr = parseExpression( parent, operation );
if ( operation instanceof Parentheses ) {
next();
}
}
} else if ( matches( "SELECT" ) ) {
expr = parseSelect( parent );
} else if ( matches( "{" ) || nextMatches( "(" ) ) {
expr = parseFunction( parent );
} else {
if ( Character.isJavaIdentifierStart( Literal.unquoted( token ).charAt( 0 ) ) ) {
expr = new Reference( parent, token );
} else {
expr = new Literal( parent, token );
}
next();
}
if ( endOfExpression() ) {
if ( previousOperation == null ) {
return expr;
}
expr.setParent( previousOperation );
previousOperation.operands.add( expr );
return previousOperation;
}
operation = operation( parent, false );
if ( operation == null ) {
return expr;
}
next();
if ( previousOperation == null ) {
expr.setParent( operation );
operation.operands.add( expr );
return parseExpression( parent, operation );
}
if ( operation.precedence < previousOperation.precedence ) {
expr.setParent( previousOperation );
previousOperation.operands.add( expr );
if ( previousOperation.parent() instanceof Operation ) {
Operation parentOperation = ( Operation ) previousOperation.parent();
if ( parentOperation.operator.equalsIgnoreCase( operation.operator ) ) {
operation = parentOperation;
return parseExpression( parent, operation );
}
}
if ( previousOperation instanceof Between ) {
operation = previousOperation;
} else {
previousOperation.setParent( operation );
operation.operands.add( previousOperation );
}
return parseExpression( parent, operation );
}
expr.setParent( operation );
operation.operands.add( expr );
operation.setParent( previousOperation );
previousOperation.operands.add( operation );
parseExpression( parent, operation );
return previousOperation;
}
private Function parseFunction( SqlObject parent ) {
Function function;
if ( matches( "EXTRACT" ) ) {
function = new Extract( parent, token );
} else if ( matches( "COUNT" ) ) {
function = new Count( parent, token );
} else if ( matches( "CAST" ) ) {
function = new Cast( parent, token );
} else if ( matches( "TRIM" ) ) {
function = new Trim( parent, token );
} else if ( matches( "{" ) ) {
nextAndMatches( "fn" );
next();
function = parseFunction( new OdbcFunction( parent, token ) );
next();
return function;
} else {
function = new Function( parent, token );
}
return parseFunction( function );
}
private Function parseFunction( Function function ) {
nextAndMatches( "(" );
next();
while ( !matches( ")" ) ) {
if ( matches( "DISTINCT" ) ) {
( ( Count ) function ).distinct = true;
next();
} else if ( matches( "LEADING", "TRAILING", "BOTH" ) ) {
( ( Trim ) function ).type = token;
next();
}
if ( matches( ",", "FROM", "AS" ) ) {
next();
}
SqlObject expr = parseExpression( function );
function.parameters.add( expr );
}
next();
return function;
}
private Insert parseInsert() {
Insert insert = new Insert();
nextAndMatches( "INTO" );
Reference tableRef = newGlobalReference( insert, next() );
insert.tables.add( tableRef );
next();
if ( matches( "SELECT" ) ) {
insert.select = parseSelect( insert );
} else {
if ( matches( "(" ) ) {
do {
Reference ref = newGlobalReference( insert, tableRef, next() );
insert.columns.add( ref );
next();
} while ( matches( "," ) );
next();
}
if ( matches( "SELECT" ) ) {
insert.select = parseSelect( insert );
} else if ( matches( "VALUES" ) ) {
nextAndMatches( "(" );
next();
while ( !matches( ")" ) ) {
if ( matches( "," ) ) {
next();
}
SqlObject expr = parseExpression( insert );
insert.values.add( expr );
}
next();
}
}
if ( matches( "WHERE" ) ) {
next();
insert.where = parseExpression( insert );
}
return insert;
}
private void parseKey( List< Reference > columns, SqlObject parent ) {
nextAndMatches( "KEY" );
next();
parseColumns( columns, parent );
}
private Select parseSelect( SqlObject parent ) {
Select select = new Select( parent );
if ( nextMatches( "DISTINCT" ) ) {
select.distinct = true;
next();
}
while ( !matches( "FROM", ";" ) ) {
next();
select.columns.add( parseAliasable( select ) );
}
if ( matches( "FROM" ) ) {
do {
next();
From from = new From( select );
select.froms.add( from );
from.aliasable = parseAliasable( select, from );
while ( matches( "LEFT", "OUTER", "INNER", "CROSS" ) ) {
Join join = new Join( from );
from.joins.add( join );
if ( matches( "LEFT" ) ) {
join.left = true;
next();
}
join.type = token;
nextAndMatches( "JOIN" );
next();
join.aliasable = parseAliasable( select, join );
if ( matches( "ON" ) ) {
next();
join.on = parseExpression( join );
}
}
} while ( matches( "," ) );
}
if ( matches( "WHERE" ) ) {
next();
select.where = parseExpression( select );
}
if ( matches( "GROUP" ) ) {
nextAndMatches( "BY" );
select.groupBy = new Reference( select, next() );
next();
}
if ( matches( "HAVING" ) ) {
next();
select.having = parseExpression( select );
}
if ( matches( "ORDER" ) ) {
nextAndMatches( "BY" );
do {
OrderBy orderBy = new OrderBy( select );
select.orderBy.add( orderBy );
orderBy.column = new Reference( select, next() );
next();
if ( matches( "ASC" ) ) {
next();
} else if ( matches( "DESC" ) ) {
orderBy.descending = true;
next();
}
} while ( matches( "," ) );
}
if ( matches( "LIMIT" ) ) {
next();
select.limit = parseExpression( select );
}
if ( matches( "OFFSET" ) ) {
next();
select.offset = parseExpression( select );
}
if ( matches( "FOR" ) ) {
nextAndMatches( "UPDATE" );
select.forUpdate = true;
next();
}
if ( matches( "UNION" ) ) {
next();
if ( matches( "ALL" ) ) {
select.unionAll = true;
next();
}
select.union = parseSelect( select );
}
return select;
}
private Update parseUpdate() {
Update update = new Update();
update.tables.add( newGlobalReference( update, next() ) );
nextAndMatches( "SET" );
do {
next();
update.sets.add( parseExpression( update ) );
} while ( matches( "," ) );
if ( matches( "WHERE" ) ) {
next();
update.where = parseExpression( update );
}
return update;
}
private boolean quoted( String token ) {
return token.startsWith( "'" ) || token.startsWith( "\"" );
}
private void setGlobalName( String text, NamedObject object ) {
object.setName( new Name( object, text ) );
object.name().setParent( object );
mapGlobalObjectByName( object );
}
private Reference setGlobalReferent( Reference reference ) {
return setGlobalReferent( reference, reference.unquoted() );
}
Reference setGlobalReferent( Reference reference, String unquotedText ) {
if ( unquotedText.startsWith( "HT_" ) ) { // Temporary table
return reference;
}
reference.referent = globalObjectsByName.get( unquotedText.toLowerCase() );
if ( reference.referent == null ) {
throw new RuntimeException( "Invalid global reference: No SQL object exists named " + reference.text );
}
return reference;
}
}

View File

@ -0,0 +1,53 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
import java.lang.reflect.Field;
import java.util.List;
/**
*
*/
public abstract class SqlVisitor {
protected boolean postVisitElements( List< ? > list, SqlObject parent, Field field, int index ) {
return true;
}
protected boolean postVisitFields( SqlObject object, SqlObject parent, Field field, int index ) {
return true;
}
protected boolean preVisitElements( List< ? > list, SqlObject parent, Field field, int index ) {
return true;
}
protected boolean preVisitFields( SqlObject object, SqlObject parent, Field field, int index ) {
return true;
}
protected boolean visit( Object object, SqlObject parent, Field field, int index ) {
return true;
}
}

View File

@ -0,0 +1,80 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
import java.lang.reflect.Field;
import java.util.List;
/**
*
*/
public class SqlWalker {
public static final SqlWalker INSTANCE = new SqlWalker();
protected SqlWalker() {
}
public boolean walk( SqlVisitor visitor, Object object ) {
return walk( visitor, object, null, null, -1 );
}
private boolean walk( SqlVisitor visitor, Object object, SqlObject parent, Field field, int index ) {
if ( !visitor.visit( object, parent, field, index ) ) {
return false;
}
if ( object instanceof List ) {
List< Object > list = ( List< Object > ) object;
if ( !visitor.preVisitElements( list, parent, field, index ) ) {
return false;
}
for ( int ndx = 0; ndx < list.size(); ++ndx ) {
if ( !walk( visitor, list.get( ndx ), parent, field, ndx ) ) {
return false;
}
}
if ( !visitor.postVisitElements( list, parent, field, index ) ) {
return false;
}
} else if ( object instanceof SqlObject && !( object instanceof Reference ) ) {
SqlObject sqlObj = ( SqlObject ) object;
if ( !visitor.preVisitFields( sqlObj, parent, field, index ) ) {
return false;
}
for ( Field fld : object.getClass().getFields() ) {
try {
if ( !walk( visitor, fld.get( object ), sqlObj, fld, -1 ) ) {
return false;
}
} catch ( IllegalAccessException error ) {
throw new RuntimeException( error );
}
}
if ( !visitor.postVisitFields( sqlObj, parent, field, index ) ) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,76 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public abstract class Statement extends AbstractSqlObject implements LocalScope {
private Map< String, Set< SqlObject > > localObjectsByName = new HashMap< String, Set< SqlObject > >();
public Statement( SqlObject parent ) {
super( parent );
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.LocalScope#localObject(java.lang.String)
*/
@Override
public SqlObject localObject( String unquotedName, boolean ignoreColumns ) {
Set< SqlObject > set = localObjectsByName.get( unquotedName.toLowerCase() );
if ( set == null ) {
return null;
}
for ( SqlObject obj : set ) {
if ( !ignoreColumns
|| ( !( obj instanceof Column ) && ( !( obj instanceof Alias ) || !( ( ( Alias ) obj ).reference.referent instanceof Column ) ) ) ) {
return obj;
}
}
return null;
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sql.LocalScope#mapLocalObjectByName(java.lang.String, org.hibernate.testing.sql.SqlObject)
*/
@Override
public void mapLocalObjectByName( String unquotedName, SqlObject object ) {
unquotedName = unquotedName.toLowerCase();
Set< SqlObject > set = localObjectsByName.get( unquotedName );
if ( set == null ) {
set = new HashSet< SqlObject >();
localObjectsByName.put( unquotedName, set );
}
if ( !set.add( object ) ) {
throw new IllegalStateException( "A SQL object already exists named " + unquotedName + " within " + this );
}
}
}

View File

@ -21,14 +21,18 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
/**
*
*/
public class Trim extends Function {
String type;
public String type;
Trim( SqlObject parent, String name ) {
super( parent, name );
}
/**
* {@inheritDoc}
@ -37,17 +41,18 @@ public class Trim extends Function {
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( "TRIM( " );
StringBuilder builder = new StringBuilder( name );
builder.append( "( " );
int ndx = 0;
if ( type != null ) {
builder.append( type );
if ( operands.size() > 1 ) {
builder.append( operands.get( ndx++ ) );
if ( parameters.size() > 1 ) {
builder.append( parameters.get( ndx++ ) );
}
builder.append( " FROM " );
}
if ( ndx < operands.size() ) {
builder.append( operands.get( ndx ) );
if ( ndx < parameters.size() ) {
builder.append( parameters.get( ndx ) );
}
builder.append( " )" );
return builder.toString();

View File

@ -21,19 +21,16 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
import java.util.ArrayList;
import java.util.List;
package org.hibernate.testing.sql;
/**
*
*/
public class Index extends Statement {
public class UnaryOperation extends Operation {
public Name name;
public Name table;
public List< Name > columns = new ArrayList< Name >();
UnaryOperation( SqlObject parent, String operator, int precedence ) {
super( parent, operator, precedence );
}
/**
* {@inheritDoc}
@ -42,9 +39,13 @@ public class Index extends Statement {
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( "CREATE INDEX " );
builder.append( name ).append( " ON " ).append( table ).append( ' ' );
Statement.listToStringInParentheses( builder, columns );
StringBuilder builder = new StringBuilder( operator );
if ( Character.isLetter( operator.charAt( 0 ) ) ) {
builder.append( ' ' );
}
if ( !operands.isEmpty() ) {
builder.append( operands.get( 0 ) );
}
return builder.toString();
}
}

View File

@ -21,7 +21,7 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
/**
*

View File

@ -21,12 +21,16 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
/**
*
*/
public class Unique extends Constraint {
public class Unique extends UniqueConstraint {
Unique( SqlObject parent ) {
super( parent );
}
/**
* {@inheritDoc}

View File

@ -0,0 +1,49 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sql;
import java.util.ArrayList;
import java.util.List;
/**
*
*/
public abstract class UniqueConstraint extends Constraint {
public List< Reference > columns = new ArrayList< Reference >();
UniqueConstraint( SqlObject parent ) {
super( parent );
}
/**
* {@inheritDoc}
*
* @see org.hibernate.testing.sqlparser.Constraint#columns()
*/
@Override
public List< Reference > columns() {
return columns;
}
}

View File

@ -0,0 +1,32 @@
package org.hibernate.testing.sql;
import java.util.List;
public class Update extends DmlStatement {
public List< SqlObject > sets = new OptionallyOrderedSet< SqlObject >();
public SqlObject where;
Update() {
super( null );
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( "UPDATE " );
if ( !tables.isEmpty() ) {
builder.append( tables.get( 0 ) );
}
builder.append( " SET " );
collectionToString( builder, sets );
if ( where != null ) {
builder.append( " WHERE " ).append( where );
}
return builder.toString();
}
}

View File

@ -21,16 +21,20 @@
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
package org.hibernate.testing.sql;
/**
*
*/
public class When {
public class When extends AbstractSqlObject {
public Object condition;
public Object then;
public Object elseExpression;
public SqlObject condition;
public SqlObject then;
public SqlObject elseExpression;
When( SqlObject parent ) {
super( parent );
}
/**
* {@inheritDoc}

View File

@ -1,17 +0,0 @@
package org.hibernate.testing.sqlparser;
public class Aliasable {
public Object name;
public String alias;
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return alias == null ? name.toString() : name + " AS " + alias;
}
}

View File

@ -1,28 +0,0 @@
package org.hibernate.testing.sqlparser;
import java.util.ArrayList;
import java.util.List;
public abstract class Constraint {
public Name name;
public List< Name > columns = new ArrayList< Name >();
public abstract String type();
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if ( name != null ) {
builder.append( "CONSTRAINT " ).append( name ).append( ' ' );
}
builder.append( type() ).append( ' ' );
Statement.listToStringInParentheses( builder, columns );
return builder.toString();
}
}

View File

@ -1,44 +0,0 @@
package org.hibernate.testing.sqlparser;
import java.util.ArrayList;
import java.util.List;
/**
*
*/
public class Expression {
public List< String > operators = new ArrayList< String >();
public List< Object > operands = new ArrayList< Object >();
private void operandToString( StringBuilder builder, int index ) {
Object operand = index < operands.size() ? operands.get( index ) : "";
if ( operand instanceof Select || operand instanceof Case ) {
builder.append( "( " ).append( operand ).append( " )" );
} else if ( operand instanceof List ) {
Statement.listToStringInParentheses( builder, ( List< ? > ) operand );
} else {
builder.append( operand );
}
}
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( "( " );
int maxOperandsNdx = operands.size() - 1;
for ( int ndx = 0, namesSize = operators.size(); ndx < namesSize || ndx < maxOperandsNdx; ndx++ ) {
if ( maxOperandsNdx > 0 ) {
operandToString( builder, ndx );
}
builder.append( ' ' ).append( ( ndx < operators.size() ? operators.get( ndx ) : "" ) ).append( ' ' );
}
operandToString( builder, Math.max( maxOperandsNdx, 0 ) );
builder.append( " )" );
return builder.toString();
}
}

View File

@ -1,19 +0,0 @@
package org.hibernate.testing.sqlparser;
public class Function extends Expression {
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
if ( !operators.isEmpty() ) {
builder.append( operators.get( 0 ) );
}
Statement.listToStringInParentheses( builder, operands );
return builder.toString();
}
}

View File

@ -1,784 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2011, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.testing.sqlparser;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
/**
* Parses <strong><em>valid</em></strong> SQL DDL and DML (not DCL or TCL), including calls to ODBC functions
*/
public class SqlParser {
private transient StringTokenizer tokenizer;
private transient String token;
private transient String nextToken;
private Map< String, Table > tablesByName = new HashMap< String, Table >();
private void appendNext() {
String oldToken = token;
token = oldToken + ' ' + next();
}
private void appendNextIfSuffix() {
if ( nextToken == null ) {
return;
}
if ( token.startsWith( "'" ) && nextToken.startsWith( "'" )
|| ( token.endsWith( "." ) && ( quoted( nextToken ) || nextMatches( "*" ) ) ) ) {
token += nextToken;
nextToken = next_();
appendNextIfSuffix();
}
}
public void clear() {
tablesByName.clear();
}
private boolean endOfExpression() {
return matches(
";",
"AS",
"FROM",
"WHERE",
"LEFT",
"INNER",
"OUTER",
"CROSS",
"GROUP",
"HAVING",
"ORDER",
")",
",",
"FOR",
"WHEN",
"THEN",
"ELSE",
"END",
"LIMIT",
"OFFSET",
"UNION" );
}
private boolean match( String token, String... matchingTokens ) {
for ( String matchingToken : matchingTokens ) {
if ( matchingToken.equalsIgnoreCase( token ) ) {
return true;
}
}
return false;
}
private boolean matches( String... tokens ) {
return match( token, tokens );
}
private String next() {
token = token == null ? next_() : nextToken;
nextToken = ";".equals( nextToken ) ? null : next_();
appendNextIfSuffix();
return token;
}
private String next_() {
String token = tokenizer.nextToken().trim();
while ( token.isEmpty() && tokenizer.hasMoreTokens() ) {
token = tokenizer.nextToken().trim();
}
if ( quoted( token ) ) {
String quote = token;
StringBuilder builder = new StringBuilder( token );
do {
token = tokenizer.nextToken();
builder.append( token );
} while ( !quote.equals( token ) );
return builder.toString();
}
return token;
}
private void nextAndMatches( String... tokens ) {
for ( String token : tokens ) {
next();
if ( !matches( token ) ) {
throw new UnexpectedTokenException( "token (Expected " + nextToken + ")", token );
}
}
}
private boolean nextMatches( String... tokens ) {
return match( nextToken, tokens );
}
/**
* @param sql
* @return the object model of the supplied SQL
* @throws UnexpectedTokenException if an unexpected token is encountered in the supplied SQL
*/
public Statement parse( String sql ) {
if ( !sql.endsWith( ";" ) ) {
sql += ";";
}
token = nextToken = null;
tokenizer = new StringTokenizer( sql, " \"'();,*/+-=<>&|^%!~:{}", true );
next();
if ( matches( "CREATE" ) ) {
return parseCreate();
}
if ( matches( "ALTER" ) ) {
return parseAlter();
}
if ( matches( "SELECT" ) ) {
return parseSelect();
}
if ( matches( "CALL" ) ) {
return parseCall();
}
if ( matches( "INSERT" ) ) {
return parseInsert();
}
if ( matches( "DELETE" ) ) {
return parseDelete();
}
if ( matches( "UPDATE" ) ) {
return parseUpdate();
}
throw new UnexpectedTokenException( "statement", token );
}
private Aliasable parseAliasable() {
Object expr;
if ( matches( "(" ) ) {
expr = parseExpression();
} else {
expr = new Name( token );
next();
}
return parseAliasable( expr );
}
private Alias parseAlias() {
Alias alias = new Alias();
alias.name = next();
nextAndMatches( "AS" );
alias.sourceCode = next();
for ( next(); !matches( ";" ); next() ) {
alias.sourceCode = token;
}
return alias;
}
private Aliasable parseAliasable( Object expression ) {
Aliasable aliasable = new Aliasable();
aliasable.name = expression;
if ( matches( "AS" ) ) {
aliasable.alias = next();
} else if ( endOfExpression() ) {
return aliasable;
} else {
aliasable.alias = token;
}
next();
return aliasable;
}
private Statement parseAlter() {
next();
if ( matches( "TABLE" ) ) {
Table table = tablesByName.get( new Name( next() ).unquoted() );
nextAndMatches( "ADD", "CONSTRAINT" );
Name name = new Name( next() );
next();
if ( matches( "FOREIGN" ) ) {
ForeignKey key = new ForeignKey();
table.constraints.add( key );
key.name = name;
parseKey( key.columns );
nextAndMatches( "REFERENCES" );
key.references = new Name( next() );
} else if ( matches( "UNIQUE" ) ) {
Unique constraint = new Unique();
table.constraints.add( constraint );
constraint.name = name;
next();
parseColumns( constraint.columns );
}
return null;
}
throw new UnexpectedTokenException( "alter type", token );
}
private Call parseCall() {
Call call = new Call();
next();
if ( matches( "NEXT" ) ) {
nextAndMatches( "VALUE", "FOR" );
NextValueFor function = new NextValueFor();
call.function = function;
function.operands.add( new Name( next() ) );
} else if ( nextMatches( "(" ) ) {
call.function = parseFunction();
} else {
throw new UnexpectedTokenException( "call procedure", token );
}
return call;
}
private Check parseCheck() {
Check check = new Check();
nextAndMatches( "(" );
next();
check.operands.add( parseExpression() );
next();
return check;
}
private void parseColumns( List< Name > columns ) {
do {
columns.add( new Name( next() ) );
next();
} while ( matches( "," ) );
}
private Statement parseCreate() {
next();
if ( matches( "SEQUENCE" ) ) {
return parseSequence();
}
if ( matches( "TABLE", "CACHED", "LOCAL", "GLOBAL", "TEMPORARY" ) ) {
return parseTable();
}
if ( matches( "INDEX" ) ) {
return parseIndex();
}
if ( matches( "ALIAS" ) ) {
return parseAlias();
}
throw new UnexpectedTokenException( "create type", token );
}
private Delete parseDelete() {
Delete delete = new Delete();
nextAndMatches( "FROM" );
delete.table = new Name( next() );
next();
if ( matches( "WHERE" ) ) {
next();
delete.where = parseExpression();
}
return delete;
}
private Object parseExpression() {
Expression expression = new Expression();
if ( matches( "SELECT" ) ) {
return parseSelect();
}
parseOperand( expression );
return parseOperator( expression );
}
private Function parseFunction() {
Function function;
if ( matches( "EXTRACT" ) ) {
function = new Extract();
} else if ( matches( "COUNT" ) ) {
function = new Count();
} else if ( matches( "CAST" ) ) {
function = new Cast();
} else if ( matches( "TRIM" ) ) {
function = new Trim();
} else {
function = new Function();
}
function.operators.add( token );
nextAndMatches( "(" );
next();
while ( !matches( ")" ) ) {
if ( matches( "DISTINCT" ) ) {
( ( Count ) function ).distinct = true;
next();
} else if ( matches( "LEADING", "TRAILING", "BOTH" ) ) {
( ( Trim ) function ).type = token;
next();
}
if ( matches( ",", "FROM", "AS" ) ) {
next();
}
function.operands.add( parseExpression() );
}
next();
return function;
}
private Index parseIndex() {
Index index = new Index();
index.name = new Name( next() );
nextAndMatches( "ON" );
index.table = new Name( next() );
nextAndMatches( "(" );
do {
index.columns.add( new Name( next() ) );
next();
} while ( matches( "," ) );
next();
return index;
}
private Insert parseInsert() {
Insert insert = new Insert();
nextAndMatches( "INTO" );
insert.table = new Name( next() );
next();
if ( matches( "SELECT" ) ) {
insert.select = parseSelect();
} else {
if ( matches( "(" ) ) {
parseColumns( insert.columns );
next();
}
if ( matches( "SELECT" ) ) {
insert.select = parseSelect();
} else if ( matches( "VALUES" ) ) {
nextAndMatches( "(" );
next();
while ( !matches( ")" ) ) {
if ( matches( "," ) ) {
next();
}
insert.values.add( parseExpression() );
}
next();
}
}
if ( matches( "WHERE" ) ) {
next();
insert.where = parseExpression();
}
return insert;
}
private void parseKey( List< Name > columns ) {
nextAndMatches( "KEY" );
next();
parseColumns( columns );
}
private void parseOperand( Expression expression ) {
if ( matches( "CASE" ) ) {
Case caseExpr = new Case();
next();
if ( !matches( "WHEN" ) ) {
caseExpr.expression = parseExpression();
next();
}
while ( matches( "WHEN" ) ) {
When when = new When();
caseExpr.operands.add( when );
next();
when.condition = parseExpression();
next();
when.then = parseExpression();
if ( matches( "ELSE" ) ) {
next();
when.elseExpression = parseExpression();
}
}
expression.operands.add( caseExpr );
next();
} else if ( matches( "(" ) ) {
if ( !expression.operators.isEmpty() && match( expression.operators.get( 0 ), "IN", "NOT IN" ) ) {
List< Object > exprs = new ArrayList< Object >();
next();
while ( !matches( ")" ) ) {
if ( matches( "," ) ) {
next();
}
exprs.add( parseExpression() );
}
expression.operands.add( exprs );
} else {
next();
expression.operands.add( parseExpression() );
}
next();
} else if ( matches( "NOT", "EXISTS", "ANY", "ALL", "SOME", "!", "~", "-" ) ) {
if ( nextMatches( "EXISTS" ) ) {
appendNext();
}
if ( matches( "EXISTS", "NOT EXISTS", "ANY", "ALL", "SOME" ) ) {
expression.operators.add( token );
nextAndMatches( "(" );
expression.operands.add( parseExpression() );
}
} else if ( matches( "{" ) ) {
nextAndMatches( "fn" );
OdbcFunction fn = new OdbcFunction();
expression.operands.add( fn );
next();
fn.function = parseFunction();
next();
} else if ( nextMatches( "(" ) ) {
expression.operands.add( parseFunction() );
} else {
expression.operands.add( new Name( token ) );
next();
}
}
private Object parseOperator( Expression expression ) {
if ( endOfExpression() ) {
return expression;
}
int precedence = precedence( expression );
if ( precedence < 0 ) {
return expression;
}
expression.operators.add( token );
next();
parseOperand( expression );
if ( endOfExpression() ) {
return expression;
}
if ( match( expression.operators.get( 0 ), "BETWEEN" ) ) {
expression.operators.add( token );
next();
if ( matches( "(" ) ) {
expression.operands.add( next() );
nextAndMatches( ")" );
} else {
expression.operands.add( token );
}
next();
if ( endOfExpression() ) {
return expression;
}
}
if ( precedence < precedence( expression ) ) {
Expression expr = new Expression();
expr.operands.add( expression.operands.remove( expression.operands.size() - 1 ) );
expression.operands.add( expr );
expr.operators.add( token );
next();
parseOperand( expr );
if ( endOfExpression() ) {
return expression;
}
}
Expression expr = expression;
expression = new Expression();
expression.operands.add( expr );
return parseOperator( expression );
}
private Select parseSelect() {
Select select = new Select();
if ( nextMatches( "DISTINCT" ) ) {
select.distinct = true;
next();
}
while ( !matches( "FROM", ";" ) ) {
next();
select.columns.add( parseAliasable( parseExpression() ) );
}
if ( matches( "FROM" ) ) {
do {
next();
From from = new From();
select.froms.add( from );
from.aliasable = parseAliasable();
while ( matches( "LEFT", "OUTER", "INNER", "CROSS" ) ) {
Join join = new Join();
from.joins.add( join );
if ( matches( "LEFT" ) ) {
join.left = true;
next();
}
join.type = token;
nextAndMatches( "JOIN" );
next();
join.table = parseAliasable();
if ( matches( "ON" ) ) {
next();
join.on = parseExpression();
}
}
} while ( matches( "," ) );
}
if ( matches( "WHERE" ) ) {
next();
select.where = parseExpression();
}
if ( matches( "GROUP" ) ) {
nextAndMatches( "BY" );
select.groupBy = next();
next();
}
if ( matches( "HAVING" ) ) {
next();
select.having = parseExpression();
}
if ( matches( "ORDER" ) ) {
nextAndMatches( "BY" );
do {
OrderBy orderBy = new OrderBy();
select.orderBy.add( orderBy );
orderBy.column = next();
next();
if ( matches( "ASC" ) ) {
next();
} else if ( matches( "DESC" ) ) {
orderBy.descending = true;
next();
}
} while ( matches( "," ) );
}
if ( matches( "LIMIT" ) ) {
next();
select.limit = parseExpression();
}
if ( matches( "OFFSET" ) ) {
next();
select.offset = parseExpression();
}
if ( matches( "FOR" ) ) {
nextAndMatches( "UPDATE" );
select.forUpdate = true;
next();
}
if ( matches( "UNION" ) ) {
next();
if ( matches( "ALL" ) ) {
select.unionAll = true;
next();
}
select.union = parseSelect();
}
return select;
}
private Sequence parseSequence() {
Sequence sequence = new Sequence();
sequence.name = new Name( next() );
next();
if ( matches( "START" ) ) {
nextAndMatches( "WITH" );
sequence.start = Integer.parseInt( next() );
next();
}
if ( matches( "INCREMENT" ) ) {
nextAndMatches( "BY" );
sequence.increment = Integer.parseInt( next() );
}
return sequence;
}
private Table parseTable() {
Table table = new Table();
if ( matches( "CACHED" ) ) {
table.cached = true;
next();
}
if ( matches( "LOCAL", "GLOBAL" ) ) {
table.temporaryType = token;
nextAndMatches( "TEMPORARY" );
}
if ( matches( "TEMPORARY" ) ) {
table.temporary = true;
nextAndMatches( "TABLE" );
}
if ( matches( "TABLE" ) ) {
next();
}
if ( matches( "IF" ) ) {
table.ifNotExists = true;
nextAndMatches( "NOT", "EXISTS" );
next();
}
table.name = new Name( token );
tablesByName.put( table.name.unquoted(), table );
for ( nextAndMatches( "(" ); !matches( ")" ); ) {
next();
if ( matches( "PRIMARY" ) ) {
PrimaryKey key = new PrimaryKey();
table.constraints.add( key );
parseKey( key.columns );
next();
} else if ( matches( "CHECK" ) ) {
table.constraints.add( parseCheck() );
} else {
Column column = new Column();
table.columns.add( column );
column.name = new Name( token );
column.type = next();
next();
if ( matches( "(" ) ) {
column.length = Integer.parseInt( next() );
next();
if ( matches( "," ) ) {
column.decimals = Integer.parseInt( next() );
nextAndMatches( ")" );
}
next();
}
if ( matches( "NOT" ) ) {
nextAndMatches( "NULL" );
column.notNull = true;
next();
}
if ( matches( "CHECK" ) ) {
column.check = parseCheck();
}
if ( matches( "GENERATED" ) ) {
nextAndMatches( "BY", "DEFAULT", "AS", "IDENTITY" );
column.generatedByDefaultAsIdentity = true;
next();
}
}
}
next();
if ( matches( "ON" ) ) {
nextAndMatches( "COMMIT" );
table.onCommit = next();
for ( next(); !matches( ";" ); next() ) {
table.onCommit += ' ' + token;
}
}
return table;
}
private Update parseUpdate() {
Update update = new Update();
update.table = next();
nextAndMatches( "SET" );
do {
next();
update.sets.add( parseExpression() );
} while ( matches( "," ) );
if ( matches( "WHERE" ) ) {
next();
update.where = parseExpression();
}
return update;
}
private int precedence( Expression expression ) {
if ( matches( "NOT" ) && nextMatches( "LIKE", "IN", "BETWEEN" ) ) {
appendNext();
} else {
while ( nextMatches( "=", "<", ">", "|", "&" ) ) {
String oldToken = token;
token = oldToken + next();
}
}
int precedence = 0;
if ( matches( ":=" ) ) {
return precedence;
}
precedence++;
if ( matches( "OR", "||" ) ) {
return precedence;
}
precedence++;
if ( matches( "XOR" ) ) {
return precedence;
}
precedence++;
if ( matches( "AND", "&&" ) ) {
return precedence;
}
precedence++;
if ( matches( "NOT" ) ) {
return precedence;
}
precedence++;
if ( matches( "BETWEEN", "NOT BETWEEN", "CASE" ) ) {
return precedence;
}
precedence++;
if ( matches(
"=",
"<",
">",
"<=",
">=",
"!=",
"<>",
"<=>",
"IN",
"NOT IN",
"LIKE",
"NOT LIKE",
"ESCAPE",
"IS",
"EXISTS",
"NOT EXISTS",
"ANY",
"ALL",
"SOME" ) ) {
return precedence;
}
precedence++;
if ( matches( "|" ) ) {
return precedence;
}
precedence++;
if ( matches( "&" ) ) {
return precedence;
}
precedence++;
if ( matches( "<<", ">>" ) ) {
return precedence;
}
precedence++;
if ( matches( "+" ) || ( matches( "-" ) && !expression.operands.isEmpty() ) ) {
return precedence;
}
precedence++;
if ( matches( "*", "/", "%", "MOD", "DIV" ) ) {
return precedence;
}
precedence++;
if ( matches( "^" ) ) {
return precedence;
}
precedence++;
if ( matches( "-", "~" ) ) {
return precedence;
}
precedence++;
if ( matches( "!" ) ) {
return precedence;
}
return -1;
}
private boolean quoted( String token ) {
return token.startsWith( "'" ) || token.startsWith( "\"" );
}
}

View File

@ -1,27 +0,0 @@
package org.hibernate.testing.sqlparser;
import java.util.ArrayList;
import java.util.List;
public class Update extends Statement {
public String table;
public List< Object > sets = new ArrayList< Object >();
public Object where;
/**
* {@inheritDoc}
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder( "UPDATE " );
builder.append( table ).append( " SET " );
listToString( builder, sets );
if ( where != null ) {
builder.append( " WHERE " ).append( where );
}
return builder.toString();
}
}