Package com.webobjects.jdbcadaptor

Source Code of com.webobjects.jdbcadaptor._MySQLPlugIn$MySQLSynchronizationFactory

package com.webobjects.jdbcadaptor;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.sql.Blob;
import java.sql.ResultSet;
import java.sql.SQLException;

import com.webobjects.eoaccess.EOAdaptor;
import com.webobjects.eoaccess.EOAttribute;
import com.webobjects.eoaccess.EOEntity;
import com.webobjects.eoaccess.EOJoin;
import com.webobjects.eoaccess.EOModel;
import com.webobjects.eoaccess.EOModelGroup;
import com.webobjects.eoaccess.EORelationship;
import com.webobjects.eoaccess.EOSQLExpression;
import com.webobjects.eoaccess.synchronization.EOSchemaGenerationOptions;
import com.webobjects.eoaccess.synchronization.EOSchemaSynchronizationFactory;
import com.webobjects.eocontrol.EOFetchSpecification;
import com.webobjects.eocontrol.EOQualifier;
import com.webobjects.eocontrol.EOSortOrdering;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSBundle;
import com.webobjects.foundation.NSData;
import com.webobjects.foundation.NSDictionary;
import com.webobjects.foundation.NSLog;
import com.webobjects.foundation.NSMutableArray;
import com.webobjects.foundation.NSMutableDictionary;
import com.webobjects.foundation.NSProperties;
import com.webobjects.foundation.NSPropertyListSerialization;
import com.webobjects.foundation.NSRange;
import com.webobjects.foundation.NSSelector;
import com.webobjects.foundation._NSStringUtilities;

public class _MySQLPlugIn extends JDBCPlugIn {

  private static final String DriverClassName = "com.mysql.jdbc.Driver";

  private static final String DriverProductName = "MySQL";
 
  private static final String QUERY_STRING_USE_BUNDLED_JDBC_INFO = "useBundledJdbcInfo";

  public _MySQLPlugIn(JDBCAdaptor adaptor) {
    super(adaptor);
  }

  public static class MySQLExpression extends JDBCExpression {

    // Lazy initialized constants
    private static class CONFIG {
      // Turning on identifier quoting allows the use of reserved words for identifier (table, field, etc.) names
      final static boolean ENABLE_IDENTIFIER_QUOTING = Boolean.getBoolean("com.webobjects.jdbcadaptor.MySQLExpression.enableIdentifierQuoting");
      // Inserts "\n\t" between statement clauses for log readability. Useful in development
      final static boolean LINE_PER_CLAUSE = Boolean.getBoolean("com.webobjects.jdbcadaptor.MySQLExpression.enableLinePerClause");
     
      // Length values for the string constant elements of the statement taking into account LINE_PER_CLAUSE for development and/or MySQL log readability.
      // Note that the space is needed before FROM, WHERE etc in the LINE_PER_CLAUSE variant to ensure compatibility with code that assumes a space
      // surrounding the FROM, as in er.extensions.jdbc.ERXSQLHelper.rowCountForFetchSpecification(...) for example
      final static String FROM_STRING = ( CONFIG.LINE_PER_CLAUSE ? "\n\t FROM " : " FROM " );
      final static String WHERE_STRING = ( CONFIG.LINE_PER_CLAUSE ? "\n\t WHERE " : " WHERE " );
      final static String ORDER_BY_STRING = ( CONFIG.LINE_PER_CLAUSE ? "\n\t ORDER BY " : " ORDER BY " );
      final static String LIMIT_STRING = ( CONFIG.LINE_PER_CLAUSE ? "\n\t LIMIT " : " LIMIT " );
      final static int FROM_LENGTH = FROM_STRING.length();
      final static int WHERE_LENGTH = WHERE_STRING.length();
      final static int ORDER_BY_LENGTH = ORDER_BY_STRING.length();
      final static int LIMIT_LENGTH = LIMIT_STRING.length();
     
      /**
       * From the MySQL Manual: "An identifier may be quoted or
       * unquoted. If an identifier contains special characters or is a
       * reserved word, you must quote it whenever you refer to it. ...
       * The identifier quote character is the backtick."
       */
      final static String IDENTIFIER_QUOTE_CHARACTER = (ENABLE_IDENTIFIER_QUOTING ? "`" : "");
    }

    private int _fetchLimit;
   
    private NSRange _fetchRange;
    private final NSSelector<NSRange> _fetchRangeSelector = new NSSelector<NSRange>("fetchRange");
   
    /**
     * Holds array of join clause definitions
     */
    private final NSMutableArray<JoinClauseDefinition> _alreadyJoined = new NSMutableArray<JoinClauseDefinition>();
   
    public MySQLExpression(EOEntity entity) {
      super(entity);
    }

    /**
     * http://dev.mysql.com/doc/refman/5.5/en/string-comparison-functions.html
     *
     * @see com.webobjects.eoaccess.EOSQLExpression#sqlEscapeChar()
     */
    @Override
        public char sqlEscapeChar(){
      return '|';
    }

    /**
     * Overridden because MySQL does not use the default quote character in
     * EOSQLExpression.externalNameQuoteCharacter() which is an empty
     * string.
     *
     * Note that quoting is disabled by default and can be enabled by setting property <code>com.webobjects.jdbcadaptor.MySQLExpression.enableIdentifierQuoting</code> to true.
     */
    @Override
    public String externalNameQuoteCharacter() {
      return CONFIG.IDENTIFIER_QUOTE_CHARACTER;
    }

    /**
     * Overriding super here so we can grab a fetch range or fetch limit if specified in the EOFetchSpecification. If a fetchRange method
     * returning an NSRange exists in the EOFetchSpecification subclass being passed in, the the fetchLimit will be ignored.
     *
     * @see com.webobjects.jdbcadaptor.JDBCExpression#prepareSelectExpressionWithAttributes(NSArray, boolean, EOFetchSpecification)
     */
    @Override
    public void prepareSelectExpressionWithAttributes(NSArray<EOAttribute> attributes, boolean lock,
        EOFetchSpecification fetchSpec) {
      try {
        _fetchRange = _fetchRangeSelector.invoke(fetchSpec);
        // We will get an error when not using our custom ERXFetchSpecification subclass
        // We could have added ERExtensions to the classpath and checked for instanceof, but I thought
        // this is a little cleaner since people may be using this PlugIn and not Wonder in some legacy apps.
      } catch (IllegalArgumentException e) {
        ;
      } catch (IllegalAccessException e) {
        ;
      } catch (InvocationTargetException e) {
        ;
      } catch (NoSuchMethodException e) {
        ;
      }
      // Only check for fetchLimit of fetchRange is not provided.
      if (_fetchRange == null && !fetchSpec.promptsAfterFetchLimit()) {
        _fetchLimit = fetchSpec.fetchLimit();
      }
      super.prepareSelectExpressionWithAttributes(attributes, lock, fetchSpec);
    }

    /**
     * Overriding to
     * <ul>
     * <li>add LIMIT clause if _fetchLimit > 0</li>
     * <li>support MySQL JOIN syntax (similar syntax to what PostgreSQL PlugIn generates)</li>
     * </ul>
     *
     * @see com.webobjects.eoaccess.EOSQLExpression#assembleSelectStatementWithAttributes(NSArray, boolean, EOQualifier, NSArray, java.lang.String, String, String, String, String, String, String)
     */
    @Override
    public String assembleSelectStatementWithAttributes(@SuppressWarnings("rawtypes") NSArray/*<EOAttribute>*/ attributes, boolean lock, EOQualifier qualifier,
        @SuppressWarnings("rawtypes") NSArray fetchOrder, String selectString, String columnList, String tableList, String whereClause,
        String joinClause, String orderByClause, String lockClause) {

      // When we are selecting from a single table, the joinClause will be empty and the tableList will contain the single table reference.
      // When we have joins, then both the joinClause and tableList will be passed in, however we will just be using the joinClause in the FROM clause.

      int size = selectString.length() + columnList.length() + CONFIG.FROM_LENGTH;
      if ((lockClause != null) && (lockClause.length() != 0)) {
        size += lockClause.length() + 1;
      }
      if ((whereClause != null) && (whereClause.length() != 0)) {
        size += (whereClause.length() + CONFIG.WHERE_LENGTH);
      }
      if ((joinClause != null) && (joinClause.length() != 0)) {
        size += joinClause.length();
      } else {
        size += tableList.length();
      }
      if ((orderByClause != null) && (orderByClause.length() != 0)) {
        size += (orderByClause.length() + CONFIG.ORDER_BY_LENGTH);
      }

      // If necessary, create LIMIT clause and add to buffer size
      String limitClause = null;
      // fetchRange override fetchLimit
      if (_fetchRange != null) {
        limitClause = _fetchRange.location() + ", " + _fetchRange.length();
        size += CONFIG.LIMIT_LENGTH + limitClause.length();
      } else if (_fetchLimit > 0) {
        limitClause = Integer.toString(_fetchLimit);
        size += CONFIG.LIMIT_LENGTH + limitClause.length();
      }

      StringBuilder buffer = new StringBuilder(size);
      buffer.append(selectString);
      buffer.append(columnList);
      buffer.append(CONFIG.FROM_STRING);
     
      if (joinClause != null && joinClause.length() > 0) {
        buffer.append(joinClause);
      } else {
      buffer.append(tableList);
      }

      if (whereClause != null && whereClause.length() > 0) {
        buffer.append(CONFIG.WHERE_STRING);
        buffer.append(whereClause);
      }

      if ((orderByClause != null) && (orderByClause.length() != 0)) {
        buffer.append(CONFIG.ORDER_BY_STRING);
        buffer.append(orderByClause);
      }

      // Add limit clause
      if (limitClause != null) {
        buffer.append(CONFIG.LIMIT_STRING);
        buffer.append(limitClause);
      }

      if ((lockClause != null) && (lockClause.length() != 0)) {
        buffer.append(' ');
        buffer.append(lockClause);
      }

      return buffer.toString();
    }

    /**
     * Overriden to contruct a valid SQL92 JOIN clause as opposed to the
     * Oracle-like SQL the superclass produces.
     *
     * kieran copied from PostgresqlExpression
     */
    @Override
    public String joinClauseString() {
      NSMutableDictionary<String, Boolean> seenIt = new NSMutableDictionary<String, Boolean>();
      StringBuilder sb = new StringBuilder();
      JoinClauseDefinition jc;
      EOSortOrdering.sortArrayUsingKeyOrderArray(_alreadyJoined, new NSArray<EOSortOrdering>(EOSortOrdering.sortOrderingWithKey("sortKey", EOSortOrdering.CompareAscending)));
      if (_alreadyJoined.count() > 0) {
        jc = _alreadyJoined.objectAtIndex(0);

        sb.append(jc);
        seenIt.setObjectForKey(Boolean.TRUE, jc._table1);
        seenIt.setObjectForKey(Boolean.TRUE, jc._table2);
      }

      for (int i = 1; i < _alreadyJoined.count(); i++) {
        jc = _alreadyJoined.objectAtIndex(i);

        sb.append(jc._op);
        if (seenIt.objectForKey(jc._table1) == null) {
          sb.append(jc._table1);
          seenIt.setObjectForKey(Boolean.TRUE, jc._table1);
        } else if (seenIt.objectForKey(jc._table2) == null) {
          sb.append(jc._table2);
          seenIt.setObjectForKey(Boolean.TRUE, jc._table2);
        }
        sb.append(jc._joinCondition);
      }
      return sb.toString();
    }
   
    /**
     * Override so that the joinClause can be constructed after super has
     * iterated though all joins and called our assembleJoinClause to create
     * our array of JoinClauseDefinitions.
     *
     * @see com.webobjects.eoaccess.EOSQLExpression#joinExpression()
     */
    @Override
    public void joinExpression() {
      super.joinExpression();
      if (_alreadyJoined.count() > 0) {
        _joinClauseString = joinClauseString();
      } else {
        _joinClauseString = null;
      }
    }

    /**
     * This is called by super for each join. We do not actually construct the join clause as we get called since
     * for SQL92 JOIN syntax we need to know about all joins before we construct the complete join clause.
     *
     * The objective of this implementation is to insert a new unique {@link JoinClauseDefinition} into
     * the <code>_alreadyJoined</code> array of {@link JoinClauseDefinition} objects.
     *
     * The join clause itself is assembled by <code>joinClauseString()</code>.
     *
     * @param leftName
     *            the table name on the left side of the clause
     * @param rightName
     *            the table name on the right side of the clause
     * @param semantic
     *            the join semantic
     * @return the join clause
     *
     * kieran based this on logic from PostgresqlExpression
     */
    @SuppressWarnings("unchecked")
    @Override
    public String assembleJoinClause(String leftName, String rightName, int semantic) {
      if (!useAliases()) {
        return super.assembleJoinClause(leftName, rightName, semantic);
      }

      String leftAlias = leftName.substring(0, leftName.indexOf("."));
      String rightAlias = rightName.substring(0, rightName.indexOf("."));

      NSArray<String> k;
      EOEntity rightEntity;
      EOEntity leftEntity;
      String relationshipKey = null;
      EORelationship r;

      if (leftAlias.equals("t0")) {
        leftEntity = entity();
      } else {
        k = aliasesByRelationshipPath().allKeysForObject(leftAlias);
        relationshipKey = k.count() > 0 ? (String) k.lastObject() : "";
        leftEntity = entityForKeyPath(relationshipKey);
      }

      if (rightAlias.equals("t0")) {
        rightEntity = entity();
      } else {
        k = aliasesByRelationshipPath().allKeysForObject(rightAlias);
        relationshipKey = k.count() > 0 ? (String) k.lastObject() : "";
        rightEntity = entityForKeyPath(relationshipKey);
      }
      int dotIndex = relationshipKey.indexOf(".");
      relationshipKey = dotIndex == -1 ? relationshipKey : relationshipKey.substring(relationshipKey.lastIndexOf(".") + 1);
      r = rightEntity.anyRelationshipNamed(relationshipKey);
      // fix from Michael Müller for the case Foo.fooBars.bar has a
      // Bar.foo relationship (instead of Bar.foos)
      if (r == null || r.destinationEntity() != leftEntity) {
        r = leftEntity.anyRelationshipNamed(relationshipKey);
      }
      // timc 2006-02-26 IMPORTANT or quotes are ignored and mixed case
      // field names won't work
      String rightTable;
      String leftTable;
      if (CONFIG.ENABLE_IDENTIFIER_QUOTING) {
        rightTable = rightEntity.valueForSQLExpression(this);
        leftTable = leftEntity.valueForSQLExpression(this);
      } else {
        rightTable = rightEntity.externalName();
        leftTable = leftEntity.externalName();
      }

      // We need the numeric table by removing the leading 't' or 'T' from the table alias
      int leftTableID = Integer.parseInt(leftAlias.substring(1));
     
      // Compute left and right table references
      String leftTableNameAndAlias = leftTable + " " + leftAlias;
      String rightTableNameAndAlias = rightTable + " " + rightAlias;

      // COmpute joinOperation
      String joinOperation = null;
      switch (semantic) {
      case EORelationship.LeftOuterJoin:
        // LEFT OUTER JOIN and LEFT JOIN are equivalent in MySQL
        joinOperation = " LEFT JOIN ";
        break;
      case EORelationship.RightOuterJoin:
        // RIGHT OUTER JOIN and RIGHT JOIN are equivalent in MySQL
        joinOperation = " RIGHT JOIN ";
        break;
      case EORelationship.FullOuterJoin:
        throw new IllegalArgumentException("Unfortunately MySQL does not support FULL OUTER JOIN that is specified for " + leftName + " joining " + rightName + "!");
        //jc.op = " FULL OUTER JOIN ";
        //break;
      case EORelationship.InnerJoin:
        // INNER JOIN and JOIN are equivalent in MySQL
        joinOperation = " JOIN ";
        break;
      }

      // Compute joinCondition
      NSArray<EOJoin> joins = r.joins();
      int joinsCount = joins.count();
      NSMutableArray<String> joinStrings = new NSMutableArray<String>(joinsCount);
      for (int i = 0; i < joinsCount; i++) {
        EOJoin currentJoin = joins.objectAtIndex(i);
        String left;
        String right;
        if (CONFIG.ENABLE_IDENTIFIER_QUOTING) {
          left = leftAlias + "." + sqlStringForSchemaObjectName(currentJoin.sourceAttribute().columnName());
          right = rightAlias + "." + sqlStringForSchemaObjectName(currentJoin.destinationAttribute().columnName());
        } else {
          left = leftAlias + "." + currentJoin.sourceAttribute().columnName();
          right = rightAlias + "." + currentJoin.destinationAttribute().columnName();
        }
        joinStrings.addObject(left + " = " + right);
      }
      String joinCondition = " ON " + joinStrings.componentsJoinedByString(" AND ");
     
      JoinClauseDefinition jc = new JoinClauseDefinition(leftTableNameAndAlias, joinOperation, rightTableNameAndAlias, joinCondition, leftTableID);
      if (!_alreadyJoined.containsObject(jc)) {
        _alreadyJoined.insertObjectAtIndex(jc, 0);
      }
      return null;
    }

    /**
     * Utility that traverses a key path to find the last destination entity
     *
     * @param keyPath
     *            the key path
     * @return the entity at the end of the keypath
     */
    private EOEntity entityForKeyPath(String keyPath) {
      NSArray<String> keys = NSArray.componentsSeparatedByString(keyPath, ".");
      EOEntity ent = entity();

      for (int i = 0; i < keys.count(); i++) {
        String k = keys.objectAtIndex(i);
        EORelationship rel = ent.anyRelationshipNamed(k);
        if (rel == null) {
          // it may be an attribute
          if (ent.anyAttributeNamed(k) != null) {
            break;
          }
          throw new IllegalArgumentException("relationship " + keyPath + " generated null");
        }
        ent = rel.destinationEntity();
      }
      return ent;
    }

    /**
     * Overriden to not call the super implementation. This simply calls our custom assembleJoinClause
     *
     * @param leftName
     *            the table name on the left side of the clause
     * @param rightName
     *            the table name on the right side of the clause
     * @param semantic
     *            the join semantic
     *           
     * kieran copied from PostgresqlExpression
     */
    @Override
    public void addJoinClause(String leftName, String rightName, int semantic) {
      assembleJoinClause(leftName, rightName, semantic);
    }

    /**
     * Helper class that stores a join definition and helps
     * <code>MySQLExpression</code> to assemble the correct join
     * clause.
     *
     * kieran copied from PostgreSQLPlugIn's JoinClause helper class
     */
    public static final class JoinClauseDefinition {
      private final String _table1;
      private final String _op;
      private final String _table2;
      private final String _joinCondition;
      private final int _leftTableID;
      private final String _toString;

      public JoinClauseDefinition(String leftTableNameAndAlias, String joinOperation, String rightTableNameAndAlias, String joinCondition2, int leftTableID) {
        _table1 = leftTableNameAndAlias;
        _op = joinOperation;
        _table2 = rightTableNameAndAlias;
        _joinCondition = joinCondition2;
        _leftTableID = leftTableID;
       
        _toString = _table1 + _op + _table2 + _joinCondition;
      }

      @Override
      public String toString() {
        return _toString;
      }

      @Override
      public boolean equals(Object obj) {
        if (obj == null || !(obj instanceof JoinClauseDefinition)) {
          return false;
        }
        return _toString.equals(obj.toString());
      }

      /* Effective Java #9 : Must override hashCode when overriding equals.
       *
       * (non-Javadoc)
       * @see java.lang.Object#hashCode()
       */
      @Override
      public int hashCode() {
        return _toString.hashCode() + 43;
      }

      /**
       * Property that makes this class "sortable" by left table ID. Needed to correctly
       * assemble a join clause.
       *
       * @return sort key
       */
      public int sortKey() {
        return _leftTableID;
      }
    }
  }

  public static class MySQLSynchronizationFactory extends EOSchemaSynchronizationFactory {

    public MySQLSynchronizationFactory(EOAdaptor adaptor) {
      super(adaptor);
    }

        @Override
        public String _alterPhraseInsertionClausePrefixAtIndex(int columnIndex) {
            return (columnIndex != 0)?"":" ADD ";
        }

    @Override
        protected String formatTableName(String name) {
      return name;
    }

    @Override
    protected String formatColumnName(String name) {
      return name;
    }
   
    @Override
        public NSArray<EOSQLExpression> statementsToConvertColumnType(String columnName, String tableName, ColumnTypes oldType, ColumnTypes newType, EOSchemaGenerationOptions options) {
        String columnTypeString = statementToCreateDataTypeClause(newType);
            StringBuilder sb = new StringBuilder();
            sb.append("ALTER TABLE ").append(formatTableName(tableName));
            sb.append(" MODIFY ").append(formatColumnName(columnName));
            sb.append(' ').append(columnTypeString);
            NSArray<EOSQLExpression> statements = new NSArray<EOSQLExpression>(_expressionForString(sb.toString()));
            return statements;
        }

    @Override
    public NSArray<EOSQLExpression> primaryKeySupportStatementsForEntityGroups(NSArray<NSArray<EOEntity>> entityGroups) {
      String pkTable = ((JDBCAdaptor)adaptor()).plugIn().primaryKeyTableName();
      NSMutableArray<EOSQLExpression> statements = new NSMutableArray<EOSQLExpression>();
      statements.addObject(_expressionForString(new StringBuilder().append("CREATE TABLE ").append(pkTable).append(" (NAME CHAR(40) PRIMARY KEY, PK INT)").toString()));
      return statements;
    }
   
    @Override
        public NSArray<EOSQLExpression> statementsToDeleteColumnNamed(String columnName, String tableName, EOSchemaGenerationOptions options) {
            return new NSArray<EOSQLExpression>(_expressionForString(new StringBuilder().append("ALTER TABLE ").append(tableName).append(" DROP COLUMN ").append(columnName).toString()));
        }
   
    @Override
        public NSArray<EOSQLExpression> statementsToInsertColumnForAttribute(EOAttribute attribute, EOSchemaGenerationOptions options) {
            String columnCreationClause = _columnCreationClauseForAttribute(attribute);
            return new NSArray<EOSQLExpression>(_expressionForString(new StringBuilder().append("ALTER TABLE ").append(attribute.entity().externalName()).append(_alterPhraseInsertionClausePrefixAtIndex(0)).append(columnCreationClause).toString()));
        }
   
    @Override
    public NSArray<EOSQLExpression> statementsToModifyColumnNullRule(String columnName, String tableName, boolean allowsNull, EOSchemaGenerationOptions options) {
        String nullStatement = allowsNull ? " NULL" : " NOT NULL";
        EOAttribute attribute = attributeInEntityWithColumnName(entityForTableName(tableName), columnName);
        String externalType = columnTypeStringForAttribute(attribute);
        return new NSArray<EOSQLExpression>(_expressionForString(new StringBuilder().append("ALTER TABLE ").append(formatTableName(tableName)).append(" MODIFY ").append(formatColumnName(columnName)).append(' ').append(externalType).append(nullStatement).toString()));
    }
   
    @Override
    public boolean supportsDirectColumnNullRuleModification() {
        return true;
    }
   
    @Override
    public NSArray<EOSQLExpression> statementsToRenameColumnNamed(String columnName, String tableName, String newName, EOSchemaGenerationOptions options) {
        EOAttribute attribute = attributeInEntityWithColumnName(entityForTableName(tableName), newName);
        String nullStatement = attribute.allowsNull() ? " NULL" : " NOT NULL";
        String externalType = columnTypeStringForAttribute(attribute);
        return new NSArray<EOSQLExpression>(_expressionForString(new StringBuilder().append("ALTER TABLE ").append(formatTableName(tableName)).append(" CHANGE ").append(formatColumnName(columnName)).append(' ').append(formatColumnName(newName)).append(' ').append(externalType).append(nullStatement).toString()));
    }
   
    @Override
    public boolean supportsDirectColumnRenaming() {
        return true;
    }
   
    private EOEntity entityForTableName(String tableName) {
        EOModelGroup modelGroup = EOModelGroup.globalModelGroup();
            for (EOModel model : modelGroup.models()) {
                for (EOEntity entity : model.entities()) {
                    if (entity.externalName() != null && entity.externalName().equalsIgnoreCase(tableName)) {
                        return entity;
                    }
                }
            }
            return null;
        }
   
    @Override
    public NSArray<EOSQLExpression> dropPrimaryKeySupportStatementsForEntityGroups(NSArray<NSArray<EOEntity>> entityGroups) {
      return new NSArray<EOSQLExpression>(_expressionForString(new StringBuilder().append("DROP TABLE ").append(((JDBCAdaptor)adaptor()).plugIn().primaryKeyTableName()).append(" CASCADE").toString()));
    }

    @Override
    public NSArray<EOSQLExpression> _statementsToDropPrimaryKeyConstraintsOnTableNamed(String tableName) {
      return new NSArray<EOSQLExpression>(_expressionForString(new StringBuilder().append("alter table ").append(tableName).append(" drop primary key").toString()));
    }

    @Override
    public NSArray<EOSQLExpression> foreignKeyConstraintStatementsForRelationship(EORelationship relationship) {
      return null;
    }

    @Override
    public NSArray<EOSQLExpression> statementsToRenameTableNamed(String tableName, String newName, EOSchemaGenerationOptions options) {
      return new NSArray<EOSQLExpression>(_expressionForString(new StringBuilder().append("rename table ").append(tableName).append(" to ").append(newName).toString()));
    }

    @Override
    public boolean supportsSchemaSynchronization() {
      return true;
    }
   
    // Shameless stolen from PostresqlSynchronizationFactory - davidleber
    //
    // 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();
    }
   
    private String statementToCreateDataTypeClause(ColumnTypes columntypes) {
      int size = columntypes.precision();
      if (size == 0) {
        size = columntypes.width();
      }

      if (size == 0) {
        return columntypes.name();
      }

      int scale = columntypes.scale();
      if (scale == 0) {
        return columntypes.name() + "(" + size + ")";
      }

      return columntypes.name() + "(" + size + "," + scale + ")";
    }

  }

  /**
   * <p>WebObjects 5.4's version of JDBCAdaptor will use this in order to
   * assemble the name of the prototype to use when it loads models.</p>
   * @return Name of the plugin.
   */
  @Override
    public String name() {
    return DriverProductName;
  }

  @Override
  public String defaultDriverName() {
    return DriverClassName;
  }

  @Override
  public String databaseProductName() {
    return DriverProductName;
  }

  @Override
  public Class<MySQLExpression> defaultExpressionClass() {
    try {
      if (NSProperties.booleanForKey("com.webobjects.jdbcadaptor.MySQLExpression.enable")) {
        return com.webobjects.jdbcadaptor.MySQLPlugIn.MySQLExpression.class;
      }
    } catch (NullPointerException ex) {
      // property was not set
    }
    return com.webobjects.jdbcadaptor._MySQLPlugIn.MySQLExpression.class;
  }

  @Override
  public EOSchemaSynchronizationFactory createSchemaSynchronizationFactory() {
    return new com.webobjects.jdbcadaptor._MySQLPlugIn.MySQLSynchronizationFactory(_adaptor);
  }

  @Override
  @SuppressWarnings({ "unchecked", "rawtypes" })
  public NSDictionary<String, Object> jdbcInfo() {

    NSDictionary<String, Object> jdbcInfo;
    // have a look at the JDBC connection URL to see if the flag has been set to
    // specify that the hard-coded jdbcInfo information should be used.
    if(shouldUseBundledJdbcInfo()) {
      if(NSLog.debugLoggingAllowedForLevel(NSLog.DebugLevelDetailed)) {
        NSLog.debug.appendln("Loading jdbcInfo from JDBCInfo.plist as opposed to using the JDBCPlugIn default implementation.");
      }

      InputStream jdbcInfoStream = NSBundle.bundleForClass(getClass()).inputStreamForResourcePath("JDBCInfo.plist");
      if (jdbcInfoStream == null) {
        throw new IllegalStateException("Unable to find 'JDBCInfo.plist' in this plugin jar.");
      }

      try {
        jdbcInfo = (NSDictionary<String, Object>) NSPropertyListSerialization.propertyListFromData(new NSData(jdbcInfoStream, 2048), "US-ASCII");
      } catch (IOException e) {
        throw new RuntimeException("Failed to load 'JDBCInfo.plist' from this plugin jar.", e);
      } finally {
        try { jdbcInfoStream.close(); } catch (IOException e) {}
      }

      } else {

      NSMutableDictionary<String, Object> mutableInfo = super.jdbcInfo().mutableClone();
      NSMutableDictionary<String, NSDictionary> typeInfo = ((NSDictionary<String, NSDictionary>)mutableInfo.objectForKey("typeInfo")).mutableClone();
      NSDictionary textTypeInfo = typeInfo.objectForKey("TEXT");
      if(textTypeInfo != null) {
        Object rawCreateParams = textTypeInfo.objectForKey("createParams");
        if(!rawCreateParams.equals("1")) {
          NSMutableDictionary newRawTypeInfo = textTypeInfo.mutableClone();
          newRawTypeInfo.setObjectForKey("1", "createParams");
          typeInfo.setObjectForKey(newRawTypeInfo, "RAW");
        }
      }
      JDBCPlugIn._takeValueForKeyPath(typeInfo, "0", "BLOB", "createParams");
      JDBCPlugIn._takeValueForKeyPath(typeInfo, "0", "LONGBLOB", "createParams");
      JDBCPlugIn._takeValueForKeyPath(typeInfo, "0", "MEDIUMBLOB", "createParams");
      JDBCPlugIn._takeValueForKeyPath(typeInfo, "0", "TINYBLOB", "createParams");
      mutableInfo.setObjectForKey(typeInfo, "typeInfo");
     
      NSLog.debug.appendln(
          new StringBuilder("fetched MySQL (")
          .append(databaseProductName())
          .append(") JDBC Info = ")
          .append(mutableInfo)
          .toString()
          );
     
      // Write a fresh copy of JDBCInfo.plist to /tmp
      //writeJDBCInfo(mutableInfo);
     
      jdbcInfo = mutableInfo.immutableClone();
      }
    return jdbcInfo;
  }

  @Override
  public Object fetchBLOB(ResultSet rs, int column, EOAttribute attribute, boolean materialize) throws SQLException {
    NSData data = null;
    Blob blob = rs.getBlob(column);
    if(blob == null) { return null; }
    if(!materialize) { return blob; }
    InputStream stream = blob.getBinaryStream();
    try {
      int chunkSize = (int)blob.length();
      if(chunkSize == 0) {
        data = NSData.EmptyData;
      } else {
        data = new NSData(stream, chunkSize);
      }
    } catch(IOException e) {
      throw new JDBCAdaptorException(e.getMessage(), null);
    } finally {
      try {if(stream != null) stream.close(); } catch(IOException e) { /* Nothing we can do */ };
    }
    return data;
  }
 
  /**
   * <P>This method returns true if the connection URL for the
   * database has a special flag on it which indicates to the
   * system that the jdbcInfo which has been bundled into the
   * plugin is acceptable to use in place of actually going to
   * the database and getting it. Default is false.
   * @return the flag set on the jdbc url with 'useBundledJdbcInfo'
   */
  protected boolean shouldUseBundledJdbcInfo() {
    boolean shouldUseBundledJdbcInfo = false;
    String url = connectionURL();
    if (url != null) {
      shouldUseBundledJdbcInfo = url.toLowerCase().matches(".*(\\?|\\?.*&)" + _MySQLPlugIn.QUERY_STRING_USE_BUNDLED_JDBC_INFO.toLowerCase() + "=(true|yes)(\\&|$)");
    }
    return shouldUseBundledJdbcInfo;
  }

  protected void writeJDBCInfo(NSDictionary<String, Object> jdbcInfo) {
    try {
      String jdbcInfoS = NSPropertyListSerialization.stringFromPropertyList(jdbcInfo);
      FileOutputStream fos = new FileOutputStream("/tmp/JDBCInfo.plist");
      fos.write(jdbcInfoS.getBytes());
      fos.close();
    } catch(Exception e) {
      throw new IllegalStateException("problem writing JDBCInfo.plist",e);
    }
  }

}
TOP

Related Classes of com.webobjects.jdbcadaptor._MySQLPlugIn$MySQLSynchronizationFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.