Looking for feedback on Statement refactoring

View: New views
1 Messages — Rating Filter:   Alert me  

Looking for feedback on Statement refactoring

by Voxland, Nathan :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Some parts of this message have been removed. Learn more about Nabble's security policy.

I am starting the extensibility refactoring at the *Statement class level and moving up from there.  I committed an initial version of what I am thinking to trunk, and am looking for feedback.

 

Renamed liquibase.database.sql package to liquibase.database.statement.

We may want to support non-“sql” statements in the future, so the name change seemeded best

 

Separated out the creation of SQL from the *Statement class. 

Before each Statement class had a “String getSqlStatement(Database)” method that would create a string containing the database specific sql required to run the statement.  Now, there is a liquibase.database.statement.generator package that contains classes that take a Statement instance containing the information about a statement (table name, column names, etc) and converts them into SQL strings. 

 

There is now a  SqlGeneratorFactory singleton with a  “SqlGenerator getBestGenerator(SqlStatement statement, Database database)”  method.  When we get to the point where we have Statement classes that need to be executed, liquibase will call this method to get the correct SqlGenerator instance for a given statement and database and use that instance to create the sql to execute.  (this change has not been made yet)  .

 

The SqlGeneratorFactory has a list of all SqlGenerators available.  That list is built up via a reflection package search in the liquibase.database.statement.generator package and sub-packages, or via a SqlGeneratorFactory.register() method.  There will be a standard package of liquibase.database.statement.generator.ext that can be used by 3rd parties to have their statements registered automatically without using the register ethod.

 

I have started breaking up the Statement classes (although I haven’t removed the old getSqlStatement methods yet).  In the liquibase.database.statement.generator package there is an SqlGenerator interface which provides a getSpecializationLevel method, an isValid method, and a generateSql method.   In the getBestGenerator() method, the SqlGeneratorFactory will loop through all known generators and call the isValid method to determine if a given SqlGenerator would work for that combination of database and statement.  Once it has a list of good generators, it will call the getSpecialiazationLevel() method which returns an int corresponding to how “good” that generator is for that statement/database combo.  There should usually be a default generator for each statement that will have a level of 1.  There will often be database-specific versions of the generator that return a level of 5.  3rd party Statement generators can return any level they want to override the built in ones or other 3rd party generators.  If no generators are found for a database/statement combo, tyere is a NotImplementedGenerator that is returned that simply throws an exception.

 

New generateSql  method returns an array. 

Before, there was a one-to-one correspondence between a Statement class, and the SQL generated.  I think there are situations, however, where a Statement may generate multiple SQL commands and so we should allow the return of an array.

 

generateSql returns instances of “Sql”, not Strings

This is the part I go back and forth on.  Since the goal of this refactoring is to give 3rd party developers an API to code against, I want to do my best to make sure that API is not going to change.  There has been talk in the past about building more flexibility into the system by giving back an syntax tree that can be modified before being executed.  The idea with the Sql interface, is that we would only have an “UnparsedSql” implementation at first, but may make smarter implementations in the future. 

 

The question is: is this too much?  We now have a mechanism where we can override what sql string is generated for a particular database and/or statement,  is that going to cover all the low-level-sql-tweaking that we would need?  I feel like the Sql interface may allow some sort of cross-cutting sql modification, or similar, but it is just a half-thought out idea. 

 

Improving tests

I am going to repurpose the test framework around the Statement classes that actually run them against the database.  This has not been done yet.

 

Below is some code samples, but feel free to take a look at the new generator package and give me feedback.  Like I said, the goal is to open this up to 3rd party developers, and so I want to make sure it is a good, workable, and stable API.

 

Nathan

 

public class AddAutoIncrementGeneratorDefault implements SqlGenerator<AddAutoIncrementStatement> {

 

    public int getSpecializationLevel() {

        return SPECIALIZATION_LEVEL_DEFAULT;

    }

 

    public boolean isValid(AddAutoIncrementStatement statement, Database database) {

        return database.supportsAutoIncrement();

    }

 

    public Sql[] generateSql(AddAutoIncrementStatement statement, Database database) throws LiquibaseException {

        return new Sql[] {

                new UnparsedSql("ALTER TABLE " + database.escapeTableName(statement.getSchemaName(), statement.getTableName()) + " MODIFY " + database.escapeColumnName(statement.getSchemaName(), statement.getTableName(), statement.getColumnName()) + " " + statement.getColumnDataType() + " AUTO_INCREMENT")

        };

    }

}

 

public class AddAutoIncrementGeneratorHsql implements SqlGenerator<AddAutoIncrementStatement> {

 

    public int getSpecializationLevel() {

        return SPECIALIZATION_LEVEL_DATABASE_SPECIFIC;

    }

 

    public boolean isValid(AddAutoIncrementStatement statement, Database database) {

        return database instanceof HsqlDatabase;

    }

    public Sql[] generateSql(AddAutoIncrementStatement statement, Database database) throws LiquibaseException {

        return new Sql[]{

                new UnparsedSql("ALTER TABLE " + database.escapeTableName(statement.getSchemaName(), statement.getTableName()) + " ALTER COLUMN " + database.escapeColumnName(statement.getSchemaName(), statement.getTableName(), statement.getColumnName()) + " " + statement.getColumnDataType() + " GENERATED BY DEFAULT AS IDENTITY IDENTITY")

        };

    }

}

 

 

SqlGeneratorFactory.getInstance().register(new CustomMysqlCreateTableGenerator());

 

SqlGenerator bestSqlGenerator = SqlGeneratorFactory.getInstance().getBestGenerator(new AddAutoIncrementStatement(null, "person", "name", "varchar(255)"), database);

 

 


------------------------------------------------------------------------------
Stay on top of everything new and different, both inside and
around Java (TM) technology - register by April 22, and save
$200 on the JavaOne (SM) conference, June 2-5, 2009, San Francisco.
300 plus technical and hands-on sessions. Register today.
Use priority code J9JMT32. http://p.sf.net/sfu/p
_______________________________________________
Liquibase-devel mailing list
Liquibase-devel@...
https://lists.sourceforge.net/lists/listinfo/liquibase-devel