All Downloads are FREE. Search and download functionalities are using the official Maven repository.

liquibase.snapshot.jvm.SQLiteDatabaseSnapshotGenerator Maven / Gradle / Ivy

There is a newer version: 4.30.0
Show newest version
package liquibase.snapshot.jvm;

import liquibase.database.Database;
import liquibase.database.jvm.JdbcConnection;
import liquibase.database.typeconversion.TypeConverterFactory;
import liquibase.database.core.SQLiteDatabase;
import liquibase.database.structure.*;
import liquibase.exception.DatabaseException;
import liquibase.executor.ExecutorService;
import liquibase.logging.LogFactory;
import liquibase.snapshot.DatabaseSnapshot;
import liquibase.statement.core.GetViewDefinitionStatement;
import liquibase.statement.core.SelectSequencesStatement;
import liquibase.util.StringUtils;

import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.ParseException;
import java.util.*;

public class SQLiteDatabaseSnapshotGenerator extends JdbcDatabaseSnapshotGenerator {

    /**
     * Creates an empty database snapshot
     */
    public SQLiteDatabaseSnapshotGenerator() {
    }

    public boolean supports(Database database) {
        return database instanceof SQLiteDatabase;
    }

    public int getPriority(Database database) {
        return PRIORITY_DATABASE;
    }

    @Override
    protected void readTables(DatabaseSnapshot snapshot, String schema, DatabaseMetaData databaseMetaData) throws SQLException, DatabaseException {

        Database database = snapshot.getDatabase();

        updateListeners("Reading tables for " + database.toString() + " ...");
        ResultSet rs = databaseMetaData.getTables(
                database.convertRequestedSchemaToCatalog(schema),
                database.convertRequestedSchemaToSchema(schema),
                null,
                new String[]{"TABLE", "VIEW"});

        try {
            while (rs.next()) {
                String type = rs.getString("TABLE_TYPE");
                String name = rs.getString("TABLE_NAME");
                String schemaName = rs.getString("TABLE_SCHEM");
                String catalogName = rs.getString("TABLE_CAT");
                String remarks = rs.getString("REMARKS");

                if (database.isSystemTable(catalogName, schemaName, name) ||
                        database.isLiquibaseTable(name) ||
                        database.isSystemView(catalogName, schemaName, name)) {
                    continue;
                }

                if ("TABLE".equals(type)) {
                    Table table = new Table(name);
                    table.setRemarks(StringUtils.trimToNull(remarks));
                    table.setDatabase(database);
                    table.setSchema(schemaName);
                    snapshot.getTables().add(table);
                } else if ("VIEW".equals(type)) {
                    View view = new View();
                    view.setName(name);
                    view.setSchema(schemaName);
                    try {
                        view.setDefinition(database.
                                getViewDefinition(schema, name));
                    } catch (DatabaseException e) {
                        System.out.println("Error getting view with " + new GetViewDefinitionStatement(schema, name));
                        throw e;
                    }
                    snapshot.getViews().add(view);
                }
            }
        } finally {
            rs.close();
        }
    }

    @Override
    protected void readViews(DatabaseSnapshot snapshot, String schema, DatabaseMetaData databaseMetaData) throws SQLException, DatabaseException {

        Database database = snapshot.getDatabase();

        updateListeners("Reading tables for " + database.toString() + " ...");
        ResultSet rs = databaseMetaData.getTables(
                database.convertRequestedSchemaToCatalog(schema),
                database.convertRequestedSchemaToSchema(schema),
                null,
                new String[]{"TABLE", "VIEW"});

        try {
            while (rs.next()) {
                String type = rs.getString("TABLE_TYPE");
                String name = rs.getString("TABLE_NAME");
                String schemaName = rs.getString("TABLE_SCHEM");
                String catalogName = rs.getString("TABLE_CAT");
                String remarks = rs.getString("REMARKS");

                if (database.isSystemTable(catalogName, schemaName, name) ||
                        database.isLiquibaseTable(name) ||
                        database.isSystemView(catalogName, schemaName, name)) {
                    continue;
                }

                if ("TABLE".equals(type)) {
                    Table table = new Table(name);
                    table.setRemarks(StringUtils.trimToNull(remarks));
                    table.setDatabase(database);
                    table.setSchema(schemaName);
                    snapshot.getTables().add(table);
                } else if ("VIEW".equals(type)) {
                    View view = new View();
                    view.setName(name);
                    view.setSchema(schemaName);
                    try {
                        view.setDefinition(database.
                                getViewDefinition(schema, name));
                    } catch (DatabaseException e) {
                        System.out.println("Error getting view with " + new GetViewDefinitionStatement(schema, name));
                        throw e;
                    }
                    snapshot.getViews().add(view);
                }
            }
        } finally {
            rs.close();
        }
    }

    /**
     * SQLite specific implementation
     */
    @Override
    protected void readForeignKeyInformation(DatabaseSnapshot snapshot, String schema, DatabaseMetaData databaseMetaData) throws DatabaseException, SQLException {
        updateListeners("Reading foreign keys for " + snapshot.getDatabase().toString() + " ...");
        // Foreign keys are not supported in SQLite until now. 
        // ...do nothing here
    }

    /**
     * SQLite specific implementation
     */
    @Override
    protected void readPrimaryKeys(DatabaseSnapshot snapshot, String schema, DatabaseMetaData databaseMetaData) throws DatabaseException, SQLException {
        Database database = snapshot.getDatabase();
        updateListeners("Reading primary keys for " + database.toString() + " ...");

        //we can't add directly to the this.primaryKeys hashSet because adding columns to an exising PK changes the hashCode and .contains() fails
        List foundPKs = new ArrayList();

        for (Table table : snapshot.getTables()) {
            ResultSet rs = databaseMetaData.getPrimaryKeys(database.convertRequestedSchemaToCatalog(schema), database.convertRequestedSchemaToSchema(schema), table.getName());

            try {
                while (rs.next()) {
                    String tableName = rs.getString("TABLE_NAME");
                    String columnName = rs.getString("COLUMN_NAME");
                    short position = rs.getShort("KEY_SEQ");

                    if (!(database instanceof SQLiteDatabase)) {
                        position -= 1;
                    }

                    boolean foundExistingPK = false;
                    for (PrimaryKey pk : foundPKs) {
                        if (pk.getTable().getName().equals(tableName)) {
                            pk.addColumnName(position, columnName);

                            foundExistingPK = true;
                        }
                    }

                    if (!foundExistingPK) {
                        PrimaryKey primaryKey = new PrimaryKey();
                        primaryKey.setTable(table);
                        primaryKey.addColumnName(position, columnName);
                        primaryKey.setName(rs.getString("PK_NAME"));

                        foundPKs.add(primaryKey);
                    }
                }
            } finally {
                rs.close();
            }

        }

        snapshot.getPrimaryKeys().addAll(foundPKs);
    }

    @Override
    protected void readColumns(DatabaseSnapshot snapshot, String schema, DatabaseMetaData databaseMetaData) throws SQLException, DatabaseException {
        Database database = snapshot.getDatabase();
        updateListeners("Reading columns for " + database.toString() + " ...");

        if (database instanceof SQLiteDatabase) {
            // ...work around for SQLite
            for (Table cur_table : snapshot.getTables()) {
                Statement selectStatement = null;
                ResultSet rs = null;
                try {
                    selectStatement = ((JdbcConnection) database.getConnection()).getUnderlyingConnection().createStatement();
                    rs = databaseMetaData.getColumns(database.convertRequestedSchemaToCatalog(schema), database.convertRequestedSchemaToSchema(schema), cur_table.getName(), null);
                    if (rs == null) {
                        rs = databaseMetaData.getColumns(database.convertRequestedSchemaToCatalog(schema), database.convertRequestedSchemaToSchema(schema), cur_table.getName(), null);
                    }
                    while ((rs != null) && rs.next()) {
                        readColumnInfo(snapshot, schema, rs);
                    }
                } finally {
                    if (rs != null) {
                        try {
                            rs.close();
                        } catch (SQLException ignored) {
                        }
                    }
                    if (selectStatement != null) {
                        selectStatement.close();
                    }
                }
            }
        } else {
            // ...if it is no SQLite database
            Statement selectStatement = null;
            ResultSet rs = null;
            try {
                selectStatement = ((JdbcConnection) database.getConnection()).getUnderlyingConnection().createStatement();
                rs = databaseMetaData.getColumns(database.convertRequestedSchemaToCatalog(schema), database.convertRequestedSchemaToSchema(schema), null, null);
                while (rs.next()) {
                    readColumnInfo(snapshot, schema, rs);
                }
            } finally {
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException ignored) {
                    }
                }
                if (selectStatement != null) {
                    selectStatement.close();
                }
            }
        }
    }

    private Column readColumnInfo(DatabaseSnapshot snapshot, String schema, ResultSet rs) throws SQLException, DatabaseException {
        Database database = snapshot.getDatabase();
        Column columnInfo = new Column();

        String tableName = rs.getString("TABLE_NAME");
        String columnName = rs.getString("COLUMN_NAME");
        String schemaName = rs.getString("TABLE_SCHEM");
        String catalogName = rs.getString("TABLE_CAT");

        String upperCaseTableName = tableName.toUpperCase(Locale.ENGLISH);

        if (database.isSystemTable(catalogName, schemaName, upperCaseTableName) ||
                database.isLiquibaseTable(upperCaseTableName)) {
            return null;
        }

        Table table = snapshot.getTable(tableName);
        if (table == null) {
            View view = snapshot.getView(tableName);
            if (view == null) {
                LogFactory.getLogger().debug("Could not find table or view " + tableName + " for column " + columnName);
                return null;
            } else {
                columnInfo.setView(view);
                view.getColumns().add(columnInfo);
            }
        } else {
            columnInfo.setTable(table);
            table.getColumns().add(columnInfo);
        }

        columnInfo.setName(columnName);
        columnInfo.setDataType(rs.getInt("DATA_TYPE"));
        columnInfo.setColumnSize(rs.getInt("COLUMN_SIZE"));
        columnInfo.setDecimalDigits(rs.getInt("DECIMAL_POINTS"));
        Object defaultValue = rs.getObject("COLUMN_DEF");
        try {
            columnInfo.setDefaultValue(TypeConverterFactory.getInstance().findTypeConverter(database).convertDatabaseValueToObject(defaultValue, columnInfo.getDataType(), columnInfo.getColumnSize(), columnInfo.getDecimalDigits(), database));
        } catch (ParseException e) {
            throw new DatabaseException(e);
        }

        int nullable = rs.getInt("NULLABLE");
        if (nullable == DatabaseMetaData.columnNoNulls) {
            columnInfo.setNullable(false);
        } else if (nullable == DatabaseMetaData.columnNullable) {
            columnInfo.setNullable(true);
        }

        columnInfo.setPrimaryKey(snapshot.isPrimaryKey(columnInfo));
        columnInfo.setAutoIncrement(isColumnAutoIncrement(database, schema, tableName, columnName));
        columnInfo.setTypeName(TypeConverterFactory.getInstance().findTypeConverter(database).getDataType(rs.getString("TYPE_NAME"), columnInfo.isAutoIncrement()).toString());

        return columnInfo;
    }

    @Override
    protected void readIndexes(DatabaseSnapshot snapshot, String schema, DatabaseMetaData databaseMetaData) throws DatabaseException, SQLException {
        Database database = snapshot.getDatabase();
        updateListeners("Reading indexes for " + database.toString() + " ...");

        for (Table table : snapshot.getTables()) {
            ResultSet rs = null;
            Statement statement = null;
            Map indexMap;
            try {
                indexMap = new HashMap();

                // for the odbc driver at http://www.ch-werner.de/sqliteodbc/
                // databaseMetaData.getIndexInfo is not implemented
                statement = ((JdbcConnection) database.getConnection()).getUnderlyingConnection().createStatement();
                String sql = "PRAGMA index_list(" + table.getName() + ");";
                try {
                    rs = statement.executeQuery(sql);
                } catch (SQLException e) {
                    if (!e.getMessage().equals("query does not return ResultSet")) {
                        System.err.println(e);
//            			throw e;
                    }
                }
                while ((rs != null) && rs.next()) {
                    String index_name = rs.getString("name");
                    boolean index_unique = rs.getBoolean("unique");
                    sql = "PRAGMA index_info(" + index_name + ");";
                    Statement statement_2 = null;
                    ResultSet rs_2 = null;
                    try {
                        statement_2 = ((JdbcConnection) database.getConnection()).getUnderlyingConnection().createStatement();
                        rs_2 = statement_2.executeQuery(sql);
                        while ((rs_2 != null) && rs_2.next()) {
                            int index_column_seqno = rs_2.getInt("seqno");
//                		int index_column_cid = rs.getInt("cid");
                            String index_column_name = rs_2.getString("name");
                            if (index_unique) {
                                Column column = snapshot.getColumn(table.getName(), index_column_name);
                                column.setUnique(true);
                            } else {
                                Index indexInformation;
                                if (indexMap.containsKey(index_name)) {
                                    indexInformation = indexMap.get(index_name);
                                } else {
                                    indexInformation = new Index();
                                    indexInformation.setTable(table);
                                    indexInformation.setName(index_name);
                                    indexInformation.setFilterCondition("");
                                    indexMap.put(index_name, indexInformation);
                                }
                                indexInformation.getColumns().add(index_column_seqno, index_column_name);
                            }
                        }
                    } finally {
                        if (rs_2 != null) {
                            try {
                                rs_2.close();
                            } catch (SQLException ignored) {
                            }
                        }
                        if (statement_2 != null) {
                            try {
                                statement_2.close();
                            } catch (SQLException ignored) {
                            }
                        }
                    }

                }
            } finally {
                if (rs != null) {
                    try {
                        rs.close();
                    } catch (SQLException ignored) { }
                }
                if (statement != null) {
                    try {
                        statement.close();
                    } catch (SQLException ignored) { }
                }
            }

            for (Map.Entry entry : indexMap.entrySet()) {
                snapshot.getIndexes().add(entry.getValue());
            }
        }

        //remove PK indexes
        Set indexesToRemove = new HashSet();
        for (Index index : snapshot.getIndexes()) {
            for (PrimaryKey pk : snapshot.getPrimaryKeys()) {
                if (index.getTable().getName().equalsIgnoreCase(pk.getTable().getName())
                        && index.getColumnNames().equals(pk.getColumnNames())) {
                    indexesToRemove.add(index);
                }
            }
        }
        snapshot.getIndexes().removeAll(indexesToRemove);
    }

    @Override
    protected void readSequences(DatabaseSnapshot snapshot, String schema, DatabaseMetaData databaseMetaData) throws DatabaseException {
        Database database = snapshot.getDatabase();
        updateListeners("Reading sequences for " + database.toString() + " ...");

        String convertedSchemaName = database.convertRequestedSchemaToSchema(schema);

        if (database.supportsSequences()) {
            //noinspection unchecked
            List sequenceNamess = (List) ExecutorService.getInstance().getExecutor(database).queryForList(new SelectSequencesStatement(schema), String.class);


            for (String sequenceName : sequenceNamess) {
                Sequence seq = new Sequence();
                seq.setName(sequenceName.trim());
                seq.setName(convertedSchemaName);

                snapshot.getSequences().add(seq);
            }
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy