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

net.java.ao.schema.ddl.SchemaReader Maven / Gradle / Ivy

/*
 * Copyright 2007 Daniel Spiewak
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at
 * 
 *	    http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.java.ao.schema.ddl;

import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import net.java.ao.Common;
import net.java.ao.DatabaseProvider;
import net.java.ao.SchemaConfiguration;
import net.java.ao.schema.helper.DatabaseMetaDataReader;
import net.java.ao.schema.helper.DatabaseMetaDataReaderImpl;
import net.java.ao.schema.helper.Field;
import net.java.ao.schema.helper.ForeignKey;
import net.java.ao.sql.SqlUtils;

import java.sql.*;
import java.util.*;

import static com.google.common.collect.Lists.newArrayList;

/**
 * WARNING: Not part of the public API.  This class is public only
 * to allow its use within other packages in the ActiveObjects library.
 *
 * @author Daniel Spiewak
 */
public final class SchemaReader
{
    /**
     * Currently doesn't account for:
     *
     * 
    *
  • setOnUpdate
  • *
  • setUnique
  • *
*/ public static DDLTable[] readSchema(DatabaseProvider provider, SchemaConfiguration schemaConfiguration) throws SQLException { return readSchema(provider, schemaConfiguration, true); } public static DDLTable[] readSchema(DatabaseProvider provider, SchemaConfiguration schemaConfiguration, final boolean includeForeignKeys) throws SQLException { Connection connection = null; try { connection = provider.getConnection(); return readSchema(connection, provider, schemaConfiguration, includeForeignKeys); } finally { SqlUtils.closeQuietly(connection); } } public static DDLTable[] readSchema(Connection connection, DatabaseProvider provider, SchemaConfiguration schemaConfiguration, final boolean includeForeignKeys) throws SQLException { final DatabaseMetaDataReader databaseMetaDataReader = new DatabaseMetaDataReaderImpl(provider, schemaConfiguration); final DatabaseMetaData databaseMetaData = connection.getMetaData(); final List tables = newArrayList(Iterables.transform(databaseMetaDataReader.getTableNames(databaseMetaData), new Function() { public DDLTable apply(String tableName) { return readTable(databaseMetaDataReader, databaseMetaData, tableName, includeForeignKeys); } })); return tables.toArray(new DDLTable[tables.size()]); } private static DDLTable readTable(DatabaseMetaDataReader databaseMetaDataReader, DatabaseMetaData databaseMetaData, String tableName, boolean includeForeignKeys) { DDLTable table = new DDLTable(); table.setName(tableName); final List fields = readFields(databaseMetaDataReader, databaseMetaData, tableName); table.setFields(fields.toArray(new DDLField[fields.size()])); if (includeForeignKeys) { final List foreignKeys = readForeignKeys(databaseMetaDataReader, databaseMetaData, tableName); table.setForeignKeys(foreignKeys.toArray(new DDLForeignKey[foreignKeys.size()])); } return table; } private static List readFields(DatabaseMetaDataReader databaseMetaDataReader, DatabaseMetaData databaseMetaData, String tableName) { return newArrayList(Iterables.transform(databaseMetaDataReader.getFields(databaseMetaData, tableName), new Function() { public DDLField apply(Field from) { DDLField field = new DDLField(); field.setAutoIncrement(from.isAutoIncrement()); field.setDefaultValue(from.getDefaultValue()); field.setName(from.getName()); field.setNotNull(from.isNotNull()); field.setPrecision(from.getPrecision()); field.setPrimaryKey(from.isPrimaryKey()); field.setScale(from.getScale()); field.setType(from.getDatabaseType()); return field; } })); } private static List readForeignKeys(DatabaseMetaDataReader databaseMetaDataReader, DatabaseMetaData databaseMetaData, String tableName) { return newArrayList(Iterables.transform(databaseMetaDataReader.getForeignKeys(databaseMetaData, tableName), new Function() { public DDLForeignKey apply(ForeignKey from) { DDLForeignKey key = new DDLForeignKey(); key.setForeignField(from.getForeignFieldName()); key.setField(from.getLocalFieldName()); key.setTable(from.getForeignTableName()); key.setDomesticTable(from.getLocalTableName()); return key; } })); } /** * Returns the difference between from and * onto with a bias toward from. * Thus, if a table is present in from but not * onto, a CREATE TABLE * statement will be generated. */ public static DDLAction[] diffSchema(DDLTable[] fromArray, DDLTable[] ontoArray, boolean caseSensetive) { Set actions = new HashSet(); List createTables = new ArrayList(); List dropTables = new ArrayList(); List alterTables = new ArrayList(); Map from = new HashMap(); Map onto = new HashMap(); for (DDLTable table : fromArray) { String tableName = table.getName(); if (!caseSensetive) { tableName = tableName.toLowerCase(); } from.put(tableName, table); } for (DDLTable table : ontoArray) { String tableName = table.getName(); if (!caseSensetive) { tableName = tableName.toLowerCase(); } onto.put(tableName, table); } for (DDLTable table : fromArray) { String tableName = transform(table.getName(), caseSensetive); if (onto.containsKey(tableName)) { alterTables.add(table); } else { createTables.add(table); } } for (DDLTable table : ontoArray) { String tableName = transform(table.getName(), caseSensetive); if (!from.containsKey(tableName)) { dropTables.add(table); } } for (DDLTable table : createTables) { DDLAction action = new DDLAction(DDLActionType.CREATE); action.setTable(table); actions.add(action); } List dropKeys = new ArrayList(); for (DDLTable table : dropTables) { DDLAction action = new DDLAction(DDLActionType.DROP); action.setTable(table); actions.add(action); // remove all foreign keys on that table dropKeys.addAll(Arrays.asList(table.getForeignKeys())); // remove all foreign to that table for (DDLTable alterTable : alterTables) { for (DDLForeignKey fKey : alterTable.getForeignKeys()) { if (equals(fKey.getTable(), table.getName(), caseSensetive)) { dropKeys.add(fKey); } } } } for (DDLTable fromTable : alterTables) { final String s = fromTable.getName(); String tableName = transform(s, caseSensetive); DDLTable ontoTable = onto.get(tableName); List createFields = new ArrayList(); List dropFields = new ArrayList(); List alterFields = new ArrayList(); Map fromFields = new HashMap(); Map ontoFields = new HashMap(); for (DDLField field : fromTable.getFields()) { String fieldName = transform(field.getName(), caseSensetive); fromFields.put(fieldName, field); } for (DDLField field : ontoTable.getFields()) { String fieldName = transform(field.getName(), caseSensetive); ontoFields.put(fieldName, field); } for (DDLField field : fromTable.getFields()) { String fieldName = transform(field.getName(), caseSensetive); if (ontoFields.containsKey(fieldName)) { alterFields.add(field); } else { createFields.add(field); } } for (DDLField field : ontoTable.getFields()) { String fieldName = transform(field.getName(), caseSensetive); if (!fromFields.containsKey(fieldName)) { dropFields.add(field); } } for (DDLField field : createFields) { DDLAction action = new DDLAction(DDLActionType.ALTER_ADD_COLUMN); action.setTable(fromTable); action.setField(field); actions.add(action); } for (DDLField field : dropFields) { DDLAction action = new DDLAction(DDLActionType.ALTER_DROP_COLUMN); action.setTable(fromTable); action.setField(field); actions.add(action); } for (DDLField fromField : alterFields) { String fieldName = transform(fromField.getName(), caseSensetive); DDLField ontoField = ontoFields.get(fieldName); if (fromField.getDefaultValue() == null && ontoField.getDefaultValue() != null) { if (!ontoField.getDefaultValue().toString().equals("CURRENT_TIMESTAMP")) { // super-hack for MySQL actions.add(createColumnAlterAction(fromTable, ontoField, fromField)); } } else if (fromField.getDefaultValue() != null && !Common.fuzzyCompare(fromField.getDefaultValue(), ontoField.getDefaultValue())) { actions.add(createColumnAlterAction(fromTable, ontoField, fromField)); } /*else if (!fromField.getOnUpdate().equals(ontoField.getOnUpdate())) { actions.add(createColumnAlterAction(fromTable, fromField)); } else if (fromField.getPrecision() != ontoField.getPrecision()) { actions.add(createColumnAlterAction(fromTable, ontoField, fromField)); } else if (fromField.getScale() != ontoField.getScale()) { actions.add(createColumnAlterAction(fromTable, ontoField, fromField)); }*/ else if (!Common.fuzzyTypeCompare(fromField.getType().getType(), ontoField.getType().getType())) { actions.add(createColumnAlterAction(fromTable, ontoField, fromField)); } /*else if (fromField.isAutoIncrement() != ontoField.isAutoIncrement()) { actions.add(createColumnAlterAction(fromTable, ontoField, fromField)); } else if (fromField.isNotNull() != ontoField.isNotNull()) { actions.add(createColumnAlterAction(fromTable, ontoField, fromField)); } else if (fromField.isUnique() != ontoField.isUnique()) { actions.add(createColumnAlterAction(fromTable, fromField)); }*/ } // foreign keys List addKeys = new ArrayList(); for (DDLForeignKey fromKey : fromTable.getForeignKeys()) { for (DDLForeignKey ontoKey : ontoTable.getForeignKeys()) { if (!(fromKey.getTable().equalsIgnoreCase(ontoKey.getTable()) && fromKey.getForeignField().equalsIgnoreCase(ontoKey.getForeignField())) && fromKey.getField().equalsIgnoreCase(ontoKey.getField()) && fromKey.getDomesticTable().equalsIgnoreCase(ontoKey.getDomesticTable())) { addKeys.add(fromKey); } } } for (DDLForeignKey ontoKey : ontoTable.getForeignKeys()) { for (DDLForeignKey fromKey : fromTable.getForeignKeys()) { if (!(ontoKey.getTable().equalsIgnoreCase(fromKey.getTable()) && ontoKey.getForeignField().equalsIgnoreCase(fromKey.getForeignField())) && ontoKey.getField().equalsIgnoreCase(fromKey.getField()) && ontoKey.getDomesticTable().equalsIgnoreCase(fromKey.getDomesticTable())) { dropKeys.add(ontoKey); } } } for (DDLForeignKey key : addKeys) { DDLAction action = new DDLAction(DDLActionType.ALTER_ADD_KEY); action.setKey(key); actions.add(action); } // field indexes // List addIndexes = new ArrayList(); // List dropIndexes = new ArrayList(); // // for (DDLIndex fromIndex : fromTable.getIndexes()) { // boolean found = false; // // for (DDLIndex ontoIndex : ontoTable.getIndexes()) { // if (fromIndex.getTable().equalsIgnoreCase(ontoIndex.getTable()) // && fromIndex.getField().equalsIgnoreCase(ontoIndex.getField())) { // found = true; // break; // } // } // // if (!found) { // addIndexes.add(fromIndex); // } // } // // for (DDLIndex ontoIndex : ontoTable.getIndexes()) { // boolean found = false; // // for (DDLIndex fromIndex : fromTable.getIndexes()) { // if (ontoIndex.getTable().equalsIgnoreCase(fromIndex.getTable()) // && ontoIndex.getField().equalsIgnoreCase(fromIndex.getField())) { // found = true; // break; // } // } // // if (!found) { // dropIndexes.add(ontoIndex); // } // } // // for (DDLIndex index : addIndexes) { // DDLAction action = new DDLAction(DDLActionType.CREATE_INDEX); // action.setIndex(index); // actions.add(action); // } // // for (DDLIndex index : dropIndexes) { // DDLAction action = new DDLAction(DDLActionType.DROP_INDEX); // action.setIndex(index); // actions.add(action); // } } for (DDLForeignKey key : dropKeys) { DDLAction action = new DDLAction(DDLActionType.ALTER_DROP_KEY); action.setKey(key); actions.add(action); } return actions.toArray(new DDLAction[actions.size()]); } private static boolean equals(String s, String s1, boolean caseSensitive) { return transform(s, caseSensitive).equals(transform(s1, caseSensitive)); } private static String transform(String s, boolean caseSensitive) { String tableName = s; if (!caseSensitive) { tableName = tableName.toLowerCase(); } return tableName; } public static DDLAction[] sortTopologically(DDLAction[] actions) { List back = new LinkedList(); Map> deps = new HashMap>(); List roots = new LinkedList(); Set covered = new HashSet(); performSort(actions, deps, roots); while (!roots.isEmpty()) { DDLAction[] rootsArray = roots.toArray(new DDLAction[roots.size()]); roots.remove(rootsArray[0]); if (covered.contains(rootsArray[0])) { throw new RuntimeException("Circular dependency detected in or below " + rootsArray[0].getTable().getName()); } covered.add(rootsArray[0]); back.add(rootsArray[0]); List toRemove = new LinkedList(); Iterator depIterator = deps.keySet().iterator(); while (depIterator.hasNext()) { DDLAction depAction = depIterator.next(); Set individualDeps = deps.get(depAction); individualDeps.remove(rootsArray[0]); if (individualDeps.isEmpty()) { roots.add(depAction); toRemove.add(depAction); } } for (DDLAction action : toRemove) { deps.remove(action); } } return back.toArray(new DDLAction[back.size()]); } /* * DROP_KEY * DROP_INDEX * DROP_COLUMN * CHANGE_COLUMN * DROP * CREATE * ADD_COLUMN * ADD_KEY * CREATE_INDEX */ private static void performSort(DDLAction[] actions, Map> deps, List roots) { List dropKeys = new LinkedList(); List dropIndexes = new LinkedList(); List dropColumns = new LinkedList(); List changeColumns = new LinkedList(); List drops = new LinkedList(); List creates = new LinkedList(); List addColumns = new LinkedList(); List addKeys = new LinkedList(); List createIndexes = new LinkedList(); for (DDLAction action : actions) { switch (action.getActionType()) { case ALTER_DROP_KEY: dropKeys.add(action); break; case DROP_INDEX: dropIndexes.add(action); break; case ALTER_DROP_COLUMN: dropColumns.add(action); break; case ALTER_CHANGE_COLUMN: changeColumns.add(action); break; case DROP: drops.add(action); break; case CREATE: creates.add(action); break; case ALTER_ADD_COLUMN: addColumns.add(action); break; case ALTER_ADD_KEY: addKeys.add(action); break; case CREATE_INDEX: createIndexes.add(action); break; } } roots.addAll(dropKeys); roots.addAll(dropIndexes); for (DDLAction action : dropColumns) { Set dependencies = new HashSet(); for (DDLAction depAction : dropKeys) { DDLForeignKey key = depAction.getKey(); if ((key.getTable().equals(action.getTable().getName()) && key.getForeignField().equals(action.getField().getName())) || (key.getDomesticTable().equals(action.getTable().getName()) && key.getField().equals(action.getField().getName()))) { dependencies.add(depAction); } } if (dependencies.size() == 0) { roots.add(action); } else { deps.put(action, dependencies); } } for (DDLAction action : changeColumns) { Set dependencies = new HashSet(); for (DDLAction depAction : dropKeys) { DDLForeignKey key = depAction.getKey(); if ((key.getTable().equals(action.getTable().getName()) && key.getForeignField().equals(action.getField().getName())) || (key.getDomesticTable().equals(action.getTable().getName()) && key.getField().equals(action.getField().getName()))) { dependencies.add(depAction); } } for (DDLAction depAction : dropColumns) { if ((depAction.getTable().equals(action.getTable()) && depAction.getField().equals(action.getField())) || (depAction.getTable().equals(action.getTable()) && depAction.getField().equals(action.getOldField()))) { dependencies.add(depAction); } } if (dependencies.size() == 0) { roots.add(action); } else { deps.put(action, dependencies); } } for (DDLAction action : drops) { Set dependencies = new HashSet(); for (DDLAction depAction : dropKeys) { DDLForeignKey key = depAction.getKey(); if (key.getTable().equals(action.getTable().getName()) || key.getDomesticTable().equals(action.getTable().getName())) { dependencies.add(depAction); } } for (DDLAction depAction : dropColumns) { if (depAction.getTable().equals(action.getTable())) { dependencies.add(depAction); } } for (DDLAction depAction : changeColumns) { if (depAction.getTable().equals(action.getTable())) { dependencies.add(depAction); } } if (dependencies.size() == 0) { roots.add(action); } else { deps.put(action, dependencies); } } for (DDLAction action : creates) { Set dependencies = new HashSet(); for (DDLForeignKey key : action.getTable().getForeignKeys()) { for (DDLAction depAction : creates) { if (depAction != action && depAction.getTable().getName().equals(key.getTable())) { dependencies.add(depAction); } } for (DDLAction depAction : addColumns) { if (depAction.getTable().getName().equals(key.getTable()) && depAction.getField().getName().equals(key.getForeignField())) { dependencies.add(depAction); } } for (DDLAction depAction : changeColumns) { if (depAction.getTable().getName().equals(key.getTable()) && depAction.getField().getName().equals(key.getForeignField())) { dependencies.add(depAction); } } } if (dependencies.size() == 0) { roots.add(action); } else { deps.put(action, dependencies); } } for (DDLAction action : addColumns) { Set dependencies = new HashSet(); for (DDLAction depAction : creates) { if (depAction.getTable().equals(action.getTable())) { dependencies.add(depAction); } } if (dependencies.size() == 0) { roots.add(action); } else { deps.put(action, dependencies); } } for (DDLAction action : addKeys) { Set dependencies = new HashSet(); DDLForeignKey key = action.getKey(); for (DDLAction depAction : creates) { if (depAction.getTable().getName().equals(key.getTable()) || depAction.getTable().getName().equals(key.getDomesticTable())) { dependencies.add(depAction); } } for (DDLAction depAction : addColumns) { if ((depAction.getTable().getName().equals(key.getTable()) && depAction.getField().getName().equals(key.getForeignField())) || (depAction.getTable().getName().equals(key.getDomesticTable())) && depAction.getField().getName().equals(key.getField())) { dependencies.add(depAction); } } for (DDLAction depAction : changeColumns) { if ((depAction.getTable().getName().equals(key.getTable()) && depAction.getField().getName().equals(key.getForeignField())) || (depAction.getTable().getName().equals(key.getDomesticTable())) && depAction.getField().getName().equals(key.getField())) { dependencies.add(depAction); } } if (dependencies.size() == 0) { roots.add(action); } else { deps.put(action, dependencies); } } for (DDLAction action : createIndexes) { Set dependencies = new HashSet(); DDLIndex index = action.getIndex(); for (DDLAction depAction : creates) { if (depAction.getTable().getName().equals(index.getTable())) { dependencies.add(depAction); } } for (DDLAction depAction : addColumns) { if (depAction.getTable().getName().equals(index.getTable()) || depAction.getField().getName().equals(index.getField())) { dependencies.add(depAction); } } for (DDLAction depAction : changeColumns) { if (depAction.getTable().getName().equals(index.getTable()) || depAction.getField().getName().equals(index.getField())) { dependencies.add(depAction); } } if (dependencies.size() == 0) { roots.add(action); } else { deps.put(action, dependencies); } } } private static DDLAction createColumnAlterAction(DDLTable table, DDLField oldField, DDLField field) { DDLAction action = new DDLAction(DDLActionType.ALTER_CHANGE_COLUMN); action.setTable(table); action.setField(field); action.setOldField(oldField); return action; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy