mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-16 16:15:06 +00:00
HHH-8090: Created testing framework for SQL output and implementation in core
This commit is contained in:
parent
ca4516a4e9
commit
6f7c7d2e99
@ -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() );
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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 );
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
@ -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 );
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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();
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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}
|
||||
*
|
@ -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() );
|
||||
}
|
@ -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 );
|
||||
}
|
||||
}
|
@ -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 );
|
||||
}
|
@ -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 );
|
||||
}
|
||||
}
|
@ -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();
|
@ -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";
|
||||
}
|
||||
}
|
@ -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 );
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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( " )" );
|
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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 );
|
||||
}
|
@ -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 );
|
||||
}
|
||||
}
|
@ -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 );
|
||||
}
|
@ -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 ) );
|
||||
}
|
||||
}
|
@ -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() + " }";
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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 > {
|
||||
}
|
@ -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" );
|
||||
}
|
@ -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 ) ) + " )";
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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}
|
@ -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 );
|
||||
}
|
||||
}
|
@ -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 );
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
@ -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();
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.hibernate.testing.sqlparser;
|
||||
package org.hibernate.testing.sql;
|
||||
|
||||
/**
|
||||
*
|
@ -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}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
@ -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( "\"" );
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user