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}