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"});
        
        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);
            }
        }
        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"});

        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);
            }
        }
        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());

            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);
                }
            }

            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 = ((JdbcConnection) database.getConnection()).getUnderlyingConnection().createStatement();
    	        ResultSet 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);
    	        }
    	        if (rs!=null) {
    	        	rs.close();
    	        }
    	        selectStatement.close();
        	}
        } else {
        	// ...if it is no SQLite database
	        Statement selectStatement = ((JdbcConnection) database.getConnection()).getUnderlyingConnection().createStatement();
	        ResultSet rs = databaseMetaData.getColumns(database.convertRequestedSchemaToCatalog(schema), database.convertRequestedSchemaToSchema(schema), null, null);
	        while (rs.next()) {
	            readColumnInfo(snapshot,  schema,rs);
	        }
	        rs.close();
	        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 = 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 = ((JdbcConnection) database.getConnection()).getUnderlyingConnection().createStatement();
				ResultSet 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);
            		}
            	}
            	if (rs_2!=null) {
            		rs_2.close();
            	}
            	if (statement_2 != null) {
	                statement_2.close();
	            }
            	
        	}
        	if (rs!=null) {
        		rs.close();
        	}
            if (statement != null) {
                statement.close();
            }
    
            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