001package io.ebeaninternal.dbmigration.ddlgeneration.platform;
002
003import io.ebean.config.dbplatform.DatabasePlatform;
004import io.ebean.util.StringHelper;
005import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
006import io.ebeaninternal.dbmigration.migration.AlterColumn;
007import io.ebeaninternal.dbmigration.migration.Column;
008
009import java.io.IOException;
010import java.util.Collection;
011
012/**
013 * MySql specific DDL.
014 */
015public class MySqlDdl extends PlatformDdl {
016
017  // check constraint support is disabled by default. See https://groups.google.com/forum/#!topic/ebean/luFN-2xBkUw
018  // this flag is for compatibility. Use it with care.
019  private static final boolean USE_CHECK_CONSTRAINT = Boolean.getBoolean("ebean.mysql.useCheckConstraint");
020
021  public MySqlDdl(DatabasePlatform platform) {
022    super(platform);
023    this.alterColumn = "modify";
024    this.dropUniqueConstraint = "drop index";
025    this.historyDdl = new MySqlHistoryDdl();
026    this.inlineComments = true;
027  }
028
029  /**
030   * Return the drop index statement.
031   */
032  @Override
033  public String dropIndex(String indexName, String tableName, boolean concurrent) {
034    return "drop index " + maxConstraintName(indexName) + " on " + tableName;
035  }
036
037  /**
038   * Return the drop foreign key clause.
039   */
040  @Override
041  public String alterTableDropForeignKey(String tableName, String fkName) {
042    return "alter table " + tableName + " drop foreign key " + maxConstraintName(fkName);
043  }
044
045  @Override
046  public String createCheckConstraint(String ckName, String checkConstraint) {
047    if (USE_CHECK_CONSTRAINT) {
048      return super.createCheckConstraint(ckName, checkConstraint);
049    } else {
050      return null;
051    }
052  }
053
054  @Override
055  public String alterTableAddCheckConstraint(String tableName, String checkConstraintName, String checkConstraint) {
056    if (USE_CHECK_CONSTRAINT) {
057      return super.alterTableAddCheckConstraint(tableName, checkConstraintName, checkConstraint);
058    } else {
059      return null;
060    }
061  }
062
063  @Override
064  public String alterTableDropConstraint(String tableName, String constraintName) {
065    // drop constraint not supported in MySQL 5.7 and 8.0 but starting with MariaDB
066    // 10.2.1 CHECK is evaluated
067    if (USE_CHECK_CONSTRAINT) {
068      StringBuilder sb = new StringBuilder();
069      // statement for MySQL >= 8.0.16
070      sb.append("/*!80016 alter table ").append(tableName);
071      sb.append(" drop check ").append(constraintName).append(" */;\n");
072      // statement for MariaDB >= 10.2.1
073      sb.append("/*M!100201 ");
074      sb.append(super.alterTableDropConstraint(tableName, constraintName));
075      sb.append(" */");
076      return sb.toString();
077    } else {
078      return null;
079    }
080  }
081
082  @Override
083  public String alterColumnType(String tableName, String columnName, String type) {
084    // can't alter itself - done in alterColumnBaseAttributes()
085    return null;
086  }
087
088  @Override
089  public String alterColumnNotnull(String tableName, String columnName, boolean notnull) {
090    // can't alter itself - done in alterColumnBaseAttributes()
091    return null;
092  }
093
094  @Override
095  public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
096    String suffix = DdlHelp.isDropDefault(defaultValue) ? columnDropDefault : columnSetDefault + " " + convertDefaultValue(defaultValue);
097    return "alter table " + tableName + " alter " + columnName + " " + suffix;
098  }
099
100  @Override
101  public String alterColumnBaseAttributes(AlterColumn alter) {
102    if (alter.getType() == null && alter.isNotnull() == null) {
103      // No type change or notNull change
104      // defaultValue change already handled in alterColumnDefaultValue
105      return null;
106    }
107    String tableName = alter.getTableName();
108    String columnName = alter.getColumnName();
109    String type = alter.getType() != null ? alter.getType() : alter.getCurrentType();
110    type = convert(type);
111    boolean notnull = (alter.isNotnull() != null) ? alter.isNotnull() : Boolean.TRUE.equals(alter.isCurrentNotnull());
112    String notnullClause = notnull ? " not null" : "";
113
114    return "alter table " + tableName + " modify " + columnName + " " + type + notnullClause;
115  }
116
117  @Override
118  protected void writeColumnDefinition(DdlBuffer buffer, Column column, DdlIdentity identity) throws IOException {
119    super.writeColumnDefinition(buffer, column, identity);
120    String comment = column.getComment();
121    if (!StringHelper.isNull(comment)) {
122      // in mysql 5.5 column comment save in information_schema.COLUMNS.COLUMN_COMMENT(VARCHAR 1024)
123      if (comment.length() > 500) {
124        comment = comment.substring(0, 500);
125      }
126      buffer.append(String.format(" comment '%s'", comment));
127    }
128  }
129
130  @Override
131  public void inlineTableComment(DdlBuffer apply, String tableComment) throws IOException {
132    if (tableComment.length() > 1000) {
133      tableComment = tableComment.substring(0, 1000);
134    }
135    apply.append(" comment='").append(tableComment).append("'");
136  }
137
138  /**
139   * Add table comment as a separate statement (from the create table statement).
140   */
141  @Override
142  public void addTableComment(DdlBuffer apply, String tableName, String tableComment) throws IOException {
143    if (DdlHelp.isDropComment(tableComment)) {
144      tableComment = "";
145    }
146    apply.append(String.format("alter table %s comment = '%s'", tableName, tableComment)).endOfStatement();
147  }
148
149  @Override
150  public void addColumnComment(DdlBuffer apply, String table, String column, String comment) {
151    // alter comment currently not supported as it requires to repeat whole column definition
152  }
153
154
155  /**
156   * Locks all tables for triggers that have to be updated.
157   */
158  @Override
159  public void lockTables(DdlBuffer buffer, Collection<String> tables) throws IOException {
160    if (!tables.isEmpty()) {
161      buffer.append("lock tables ");
162      int i = 0;
163      for (String table : tables) {
164        if (i > 0) {
165          buffer.append(", ");
166        }
167        buffer.append(table).append(" write");
168        i++;
169      }
170      buffer.endOfStatement();
171    }
172  }
173
174  /**
175   * Unlocks all tables for triggers that have to be updated.
176   */
177  @Override
178  public void unlockTables(DdlBuffer buffer, Collection<String> tables) throws IOException {
179    buffer.append("unlock tables").endOfStatement();
180  }
181
182}