package com.webobjects.jdbcadaptor;
import java.util.Iterator;
import com.webobjects.eoaccess.EOAdaptor;
import com.webobjects.eoaccess.EOAttribute;
import com.webobjects.eoaccess.EOEntity;
import com.webobjects.eoaccess.EOModel;
import com.webobjects.eoaccess.EORelationship;
import com.webobjects.eoaccess.EOSQLExpression;
import com.webobjects.eoaccess.EOSchemaGeneration;
import com.webobjects.eoaccess.EOSchemaSynchronization;
import com.webobjects.eoaccess.EOSynchronizationFactory;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSMutableSet;
import com.webobjects.foundation._NSDictionaryUtilities;
import com.webobjects.foundation._NSStringUtilities;
/**
* A synchronization factory usable outside EOModeler.
* Original code was for Postress changed for DB2
*
* @author simpson
* @author giorgio_v
*/
@SuppressWarnings({ "rawtypes", "unchecked", "deprecation" })
public class DB2SynchronizationFactory extends EOSynchronizationFactory implements EOSchemaGeneration, EOSchemaSynchronization {
public static final String USING_KEY = "USING";
private Boolean _enableIdentifierQuoting;
public DB2SynchronizationFactory(EOAdaptor adaptor) {
super(adaptor);
}
private boolean enableIdentifierQuoting() {
if(_enableIdentifierQuoting == null) {
_enableIdentifierQuoting = Boolean.getBoolean(DB2Expression.class.getName() + ".enableIdentifierQuoting") ? Boolean.TRUE : Boolean.FALSE;
}
return _enableIdentifierQuoting.booleanValue();
}
@Override
protected String formatTableName(String name) {
if (!enableIdentifierQuoting()) {
return name;
}
return "\"" + name + "\"";
}
@Override
protected String formatColumnName(String name) {
if (!enableIdentifierQuoting()) {
return name;
}
return "\"" + name + "\"";
}
@Override
public NSArray _foreignKeyConstraintStatementsForEntityGroup(NSArray group) {
if (group == null)
return NSArray.EmptyArray;
NSMutableArray result = new NSMutableArray();
NSMutableSet generatedStatements = new NSMutableSet();
int i = 0;
for (int groupCount = group.count(); i < groupCount; i++) {
EOEntity currentEntity = (EOEntity) group.objectAtIndex(i);
if (currentEntity.externalName() != null) {
NSArray relationships = currentEntity.relationships();
int relCount = relationships.count();
for (int j = 0; j < relCount; j++) {
EORelationship currentRelationship = ((EORelationship) relationships.objectAtIndex(j));
if (_shouldGenerateForeignKeyConstraints(currentRelationship)) {
NSArray statements = foreignKeyConstraintStatementsForRelationship(currentRelationship);
if (!generatedStatements.containsObject(statements.valueForKey("statement"))) {
result.addObjectsFromArray(statements);
generatedStatements.addObject(statements.valueForKey("statement"));
}
}
}
}
}
return result;
}
protected boolean _shouldGenerateForeignKeyConstraints(EORelationship rel) {
EOEntity destinationEntity = rel.destinationEntity();
return !rel.isFlattened() && destinationEntity.externalName() != null && rel.entity().model() == destinationEntity.model();
}
/**
* <code>DB2Expression</code> factory method.
*
* @param entity
* the entity to which <code>DB2Expression</code> is to
* be rooted
* @param statement
* the SQL statement
* @return a <code>DB2Expression</code> rooted to
* <code>entity</code>
*/
private static DB2Expression createExpression(EOEntity entity, String statement) {
DB2Expression result = new DB2Expression(entity);
result.setStatement(statement);
return result;
}
/**
* Generates the DB2-specific SQL statements to drop the primary key
* support.
*
* @param entityGroup
* an array of <code>EOEntity</code> objects
* @return the array of SQL statements
*/
@Override
public NSArray dropPrimaryKeySupportStatementsForEntityGroup(NSArray entityGroup) {
NSMutableSet sequenceNames = new NSMutableSet();
NSMutableArray results = new NSMutableArray();
int count = entityGroup.count();
for (int i = 0; i < count; i++) {
EOEntity entity = (EOEntity) entityGroup.objectAtIndex(i);
String sequenceName = DB2PlugIn._sequenceNameForEntity(entity);
if (!sequenceNames.containsObject(sequenceName)) {
sequenceNames.addObject(sequenceName);
String sql = "DROP SEQUENCE " + sequenceName ;
results.addObject(createExpression(entity, sql));
}
}
return results;
}
/**
* Generates the DB2-specific SQL statements to drop tables.
*
* @param entityGroup
* an array of <code>EOEntity</code> objects
* @return the array of SQL statements
*/
@Override
public NSArray dropTableStatementsForEntityGroup(NSArray entityGroup) {
NSMutableArray results = new NSMutableArray();
int count = entityGroup.count();
for (int i = 0; i < count; i++) {
EOEntity entity = (EOEntity) entityGroup.objectAtIndex(i);
// timc 2006-11-06 create result here so we can check for
// enableIdentifierQuoting while building the statement
DB2Expression result = new DB2Expression(entity);
String tableName = result.sqlStringForSchemaObjectName(entity.externalName());
if(entityUsesSeparateTable(entity)) {
result.setStatement("DROP TABLE " + tableName );
results.addObject(result);
}
}
return results;
}
/**
* Generates the DB2-specific SQL statements to enforce the foreign
* key constraints for <code>relationship</code>.
*
* @param relationship
* the relationship, as represented by EOF
* @return the array of SQL statements
*/
@Override
public NSArray foreignKeyConstraintStatementsForRelationship(EORelationship relationship) {
NSArray superResults;
NSMutableArray results;
int count;
int i;
EOSQLExpression expression;
results = new NSMutableArray();
superResults = super.foreignKeyConstraintStatementsForRelationship(relationship);
count = superResults.count();
for (i = 0; i < count; i++) {
expression = (EOSQLExpression) superResults.objectAtIndex(i);
String s = expression.statement();
s = replaceStringByStringInString(") INITIALLY DEFERRED", ") ENFORCED", s);
expression.setStatement(s);
results.addObject(expression);
// timc 2006-11-06 check for enableIdentifierQuoting
String tableName = expression.sqlStringForSchemaObjectName(expression.entity().externalName());
NSArray columnNames = ((NSArray) relationship.sourceAttributes().valueForKey("columnName"));
StringBuilder sbColumnNames = new StringBuilder();
for (int j = 0; j < columnNames.count(); j++) {
sbColumnNames.append((j == 0 ? "" : ", ") + expression.sqlStringForSchemaObjectName((String) columnNames.objectAtIndex(j)));
}
String indexName = externalNameForEntityWithoutSchema(relationship.entity()) + "_" + columnNames.componentsJoinedByString("_") + "_idx";
results.addObject(createExpression(expression.entity(), "CREATE INDEX " + indexName + " ON " + tableName + "( " + sbColumnNames.toString() + " )"));
}
return results;
}
protected String externalNameForEntityWithoutSchema(EOEntity entity) {
String externalName = entity.externalName();
if (externalName != null) {
int dotIndex = externalName.indexOf('.');
if (dotIndex != -1) {
externalName = externalName.substring(dotIndex + 1);
}
}
return externalName;
}
/**
* Generates the DB2-specific SQL statements to enforce primary key
* constraints.
*
* @param entityGroup
* an array of <code>EOEntity</code> objects
* @return the array of SQL statements
*/
@Override
public NSArray primaryKeyConstraintStatementsForEntityGroup(NSArray entityGroup) {
EOEntity entity;
int count;
int i;
NSMutableArray results;
NSArray priKeyAttributes;
EOAttribute priKeyAttribute;
int priKeyAttributeCount;
int j;
results = new NSMutableArray();
count = entityGroup.count();
for (i = 0; i < count; i++) {
entity = (EOEntity) entityGroup.objectAtIndex(i);
if (!entityUsesSeparateTable(entity))
continue;
// timc 2006-11-06 create result here so we can check for
// enableIdentifierQuoting while building the statement
DB2Expression result = new DB2Expression(entity);
String constraintName = result.sqlStringForSchemaObjectName(externalNameForEntityWithoutSchema(entity) + "_pk");
String tableName = result.sqlStringForSchemaObjectName(entity.externalName());
StringBuilder statement = new StringBuilder("ALTER TABLE ");
statement.append(tableName);
statement.append(" ADD CONSTRAINT ");
statement.append(constraintName);
statement.append(" PRIMARY KEY (");
priKeyAttributes = entity.primaryKeyAttributes();
priKeyAttributeCount = priKeyAttributes.count();
for (j = 0; j < priKeyAttributeCount; j++) {
priKeyAttribute = (EOAttribute) priKeyAttributes.objectAtIndex(j);
String attributeName = result.sqlStringForAttribute(priKeyAttribute);
statement.append(attributeName);
if (j < priKeyAttributeCount - 1) {
statement.append(", ");
} else {
statement.append(')');
}
}
result.setStatement(statement.toString());
results.addObject(result);
}
return results;
}
/**
* Returns true if Entity Modeler is running the operation on this model.
*
* @param model the model to check
* @return true if Entity Modeler is running
*/
protected boolean isInEntityModeler(EOModel model) {
boolean inEntityModeler = false;
if (model != null) {
NSDictionary userInfo = model.userInfo();
NSDictionary entityModelerDict = (NSDictionary) userInfo.objectForKey("_EntityModeler");
if (entityModelerDict != null) {
Boolean inEntityModelerBoolean = (Boolean)entityModelerDict.objectForKey("inEntityModeler");
if (inEntityModelerBoolean != null && inEntityModelerBoolean.booleanValue()) {
inEntityModeler = inEntityModelerBoolean.booleanValue();
}
}
}
return inEntityModeler;
}
/**
* Generates the DB2-specific SQL statements to create the primary
* key support.
*
* @param entityGroup
* an array of <code>EOEntity</code> objects
* @return the array of SQL statements
*/
@Override
public NSArray primaryKeySupportStatementsForEntityGroup(NSArray entityGroup) {
EOEntity entity;
int count;
int i;
NSMutableArray results;
NSArray priKeyAttributes;
EOAttribute priKeyAttribute;
String sequenceName;
NSMutableSet sequenceNames = new NSMutableSet();
results = new NSMutableArray();
count = entityGroup.count();
for (i = 0; i < count; i++) {
entity = (EOEntity) entityGroup.objectAtIndex(i);
priKeyAttributes = entity.primaryKeyAttributes();
if (priKeyAttributes.count() == 1) {
priKeyAttribute = (EOAttribute) priKeyAttributes.objectAtIndex(0);
// Q: Don't create a sequence for non number primary keys
if (priKeyAttribute.adaptorValueType() != EOAttribute.AdaptorNumberType) {
continue;
}
String sql;
sequenceName = DB2PlugIn._sequenceNameForEntity(entity);
if (!sequenceNames.containsObject(sequenceName)) {
sequenceNames.addObject(sequenceName);
// timc 2006-11-06 create result here so we can check for
// enableIdentifierQuoting while building the statement
sql = "CREATE SEQUENCE " + sequenceName;
results.addObject(createExpression(entity, sql));
}
}
}
return results;
}
public static boolean entityUsesSeparateTable(EOEntity entity) {
if (entity.parentEntity() == null)
return true;
EOEntity parent = entity.parentEntity();
while (parent != null) {
if (!entity.externalName().equals(parent.externalName()))
return true;
entity = parent;
parent = entity.parentEntity();
}
return false;
}
/**
* Quote table name if necessary
*/
@Override
public NSArray createTableStatementsForEntityGroup(NSArray entityGroup) {
NSMutableSet columnNames = new NSMutableSet();
StringBuffer aStatement = new StringBuffer(128);
if (entityGroup != null && entityGroup.count() > 0) {
EOSQLExpression sqlExpr = _expressionForEntity((EOEntity) entityGroup.objectAtIndex(0));
for (Iterator entityIterator = entityGroup.iterator(); entityIterator.hasNext();) {
EOEntity entity = (EOEntity) entityIterator.next();
Iterator attributeIterator = entity.attributes().iterator();
while (attributeIterator.hasNext()) {
EOAttribute attribute = (EOAttribute) attributeIterator.next();
String columnName = attribute.columnName();
if (!attribute.isDerived() && !attribute.isFlattened() && columnName != null && columnName.length() > 0 && !columnNames.contains(columnName)) {
sqlExpr.appendItemToListString(_columnCreationClauseForAttribute(attribute), aStatement);
columnNames.addObject(columnName);
}
}
}
return new NSArray(_expressionForString(new StringBuilder().append("CREATE TABLE ").append(formatTableName(((EOEntity) entityGroup.objectAtIndex(0)).externalName())).append(" (").append(aStatement.toString()).append(')').toString()));
}
return new NSArray();
}
/**
* Replaces a given string by another string in a string.
*
* @param old
* string to be replaced
* @param newString
* to be inserted
* @param buffer
* string to have the replacement done on it
* @return string after having all of the replacement done.
*/
public static String replaceStringByStringInString(String old, String newString, String buffer) {
int begin, end;
int oldLength = old.length();
int length = buffer.length();
StringBuilder convertedString = new StringBuilder(length + 100);
begin = 0;
while (begin < length) {
end = buffer.indexOf(old, begin);
if (end == -1) {
convertedString.append(buffer.substring(begin));
break;
}
if (end == 0)
convertedString.append(newString);
else {
convertedString.append(buffer.substring(begin, end));
convertedString.append(newString);
}
begin = end + oldLength;
}
return convertedString.toString();
}
// I blame statementstToConvertColumnType for not taking a damn EOAttribute for
// having to steal this from EOSQLExpression
public String columnTypeStringForAttribute(EOAttribute attribute) {
if (attribute.precision() != 0) {
String precision = String.valueOf(attribute.precision());
String scale = String.valueOf(attribute.scale());
return _NSStringUtilities.concat(attribute.externalType(), "(", precision, ",", scale, ")");
}
if (attribute.width() != 0) {
String width = String.valueOf(attribute.width());
return _NSStringUtilities.concat(attribute.externalType(), "(", width, ")");
}
return attribute.externalType();
}
@Override
public NSArray statementsToModifyColumnNullRule(String columnName, String tableName, boolean allowsNull, NSDictionary nsdictionary) {
NSArray statements;
if (allowsNull) {
statements = new NSArray(_expressionForString("alter table " + formatTableName(tableName) + " alter column " + formatColumnName(columnName) + " drop not null"));
} else {
statements = new NSArray(_expressionForString("alter table " + formatTableName(tableName) + " alter column " + formatColumnName(columnName) + " set not null"));
}
return statements;
}
@Override
public NSArray statementsToConvertColumnType(String columnName, String tableName, ColumnTypes oldType, ColumnTypes newType, NSDictionary options) {
EOAttribute attr = new EOAttribute();
attr.setName(columnName);
attr.setColumnName(columnName);
attr.setExternalType(newType.name());
attr.setScale(newType.scale());
attr.setPrecision(newType.precision());
attr.setWidth(newType.width());
String usingClause = "";
String columnTypeString = columnTypeStringForAttribute(attr);
if (options != null) {
String usingExpression = (String) options.objectForKey(DB2SynchronizationFactory.USING_KEY);
if (usingExpression != null) {
usingClause = " USING " + usingExpression;
}
}
NSArray statements = new NSArray(_expressionForString("alter table " + formatTableName(tableName) + " alter column " + formatColumnName(columnName) + "set data type " + columnTypeString + usingClause));
return statements;
}
@Override
public NSArray statementsToRenameColumnNamed(String columnName, String tableName, String newName, NSDictionary nsdictionary) {
return new NSArray(_expressionForString("alter table " + formatTableName(tableName) + " rename column " + formatColumnName(columnName) + " to " + formatColumnName(newName)));
}
@Override
public NSArray statementsToInsertColumnForAttribute(EOAttribute attribute, NSDictionary options) {
String clause = _columnCreationClauseForAttribute(attribute);
return new NSArray(_expressionForString("alter table " + formatTableName(attribute.entity().externalName()) + " add " + clause));
}
@Override
public NSArray statementsToRenameTableNamed(String tableName, String newName, NSDictionary options) {
return new NSArray(_expressionForString("alter table " + formatTableName(tableName) + " rename to " + formatTableName(newName)));
}
@Override
public NSArray statementsToDeleteColumnNamed(String columnName, String tableName, NSDictionary options) {
return new NSArray(_expressionForString("alter table " + formatTableName(tableName) + " drop column " + formatTableName(columnName) + " cascade"));
}
/*
public StringBuffer addCreateClauseForAttribute(EOAttribute eoattribute) {
EOSQLExpression expression = _expressionForEntity(eoattribute.entity());
expression.addCreateClauseForAttribute(eoattribute);
return new StringBuffer(expression.listString());
}
public String _columnCreationClauseForAttribute(EOAttribute attribute) {
return addCreateClauseForAttribute(attribute).toString();
}
*/
@Override
public String schemaCreationScriptForEntities(NSArray allEntities, NSDictionary options)
{
/* 741*/ StringBuffer result = new StringBuffer();
/* 744*/ if(options == null)
/* 745*/ options = NSDictionary.EmptyDictionary;
/* 747*/ NSArray statements = schemaCreationStatementsForEntities(allEntities, options);
/* 748*/ int i = 0;
/* 748*/ for(int count = statements.count(); i < count; i++)
/* 749*/ appendExpressionToScript((EOSQLExpression)statements.objectAtIndex(i), result);
/* 751*/ return result.toString();
}
@Override
public NSArray schemaCreationStatementsForEntities(NSArray allEntities, NSDictionary options)
{
/* 879*/ NSMutableArray result = new NSMutableArray();
/* 880*/ if(allEntities == null || allEntities.count() == 0)
/* 881*/ return result;
/* 883*/ if(options == null)
/* 884*/ options = NSDictionary.EmptyDictionary;
/* 889*/ NSDictionary connectionDictionary = ((EOEntity)allEntities.lastObject()).model().connectionDictionary();
/* 892*/ boolean createDatabase = _NSDictionaryUtilities.boolValueForKeyDefault(options, "createDatabase", false);
/* 893*/ boolean dropDatabase = _NSDictionaryUtilities.boolValueForKeyDefault(options, "dropDatabase", false);
/* 895*/ if(createDatabase || dropDatabase)
{
/* 896*/ boolean adminCommentsNeeded = false;
/* 897*/ NSArray dropDatabaseStatements = null;
/* 898*/ NSArray createDatabaseStatements = null;
/* 900*/ if(dropDatabase)
{
/* 901*/ dropDatabaseStatements = dropDatabaseStatementsForConnectionDictionary(connectionDictionary, null);
/* 903*/ if(dropDatabaseStatements == null)
/* 904*/ dropDatabaseStatements = new NSArray(_expressionForString("/* The 'Drop Database' option is unavailable. */"));
/* 907*/ else
/* 907*/ adminCommentsNeeded = true;
}
/* 911*/ if(createDatabase)
{
/* 912*/ createDatabaseStatements = createDatabaseStatementsForConnectionDictionary(connectionDictionary, null);
/* 914*/ if(createDatabaseStatements == null)
/* 915*/ createDatabaseStatements = new NSArray(_expressionForString("/* The 'Create Database' option is unavailable. */"));
/* 918*/ else
/* 918*/ adminCommentsNeeded = true;
}
/* 922*/ if(adminCommentsNeeded)
/* 923*/ result.addObject(_expressionForString("/* connect as an administrator */"));
/* 926*/ if(dropDatabaseStatements != null)
/* 927*/ result.addObjectsFromArray(dropDatabaseStatements);
/* 930*/ if(createDatabaseStatements != null)
/* 931*/ result.addObjectsFromArray(createDatabaseStatements);
/* 934*/ if(adminCommentsNeeded)
/* 935*/ result.addObject(_expressionForString("/* connect as the user from the connection dictionary */"));
}
/* 939*/ if(_NSDictionaryUtilities.boolValueForKeyDefault(options, "dropPrimaryKeySupport", true))
{
/* 940*/ NSArray entityGroups = primaryKeyEntityGroupsForEntities(allEntities);
/* 941*/ result.addObjectsFromArray(dropPrimaryKeySupportStatementsForEntityGroups(entityGroups));
}
/* 944*/ if(_NSDictionaryUtilities.boolValueForKeyDefault(options, "dropTables", true))
{
/* 945*/ NSArray entityGroups = tableEntityGroupsForEntities(allEntities);
/* 946*/ result.addObjectsFromArray(dropTableStatementsForEntityGroups(entityGroups));
}
/* 949*/ if(_NSDictionaryUtilities.boolValueForKeyDefault(options, "createTables", true))
{
/* 950*/ NSArray entityGroups = tableEntityGroupsForEntities(allEntities);
/* 951*/ result.addObjectsFromArray(createTableStatementsForEntityGroups(entityGroups));
}
/* 954*/ if(_NSDictionaryUtilities.boolValueForKeyDefault(options, "createPrimaryKeySupport", true))
{
/* 955*/ NSArray entityGroups = primaryKeyEntityGroupsForEntities(allEntities);
/* 956*/ result.addObjectsFromArray(primaryKeySupportStatementsForEntityGroups(entityGroups));
}
/* 959*/ if(_NSDictionaryUtilities.boolValueForKeyDefault(options, "primaryKeyConstraints", true))
{
/* 960*/ NSArray entityGroups = tableEntityGroupsForEntities(allEntities);
/* 961*/ result.addObjectsFromArray(primaryKeyConstraintStatementsForEntityGroups(entityGroups));
}
/* 964*/ if(_NSDictionaryUtilities.boolValueForKeyDefault(options, "foreignKeyConstraints", false))
{
/* 965*/ NSArray entityGroups = tableEntityGroupsForEntities(allEntities);
/* 966*/ int i = 0;
/* 966*/ for(int iCount = entityGroups.count(); i < iCount; i++)
/* 967*/ result.addObjectsFromArray(_foreignKeyConstraintStatementsForEntityGroup((NSArray)entityGroups.objectAtIndex(i)));
}
/* 970*/ return result;
}
}