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