001package io.ebeaninternal.dbmigration.model;
002
003import io.ebean.config.DatabaseConfig;
004import io.ebean.config.dbplatform.DatabasePlatform;
005import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
006import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler;
007import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
008import io.ebeaninternal.dbmigration.ddlgeneration.PlatformDdlBuilder;
009import io.ebeaninternal.dbmigration.ddlgeneration.platform.PlatformDdl;
010import io.ebeaninternal.dbmigration.migration.ChangeSet;
011import io.ebeaninternal.dbmigration.migration.ChangeSetType;
012import io.ebeaninternal.dbmigration.migration.Migration;
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015
016import java.io.File;
017import java.io.FileWriter;
018import java.io.IOException;
019import java.io.Writer;
020import java.util.List;
021
022/**
023 * Writes migration changes as platform specific DDL.
024 */
025public class PlatformDdlWriter {
026
027  private static final Logger logger = LoggerFactory.getLogger(PlatformDdlWriter.class);
028
029  private final DatabaseConfig databaseConfig;
030
031  private final PlatformDdl platformDdl;
032
033  private final int lockTimeoutSeconds;
034
035  public PlatformDdlWriter(DatabasePlatform platform, DatabaseConfig dbConfig, int lockTimeoutSeconds) {
036    this.platformDdl = PlatformDdlBuilder.create(platform);
037    this.databaseConfig = dbConfig;
038    this.lockTimeoutSeconds = lockTimeoutSeconds;
039  }
040
041  /**
042   * Write the migration as platform specific ddl.
043   */
044  public void processMigration(Migration dbMigration, DdlWrite write, File writePath, String fullVersion) throws IOException {
045
046    DdlHandler handler = handler();
047    handler.generateProlog(write);
048    if (lockTimeoutSeconds > 0) {
049      String lockSql = platformDdl.setLockTimeout(lockTimeoutSeconds);
050      if (lockSql != null) {
051        write.apply().append(lockSql).endOfStatement().newLine();
052      }
053    }
054
055    List<ChangeSet> changeSets = dbMigration.getChangeSet();
056    for (ChangeSet changeSet : changeSets) {
057      if (isApply(changeSet)) {
058        handler.generate(write, changeSet);
059      }
060    }
061    handler.generateEpilog(write);
062
063    writePlatformDdl(write, writePath, fullVersion);
064  }
065
066  /**
067   * Return true if the changeSet is APPLY and not empty.
068   */
069  private boolean isApply(ChangeSet changeSet) {
070    return changeSet.getType() == ChangeSetType.APPLY && !changeSet.getChangeSetChildren().isEmpty();
071  }
072
073  /**
074   * Write the ddl files.
075   */
076  protected void writePlatformDdl(DdlWrite write, File resourcePath, String fullVersion) throws IOException {
077    if (!write.isApplyEmpty()) {
078      try (FileWriter applyWriter = createWriter(resourcePath, fullVersion, ".sql")) {
079        writeApplyDdl(applyWriter, write);
080        applyWriter.flush();
081      }
082    }
083  }
084
085  protected FileWriter createWriter(File path, String fullVersion, String suffix) throws IOException {
086    File applyFile = new File(path, fullVersion + suffix);
087    return new FileWriter(applyFile);
088  }
089
090  /**
091   * Write the 'Apply' DDL buffers to the writer.
092   */
093  protected void writeApplyDdl(Writer writer, DdlWrite write) throws IOException {
094    String header = databaseConfig.getDdlHeader();
095    if (header != null && !header.isEmpty()) {
096      writer.append(header).append('\n');
097    }
098    // merge the apply buffers in the appropriate order
099    prependDropDependencies(writer, write.applyDropDependencies());
100    writer.append("-- apply changes\n");
101    writer.append(write.apply().getBuffer());
102    writer.append(write.applyForeignKeys().getBuffer());
103    writer.append(write.applyHistoryView().getBuffer());
104    writer.append(write.applyHistoryTrigger().getBuffer());
105  }
106
107  private void prependDropDependencies(Writer writer, DdlBuffer buffer) throws IOException {
108    if (!buffer.isEmpty()) {
109      writer.append("-- drop dependencies\n");
110      writer.append(buffer.getBuffer());
111      writer.append("\n");
112    }
113  }
114
115  /**
116   * Return the platform specific DdlHandler (to generate DDL).
117   */
118  protected DdlHandler handler() {
119    return platformDdl.createDdlHandler(databaseConfig);
120  }
121
122  /**
123   * Return a sub directory (for multi-platform ddl generation).
124   */
125  public File subPath(File path, String suffix) {
126    File subPath = new File(path, suffix);
127    if (!subPath.exists()) {
128      if (!subPath.mkdirs()) {
129        logger.error("failed to create directories for " + subPath.getAbsolutePath());
130      }
131    }
132    return subPath;
133  }
134
135}