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}