Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dry run failing on JDBC defined data types #2425

Closed
jwheeler31 opened this issue Jul 2, 2019 · 2 comments

Comments

@jwheeler31
Copy link

commented Jul 2, 2019

Which version and edition of Flyway are you using?

6.0.0-beta

If this is not the latest version, can you reproduce the issue with the latest one as well?

(Many bugs are fixed in newer releases and upgrading will often resolve the issue)

Which client are you using? (Command-line, Java API, Maven plugin, Gradle plugin)

Command-line/Java API

Which database are you using (type & version)?

MariaDB 10.2.6

Which operating system are you using?

macOS Mojave 10.14.5, CentOS 7

What did you do?

(Please include the content causing the issue, any relevant configuration settings, the SQL statement that failed (if relevant) and the command you ran.)
Dry run on a Java migration (verbatim code below):

public class V120__PopulateMCSTowerTable extends BaseJavaMigration
{
    //---------------------------------------------------------------------
    // Constants
    //---------------------------------------------------------------------

    private static String INSERT_MCS_TOWER_SQL =
        "INSERT INTO mcs_tower(mcs_tower_pk, tower_id, idle_bot_priority, max_idle_bot_pct) " +
        "VALUES(?,?,?,?)";

    private static String QUERY_TOWER_TABLE_SQL =
        "SELECT tower_pk, tower_id, bay FROM TOWER";

    //---------------------------------------------------------------------
    // Attributes
    //---------------------------------------------------------------------
    //---------------------------------------------------------------------
    // Constructors
    //---------------------------------------------------------------------
    //---------------------------------------------------------------------
    // BaseJavaMigration Methods
    //---------------------------------------------------------------------

    /**
     * Executes this migration. The execution will automatically take place
     * within a transaction, when the underlying database supports it and the
     * canExecuteInTransaction returns {@code true}.
     *
     * @param context The context relevant for this migration, containing things
     *                like the JDBC connection to use and the current Flyway
     *                configuration.
     * @throws Exception when the migration failed.
     */
    @Override
    public void migrate(Context context) throws Exception
    {
        Connection c = context.getConnection();

        PreparedStatement ps = null;
        PreparedStatement ps2 = null;
        ResultSet rs = null;

        try
        {
            ps = c.prepareStatement(QUERY_TOWER_TABLE_SQL);
            ps2 = c.prepareStatement(INSERT_MCS_TOWER_SQL);

            rs = ps.executeQuery();
            while (rs.next())
            {
                long pk = rs.getLong(1);
                String towerID = rs.getString(2);
                int bay = rs.getInt(3);

                ps2.setLong(1, pk);
                ps2.setString(2, towerID);

                if (bay == 1)
                {
                    ps2.setInt(3, MCSTowerInfo.DEFAULT_IDLE_BOT_PRIORITY_DECK_BAY);
                    ps2.setInt(4, MCSTowerInfo.DEFAULT_MAX_IDLE_BOT_PCT_DECK_BAY);
                }
                else
                {
                    ps2.setInt(3, MCSTowerInfo.DEFAULT_IDLE_BOT_PRIORITY);
                    ps2.setInt(4, MCSTowerInfo.DEFAULT_MAX_IDLE_BOT_PCT);
                }

                ps2.executeUpdate();
            }
        }
        finally
        {
            if (rs != null)
            {
                rs.close();
            }
            if (ps2 != null)
            {
                ps2.close();
            }
            if (ps != null)
            {
                ps.close();
            }
        }
    }
}
What did you expect to see?

A successful migration.

What did you see instead?

A failure to capture PreparedStatement#setLong(int parameterIndex, long x)

Migrating schema `racedb` to version 120 - PopulateMCSTowerTable
DEBUG: Rolling back transaction...
DEBUG: Transaction rolled back
ERROR: Migration of schema `racedb` to version 120 - PopulateMCSTowerTable failed! Please restore backups and roll back database and code!
DEBUG: Memory usage: 16 of 150M
ERROR: Unexpected error
org.flywaydb.core.api.FlywayException: org.flywaydb.core.internal.command.DbMigrate$FlywayMigrateException: Migration failed !
       at org.flywaydb.core.internal.schemahistory.pro.InMemorySchemaHistory.lock(InMemorySchemaHistory.java:61)
       at org.flywaydb.core.internal.command.DbMigrate.migrateAll(DbMigrate.java:159)
       at org.flywaydb.core.internal.command.DbMigrate.migrate(DbMigrate.java:137)
       at org.flywaydb.core.Flyway$1.execute(Flyway.java:182)
       at org.flywaydb.core.Flyway$1.execute(Flyway.java:142)
       at org.flywaydb.core.Flyway.execute(Flyway.java:504)
       at org.flywaydb.core.Flyway.migrate(Flyway.java:142)
       at org.flywaydb.commandline.Main.executeOperation(Main.java:165)
       at org.flywaydb.commandline.Main.main(Main.java:112)
Caused by: org.flywaydb.core.internal.command.DbMigrate$FlywayMigrateException: Migration failed !
       at org.flywaydb.core.internal.command.DbMigrate.doMigrateGroup(DbMigrate.java:368)
       at org.flywaydb.core.internal.command.DbMigrate.access$200(DbMigrate.java:54)
       at org.flywaydb.core.internal.command.DbMigrate$3.call(DbMigrate.java:282)
       at org.flywaydb.core.internal.jdbc.TransactionTemplate.execute(TransactionTemplate.java:74)
       at org.flywaydb.core.internal.command.DbMigrate.applyMigrations(DbMigrate.java:279)
       at org.flywaydb.core.internal.command.DbMigrate.migrateGroup(DbMigrate.java:244)
       at org.flywaydb.core.internal.command.DbMigrate.access$100(DbMigrate.java:54)
       at org.flywaydb.core.internal.command.DbMigrate$2.call(DbMigrate.java:162)
       at org.flywaydb.core.internal.command.DbMigrate$2.call(DbMigrate.java:159)
       at org.flywaydb.core.internal.schemahistory.pro.InMemorySchemaHistory.lock(InMemorySchemaHistory.java:57)
       ... 8 more
Caused by: org.flywaydb.core.api.FlywayException: Migration failed !
       at org.flywaydb.core.internal.resolver.java.JavaMigrationExecutor.execute(JavaMigrationExecutor.java:62)
       at org.flywaydb.core.internal.command.DbMigrate.doMigrateGroup(DbMigrate.java:365)
       ... 17 more
Caused by: java.util.MissingFormatArgumentException: Format specifier '%s'
       at java.util.Formatter.format(Formatter.java:2519)
       at java.util.Formatter.format(Formatter.java:2455)
       at java.lang.String.format(String.java:2940)
       at org.flywaydb.core.internal.jdbc.pro.DryRunStatementInterceptor.prepareStatement(DryRunStatementInterceptor.java:128)
       at org.flywaydb.core.internal.jdbc.pro.DryRunStatementInterceptor.interceptPreparedStatement(DryRunStatementInterceptor.java:112)
       at org.flywaydb.core.internal.jdbc.pro.DryRunJdbcProxies$3.invoke(DryRunJdbcProxies.java:120)
       at com.sun.proxy.$Proxy4.executeUpdate(Unknown Source)
       at com.romaric.mcs.mgr.db.migration.V120__PopulateMCSTowerTable.migrate(V120__PopulateMCSTowerTable.java:105)
       at org.flywaydb.core.internal.resolver.java.JavaMigrationExecutor.execute(JavaMigrationExecutor.java:48)
       ... 18 more

The best that I can tell, a Dry Run only catches [ null | int | boolean | String ] as demonstrated in DryRunJdbcProxies#createPreparedStatementProxy(final ClassLoader classLoader, final PreparedStatement statement, final String sql, final DryRunStatementInterceptor dryRunStatementInterceptor), resulting in only 3 parameters in the params List instead of the required 4. The failing Flyway source code is below:

private final List<Object> params = new ArrayList<>();

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            String methodName = method.getName();
            if ("execute".equals(methodName)
                    || ("executeQuery".equals(methodName) && !QUERY_REGEX.matcher(sql).matches())
                    || "executeUpdate".equals(methodName)
                    || "executeLargeUpdate".equals(methodName)) {
                if (args == null) {
                    dryRunStatementInterceptor.interceptPreparedStatement(sql, params);
                } else {
                    dryRunStatementInterceptor.interceptStatement((String) args[0]);
                }
                if ("execute".equals(methodName)) {
                    return false;
                }
                if ("executeQuery".equals(methodName)) {
                    return createDummyResultSet(classLoader);
                }
                return 0;
            } else if ("setNull".equals(methodName)) {
                params.add(null);
            } else if ("setInt".equals(methodName)
                    || "setBoolean".equals(methodName)
                    || "setString".equals(methodName)) {
                params.add(args[1]);
            }

            return method.invoke(statement, args);
        } catch (InvocationTargetException e) {
            throw e.getCause();
        }
    }
});

Does the 6.0.0 release plan to support all 36 JDBC Types, or only a subset?

@jwheeler31 jwheeler31 changed the title Dry run failing on supported JDBC data types Dry run failing on JDBC defined data types Jul 2, 2019

@axelfontaine axelfontaine added this to the Flyway 6.0.0 milestone Jul 2, 2019

@jwheeler31

This comment has been minimized.

Copy link
Author

commented Jul 2, 2019

Addendum: this also exists on MariaDB 10.3.10.

@juliahayward

This comment has been minimized.

Copy link
Member

commented Aug 8, 2019

For the immediate 6.0.0 release, expanding to handle double | float | long | nstring | short | URL. More work will be needed for the other types ensuring that string representations in the dry run output are handled correctly, and also checking the case when an unexpected number of setXxx calls are made.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.