From 33978b2ca9ff4ce4be79c0510ce0a6266036b262 Mon Sep 17 00:00:00 2001 From: Jon Knight Date: Mon, 19 Jun 2023 10:54:45 +0100 Subject: [PATCH 01/10] Add extra fields to UpgradeAudit table --- .../morf/upgrade/AuditRecordHelper.java | 133 +++++++++++++++++- .../GraphBasedUpgradeSchemaChangeVisitor.java | 9 ++ .../GraphBasedUpgradeTraversalService.java | 2 +- .../morf/upgrade/InlineTableUpgrader.java | 11 ++ .../morf/upgrade/SchemaChangeSequence.java | 32 ++++- .../morf/upgrade/SchemaChangeVisitor.java | 9 ++ .../morf/upgrade/UpgradeStatus.java | 5 + .../morf/upgrade/UpgradeStepStatus.java | 8 ++ .../db/DatabaseUpgradeTableContribution.java | 9 +- .../AddExtraLoggingToUpgradeAuditTable.java | 57 ++++++++ .../morf/upgrade/upgrade/UpgradeSteps.java | 3 +- .../morf/upgrade/TestUpgrade.java | 7 +- 12 files changed, 275 insertions(+), 10 deletions(-) create mode 100644 morf-core/src/main/java/org/alfasoftware/morf/upgrade/UpgradeStepStatus.java create mode 100644 morf-core/src/main/java/org/alfasoftware/morf/upgrade/upgrade/AddExtraLoggingToUpgradeAuditTable.java diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java index d0166a650..fb4439150 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java @@ -16,22 +16,53 @@ package org.alfasoftware.morf.upgrade; import static org.alfasoftware.morf.sql.SqlUtils.cast; +import static org.alfasoftware.morf.sql.SqlUtils.field; +import static org.alfasoftware.morf.sql.SqlUtils.literal; +import static org.alfasoftware.morf.sql.SqlUtils.tableRef; import static org.alfasoftware.morf.sql.element.Function.dateToYyyyMMddHHmmss; import static org.alfasoftware.morf.sql.element.Function.now; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.UUID; import org.alfasoftware.morf.metadata.DataType; import org.alfasoftware.morf.metadata.Schema; import org.alfasoftware.morf.sql.InsertStatement; +import org.alfasoftware.morf.sql.UpdateStatement; import org.alfasoftware.morf.sql.element.FieldLiteral; import org.alfasoftware.morf.sql.element.TableReference; +import org.alfasoftware.morf.upgrade.db.DatabaseUpgradeTableContribution; /** * A helper class to add audit records. */ public class AuditRecordHelper { + + private static String serverName; + + /* + //TODO tidy this into proper JavaDocs. + + +UpgradeAudit Table Schema: + + column("upgradeUUID", DataType.STRING, 100).primaryKey(), + column("description", DataType.STRING, 200).nullable(), + column("appliedTime", DataType.DECIMAL, 14).nullable(), + column("status", DataType.STRING, 10).nullable(), + column("server", DataType.STRING, 100).nullable(), + column("processingTimeMs", DataType.DECIMAL, 14).nullable() + + Initial insert sets upgradeUUID, description, server, appliedTime, status = SCHEDULED + + Update appliedTime, server status = RUNNING when start running + + On result set STATUS = COMPLETED/FAILED and set processingTimeMs + + */ + /** * Add the audit record, writing out the SQL for the insert. * @@ -42,7 +73,8 @@ public class AuditRecordHelper { * @param uuid The UUID of the step which has been applied * @param description The description of the step. */ - public static void addAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid, String description) { + @Deprecated + public static void addAuditRecordOriginal(SchemaChangeVisitor visitor, Schema schema, UUID uuid, String description) { // There's no point adding an UpgradeAudit row if the table isn't there. if (!schema.tableExists("UpgradeAudit")) return; @@ -53,6 +85,105 @@ public static void addAuditRecord(SchemaChangeVisitor visitor, Schema schema, UU } + /** + * Add the audit record, writing out the SQL for the insert. + * + * @see org.alfasoftware.morf.upgrade.SchemaChangeVisitor#addAuditRecord(java.util.UUID, java.lang.String) + * + * @param visitor The schema change visitor adding the audit record. + * @param schema The schema to add the audit record to. + * @param uuid The UUID of the step which has been applied + * @param description The description of the step. + */ + public static void addAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid, String description) { + // There's no point adding an UpgradeAudit row if the table isn't there. + if (!schema.tableExists("UpgradeAudit")) + return; + + InsertStatement auditRecord = createExtendedAuditInsertStatement(uuid, description); + + visitor.visit(new ExecuteStatement(auditRecord)); + } + + + public static void updateRunningAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid) { + // There's no point adding an UpgradeAudit row if the table isn't there. + if (!schema.tableExists("UpgradeAudit")) + return; + + UpdateStatement auditRecord = createRunningAuditUpdateStatement(uuid); + + visitor.visit(new ExecuteStatement(auditRecord)); + } + + + public static void updateFinishedAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid, long processingTimeMs, boolean success) { + // There's no point adding an UpgradeAudit row if the table isn't there. + if (!schema.tableExists("UpgradeAudit")) + return; + + UpdateStatement auditRecord = createFinishedAuditUpdateStatement(uuid, processingTimeMs, success); + + visitor.visit(new ExecuteStatement(auditRecord)); + } + + + /** + * Utility method to get a name for the server that this class is executing on. Value is cached after the first execution + * + * @return host name + */ + private static String getServerName() { + if (serverName == null) { + try { + serverName = InetAddress.getLocalHost().getHostName(); + } catch (UnknownHostException e) { + serverName = "unknown server"; + } + } + + return serverName; + } + + + + + // Initial insert sets upgradeUUID, description, server, appliedTime, status = SCHEDULED + public static InsertStatement createExtendedAuditInsertStatement(UUID uuid, String description) { + InsertStatement auditRecord = new InsertStatement().into( + tableRef("UpgradeAudit")).values( + literal(uuid.toString()).as("upgradeUUID"), + literal(description).as("description"), + cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as("appliedTime"), + literal(UpgradeStepStatus.SCHEDULED.name()).as("status"), + new FieldLiteral(getServerName()).as("server") + ); + return auditRecord; + } + + public static UpdateStatement createRunningAuditUpdateStatement(UUID uuid) { + UpdateStatement auditRecord = new UpdateStatement(new TableReference(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME)) + .set(cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as("appliedTime")) + .set(literal(UpgradeStepStatus.RUNNING.name()).as("status")) + .set(literal(getServerName()).as("server")) + .where(field("upgradeUUID").eq(uuid.toString())); + return auditRecord; + } + + public static UpdateStatement createFinishedAuditUpdateStatement(UUID uuid, long processingTimeMs, boolean success) { + UpdateStatement auditRecord = new UpdateStatement(new TableReference(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME)) + .set(literal(processingTimeMs).as("processingTimeMs")) + .set(literal(success ? UpgradeStepStatus.COMPLETED.name() : UpgradeStepStatus.FAILED.name()).as("status")) + .where(field("upgradeUUID").eq(uuid.toString())); + return auditRecord; + } + +// column("status", DataType.STRING, 10).nullable(), +// column("server", DataType.STRING, 100).nullable(), +// column("processingTimeMs", DataType.DECIMAL, 14).nullable() + + + /** * Returns an {@link InsertStatement} used to be added to the upgrade audit table. * diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java index d825fa3c2..2de669c55 100644 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java @@ -179,6 +179,15 @@ public void addAuditRecord(UUID uuid, String description) { AuditRecordHelper.addAuditRecord(this, sourceSchema, uuid, description); } + @Override + public void updateRunningAuditRecord(UUID uuid) { + AuditRecordHelper.updateRunningAuditRecord(this, sourceSchema, uuid); + } + + @Override + public void updateFinishedAuditRecord(UUID uuid, long processingTimeMs, boolean success) { + AuditRecordHelper.updateFinishedAuditRecord(this, sourceSchema, uuid, processingTimeMs, success); + } /** * Set the current {@link GraphBasedUpgradeNode} which is being processed. diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeTraversalService.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeTraversalService.java index 69f1dd54c..ae515ec39 100644 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeTraversalService.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeTraversalService.java @@ -143,7 +143,7 @@ public void waitForReadyToExecuteNode() throws InterruptedException { while(readyToExecuteNodes.isEmpty() && !allNodesCompletedNoLock()) { // The result of this await is (indirectly) checked by the wait loop // so there is no need to check the result of the await (so NOSONAR). - newReadyToExecuteNode.await(500, TimeUnit.MILLISECONDS); // NOSONAR + newReadyToExecuteNode.await(500, TimeUnit.MILLISECONDS); //NOSONAR } } catch (InterruptedException e) { LOG.error("InterruptedException in GraphBasedUpgradeService.waitForAllNodesToBeCompleted", e); diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java index d89fd1ed6..c33d8f3e1 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java @@ -248,6 +248,17 @@ public void addAuditRecord(UUID uuid, String description) { } + @Override + public void updateRunningAuditRecord(UUID uuid) { + AuditRecordHelper.updateRunningAuditRecord(this,currentSchema, uuid); + } + + @Override + public void updateFinishedAuditRecord(UUID uuid, long processingTimeMs, boolean success) { + AuditRecordHelper.updateFinishedAuditRecord(this, currentSchema, uuid, processingTimeMs, success); + } + + /** * @see org.alfasoftware.morf.upgrade.SchemaChangeVisitor#startStep(java.lang.Class) */ diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java index 7c05e7f47..ec11b12a7 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java @@ -32,6 +32,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; +import org.joda.time.Instant; +import org.joda.time.Interval; /** * Tracks a sequence of {@link SchemaChange}s as various {@link SchemaEditor} @@ -137,14 +139,31 @@ public UpgradeTableResolution getUpgradeTableResolution() { * @param visitor The schema change visitor against which to write the changes. */ public void applyTo(SchemaChangeVisitor visitor) { + + // Add all audit records + for (UpgradeStepWithChanges changesForStep : allChanges) { + visitor.addAuditRecord(changesForStep.getUUID(), changesForStep.getDescription()); + } + for (UpgradeStepWithChanges changesForStep : allChanges) { try { + // Start timer for this UpgradeStep + Instant startInstant = Instant.now(); + // Update Audit record to show upgrade step is running + visitor.updateRunningAuditRecord(changesForStep.getUUID()); + // Run prerequisites visitor.startStep(changesForStep.getUpgradeClass()); + + // Apply each change for (SchemaChange change : changesForStep.getChanges()) { change.accept(visitor); } - visitor.addAuditRecord(changesForStep.getUUID(), changesForStep.getDescription()); + + // Update Audit Record will successful run + visitor.updateFinishedAuditRecord(changesForStep.getUUID(), new Interval(startInstant, Instant.now()).toDurationMillis(), true); } catch (Exception e) { + // Set Audit Record to failed then throw runtime exception + visitor.updateFinishedAuditRecord(changesForStep.getUUID(), 0, false); throw new RuntimeException("Failed to apply step: [" + changesForStep.getUpgradeClass() + "]", e); } } @@ -370,7 +389,7 @@ public void addTableFrom(Table table, SelectStatement select) { /** - * @see org.alfasoftware.morf.upgrade.SchemaEditor#analyseTable(org.alfasoftware.morf.metadata.Table) + * @see org.alfasoftware.morf.upgrade.SchemaEditor#analyseTable(String) */ @Override public void analyseTable(String tableName) { @@ -500,6 +519,15 @@ public void addAuditRecord(java.util.UUID uuid, String description) { // no-op here. We don't need to record the UUIDs until we actually apply the changes. } + @Override + public void updateRunningAuditRecord(java.util.UUID uuid) { + // no-op here. We don't need to record the UUIDs until we actually apply the changes. + } + + @Override + public void updateFinishedAuditRecord(java.util.UUID uuid, long processingTimeMs, boolean success) { + // no-op here. We don't need to record the UUIDs until we actually apply the changes. + } @Override public void startStep(Class upgradeClass) { diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java index 33d5829ee..a2b01c310 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java @@ -16,6 +16,8 @@ package org.alfasoftware.morf.upgrade; +import java.util.UUID; + /** * Interface for any upgrade / downgrade strategy which handles all the * defined {@link SchemaChange} implementations. @@ -149,6 +151,13 @@ public interface SchemaChangeVisitor { public void addAuditRecord(java.util.UUID uuid, String description); +//TODO Javadocs + public void updateRunningAuditRecord(UUID uuid); + + //TODO Javadocs + public void updateFinishedAuditRecord(UUID uuid, long processingTimeMs, boolean success); + + /** * Invoked before the changes from each upgrade step are applied. * diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/UpgradeStatus.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/UpgradeStatus.java index a48322d67..392435221 100644 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/UpgradeStatus.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/UpgradeStatus.java @@ -43,6 +43,11 @@ public enum UpgradeStatus { */ IN_PROGRESS, + /** + * Upgrade failed. + */ + FAILED, + /** * Upgrade has been completed. */ diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/UpgradeStepStatus.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/UpgradeStepStatus.java new file mode 100644 index 000000000..aaf8fb242 --- /dev/null +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/UpgradeStepStatus.java @@ -0,0 +1,8 @@ +package org.alfasoftware.morf.upgrade; + +public enum UpgradeStepStatus { + SCHEDULED, + RUNNING, + FAILED, + COMPLETED +} diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/db/DatabaseUpgradeTableContribution.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/db/DatabaseUpgradeTableContribution.java index df29f0945..202ef186e 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/db/DatabaseUpgradeTableContribution.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/db/DatabaseUpgradeTableContribution.java @@ -48,9 +48,12 @@ public class DatabaseUpgradeTableContribution implements TableContribution { public static Table upgradeAuditTable() { return table(UPGRADE_AUDIT_NAME) .columns( - column("upgradeUUID", DataType.STRING, 100).primaryKey(), - column("description", DataType.STRING, 200).nullable(), - column("appliedTime", DataType.DECIMAL, 14).nullable() + column("upgradeUUID", DataType.STRING, 100).primaryKey(), + column("description", DataType.STRING, 200).nullable(), + column("appliedTime", DataType.DECIMAL, 14).nullable(), + column("status", DataType.STRING, 10).nullable(), + column("server", DataType.STRING, 100).nullable(), + column("processingTimeMs", DataType.DECIMAL, 14).nullable() ); } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/upgrade/AddExtraLoggingToUpgradeAuditTable.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/upgrade/AddExtraLoggingToUpgradeAuditTable.java new file mode 100644 index 000000000..a7846d3fd --- /dev/null +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/upgrade/AddExtraLoggingToUpgradeAuditTable.java @@ -0,0 +1,57 @@ +package org.alfasoftware.morf.upgrade.upgrade; + +import org.alfasoftware.morf.metadata.DataType; +import org.alfasoftware.morf.sql.UpdateStatement; +import org.alfasoftware.morf.sql.element.FieldLiteral; +import org.alfasoftware.morf.sql.element.TableReference; +import org.alfasoftware.morf.upgrade.DataEditor; +import org.alfasoftware.morf.upgrade.SchemaEditor; +import org.alfasoftware.morf.upgrade.Sequence; +import org.alfasoftware.morf.upgrade.UUID; +import org.alfasoftware.morf.upgrade.UpgradeStep; +import org.alfasoftware.morf.upgrade.UpgradeStepStatus; +import org.alfasoftware.morf.upgrade.Version; +import org.alfasoftware.morf.upgrade.db.DatabaseUpgradeTableContribution; + +import static org.alfasoftware.morf.metadata.SchemaUtils.column; +import static org.alfasoftware.morf.sql.SqlUtils.field; +import static org.alfasoftware.morf.sql.element.Criterion.isNull; + +@Version("2.5.2") +@Sequence(1686844860) +@UUID("47832d23-f1e1-422f-b6de-b76e57517334") +public class AddExtraLoggingToUpgradeAuditTable implements UpgradeStep { + + private final String statusColumn = "status"; + private final String serverColumn = "server"; + private final String processingTimeMsColumn = "processingTimeMs"; + + + @Override + public String getJiraId() { + return "MORF-72"; + } + + @Override + public String getDescription() { + return "Add extra logging columns to the UpgradeAudit table"; + } + + @Override + public void execute(SchemaEditor schema, DataEditor data) { + + schema.addColumn(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME, + column(statusColumn, DataType.STRING, 10).nullable()); + schema.addColumn(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME, + column(serverColumn, DataType.STRING, 100).nullable()); + schema.addColumn(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME, + column(processingTimeMsColumn, DataType.DECIMAL, 14).nullable()); + + data.executeStatement( + new UpdateStatement(new TableReference(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME)) + .set(new FieldLiteral(UpgradeStepStatus.COMPLETED.name()).as(statusColumn)) + .where(isNull(field(statusColumn))) + ); + + } +} diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/upgrade/UpgradeSteps.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/upgrade/UpgradeSteps.java index ea69659e7..e3c34c655 100644 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/upgrade/UpgradeSteps.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/upgrade/UpgradeSteps.java @@ -9,6 +9,7 @@ public class UpgradeSteps { public static final List> LIST = ImmutableList.of( - AddDeployedViewsSqlDefinition.class + AddDeployedViewsSqlDefinition.class, + AddExtraLoggingToUpgradeAuditTable.class ); } diff --git a/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestUpgrade.java b/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestUpgrade.java index 9444cc7af..d9c5feaf7 100755 --- a/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestUpgrade.java +++ b/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestUpgrade.java @@ -179,7 +179,7 @@ public void testUpgrade() throws SQLException { .findPath(targetSchema, upgradeSteps, Lists.newArrayList("^Drivers$", "^EXCLUDE_.*$"), mockConnectionResources.getDataSource()); assertEquals("Should be two steps.", 2, results.getSteps().size()); - assertEquals("Number of SQL statements", 18, results.getSql().size()); // Includes statements to create, truncate and then drop temp table, also 2 comments + assertEquals("Number of SQL statements", 22, results.getSql().size()); // Includes statements to create, truncate and then drop temp table, also 2 comments } @@ -885,7 +885,10 @@ private static Table upgradeAudit() { versionColumn(), column("upgradeUUID", DataType.STRING, 100).nullable(), column("description", DataType.STRING, 200).nullable(), - column("appliedTime", DataType.BIG_INTEGER).nullable() + column("appliedTime", DataType.BIG_INTEGER).nullable(), + column("status", DataType.STRING, 10).nullable(), + column("server", DataType.STRING, 100).nullable(), + column("processingTimeMs", DataType.DECIMAL, 14).nullable() ); } From 4fef808a0df0f8e4d249e892fa6405157bf4be0a Mon Sep 17 00:00:00 2001 From: Jon Knight Date: Fri, 23 Jun 2023 12:54:20 +0100 Subject: [PATCH 02/10] Work In Progress --- .../morf/upgrade/AuditRecordHelper.java | 57 ++++++++++++-- .../GraphBasedUpgradeSchemaChangeVisitor.java | 5 +- .../morf/upgrade/InlineTableUpgrader.java | 4 +- .../morf/upgrade/SchemaChangeSequence.java | 10 ++- .../morf/upgrade/SchemaChangeVisitor.java | 2 +- .../db/DatabaseUpgradeTableContribution.java | 3 +- .../morf/upgrade/TestAuditRecordHelper.java | 38 ++++++++- .../morf/upgrade/TestFullDeployment.java | 78 +++++++++++++++++++ 8 files changed, 182 insertions(+), 15 deletions(-) diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java index fb4439150..5d02ec2e1 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java @@ -19,8 +19,10 @@ import static org.alfasoftware.morf.sql.SqlUtils.field; import static org.alfasoftware.morf.sql.SqlUtils.literal; import static org.alfasoftware.morf.sql.SqlUtils.tableRef; +import static org.alfasoftware.morf.sql.element.Function.addDays; import static org.alfasoftware.morf.sql.element.Function.dateToYyyyMMddHHmmss; import static org.alfasoftware.morf.sql.element.Function.now; +import static org.alfasoftware.morf.upgrade.db.DatabaseUpgradeTableContribution.UPGRADE_STEP_DESCRIPTION_LENGTH; import java.net.InetAddress; import java.net.UnknownHostException; @@ -42,6 +44,9 @@ public class AuditRecordHelper { private static String serverName; + // variable to track whether the status, server and processingTimeMs columns have been added + private static boolean extendedUpgradeAuditTableRowsPresent; + /* //TODO tidy this into proper JavaDocs. @@ -74,7 +79,7 @@ public class AuditRecordHelper { * @param description The description of the step. */ @Deprecated - public static void addAuditRecordOriginal(SchemaChangeVisitor visitor, Schema schema, UUID uuid, String description) { + public static void addAuditRecordNoStatus(SchemaChangeVisitor visitor, Schema schema, UUID uuid, String description) { // There's no point adding an UpgradeAudit row if the table isn't there. if (!schema.tableExists("UpgradeAudit")) return; @@ -100,6 +105,20 @@ public static void addAuditRecord(SchemaChangeVisitor visitor, Schema schema, UU if (!schema.tableExists("UpgradeAudit")) return; + if ( ! extendedUpgradeAuditTableRowsPresent ) { + // adding the extended tables expanded the table from 3 columns to 6. If we have more than 5 columns + // we can assume that we have the extended fields + if(schema.getTable("UpgradeAudit").columns().size() > 5) { + extendedUpgradeAuditTableRowsPresent = true; + } else { + // If we don't have the expanded fields we can no-op as UpgradeAudit records are only inserted after we + // have run the upgrade step + return; + } + } + + + InsertStatement auditRecord = createExtendedAuditInsertStatement(uuid, description); visitor.visit(new ExecuteStatement(auditRecord)); @@ -111,17 +130,42 @@ public static void updateRunningAuditRecord(SchemaChangeVisitor visitor, Schema if (!schema.tableExists("UpgradeAudit")) return; - UpdateStatement auditRecord = createRunningAuditUpdateStatement(uuid); + if ( ! extendedUpgradeAuditTableRowsPresent ) { + // adding the extended tables expanded the table from 3 columns to 6. If we have more than 5 columns + // we can assume that we have the extended fields + if(schema.getTable("UpgradeAudit").columns().size() > 5) { + extendedUpgradeAuditTableRowsPresent = true; + } else { + // If we don't have the expanded fields we can no-op as UpgradeAudit records are only inserted after we + // have run the upgrade step + return; + } + } + + UpdateStatement auditRecord = createRunningAuditUpdateStatement(uuid); visitor.visit(new ExecuteStatement(auditRecord)); } - public static void updateFinishedAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid, long processingTimeMs, boolean success) { + public static void updateFinishedAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid, long processingTimeMs, boolean success, String description) { // There's no point adding an UpgradeAudit row if the table isn't there. if (!schema.tableExists("UpgradeAudit")) return; + if ( ! extendedUpgradeAuditTableRowsPresent ) { + // adding the extended tables expanded the table from 3 columns to 6. If we have more than 5 columns + // we can assume that we have the extended fields + if(schema.getTable("UpgradeAudit").columns().size() > 5) { + extendedUpgradeAuditTableRowsPresent = true; + // Just extended so will not have a UpgradeAudit table record. So we need to add one, so it can be upgraded afterwards + addAuditRecord(visitor, schema, uuid, description); + } else { + addAuditRecordNoStatus(visitor, schema, uuid, description); + return; + } + } + UpdateStatement auditRecord = createFinishedAuditUpdateStatement(uuid, processingTimeMs, success); visitor.visit(new ExecuteStatement(auditRecord)); @@ -153,7 +197,7 @@ public static InsertStatement createExtendedAuditInsertStatement(UUID uuid, Stri InsertStatement auditRecord = new InsertStatement().into( tableRef("UpgradeAudit")).values( literal(uuid.toString()).as("upgradeUUID"), - literal(description).as("description"), + literal(description.length() > UPGRADE_STEP_DESCRIPTION_LENGTH ? description.substring(0, UPGRADE_STEP_DESCRIPTION_LENGTH) : description).as("description"), cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as("appliedTime"), literal(UpgradeStepStatus.SCHEDULED.name()).as("status"), new FieldLiteral(getServerName()).as("server") @@ -192,10 +236,13 @@ public static UpdateStatement createFinishedAuditUpdateStatement(UUID uuid, long * @return The insert statement */ public static InsertStatement createAuditInsertStatement(UUID uuid, String description) { + + + InsertStatement auditRecord = new InsertStatement().into( new TableReference("UpgradeAudit")).values( new FieldLiteral(uuid.toString()).as("upgradeUUID"), - new FieldLiteral(description).as("description"), + new FieldLiteral(description.length() > UPGRADE_STEP_DESCRIPTION_LENGTH ? description.substring(0, UPGRADE_STEP_DESCRIPTION_LENGTH) : description).as("description"), cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as("appliedTime") ); return auditRecord; diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java index 2de669c55..d90e07379 100644 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java @@ -48,6 +48,7 @@ class GraphBasedUpgradeSchemaChangeVisitor implements SchemaChangeVisitor { * Write statements to the current node */ private void writeStatements(Collection statements) { + //TODO jonk - log SQL currentNode.addAllUpgradeStatements(statements); } @@ -185,8 +186,8 @@ public void updateRunningAuditRecord(UUID uuid) { } @Override - public void updateFinishedAuditRecord(UUID uuid, long processingTimeMs, boolean success) { - AuditRecordHelper.updateFinishedAuditRecord(this, sourceSchema, uuid, processingTimeMs, success); + public void updateFinishedAuditRecord(UUID uuid, long processingTimeMs, boolean success, String description) { + AuditRecordHelper.updateFinishedAuditRecord(this, sourceSchema, uuid, processingTimeMs, success, description); } /** diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java index c33d8f3e1..53da9195f 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java @@ -254,8 +254,8 @@ public void updateRunningAuditRecord(UUID uuid) { } @Override - public void updateFinishedAuditRecord(UUID uuid, long processingTimeMs, boolean success) { - AuditRecordHelper.updateFinishedAuditRecord(this, currentSchema, uuid, processingTimeMs, success); + public void updateFinishedAuditRecord(UUID uuid, long processingTimeMs, boolean success, String description) { + AuditRecordHelper.updateFinishedAuditRecord(this, currentSchema, uuid, processingTimeMs, success, description); } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java index ec11b12a7..22e13c754 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java @@ -149,21 +149,25 @@ public void applyTo(SchemaChangeVisitor visitor) { try { // Start timer for this UpgradeStep Instant startInstant = Instant.now(); + + + //TODO roll up line below into visitor.startStep // Update Audit record to show upgrade step is running visitor.updateRunningAuditRecord(changesForStep.getUUID()); // Run prerequisites visitor.startStep(changesForStep.getUpgradeClass()); + // Apply each change for (SchemaChange change : changesForStep.getChanges()) { change.accept(visitor); } // Update Audit Record will successful run - visitor.updateFinishedAuditRecord(changesForStep.getUUID(), new Interval(startInstant, Instant.now()).toDurationMillis(), true); + visitor.updateFinishedAuditRecord(changesForStep.getUUID(), new Interval(startInstant, Instant.now()).toDurationMillis(), true, changesForStep.getDescription()); } catch (Exception e) { // Set Audit Record to failed then throw runtime exception - visitor.updateFinishedAuditRecord(changesForStep.getUUID(), 0, false); + visitor.updateFinishedAuditRecord(changesForStep.getUUID(), 0, false, changesForStep.getDescription()); throw new RuntimeException("Failed to apply step: [" + changesForStep.getUpgradeClass() + "]", e); } } @@ -525,7 +529,7 @@ public void updateRunningAuditRecord(java.util.UUID uuid) { } @Override - public void updateFinishedAuditRecord(java.util.UUID uuid, long processingTimeMs, boolean success) { + public void updateFinishedAuditRecord(java.util.UUID uuid, long processingTimeMs, boolean success, String description) { // no-op here. We don't need to record the UUIDs until we actually apply the changes. } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java index a2b01c310..dc6f90a08 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java @@ -155,7 +155,7 @@ public interface SchemaChangeVisitor { public void updateRunningAuditRecord(UUID uuid); //TODO Javadocs - public void updateFinishedAuditRecord(UUID uuid, long processingTimeMs, boolean success); + public void updateFinishedAuditRecord(UUID uuid, long processingTimeMs, boolean success, String description); /** diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/db/DatabaseUpgradeTableContribution.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/db/DatabaseUpgradeTableContribution.java index 202ef186e..6f5d2bbb7 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/db/DatabaseUpgradeTableContribution.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/db/DatabaseUpgradeTableContribution.java @@ -41,6 +41,7 @@ public class DatabaseUpgradeTableContribution implements TableContribution { /** Name of the table containing information on the views deployed within the app's database. */ public static final String DEPLOYED_VIEWS_NAME = "DeployedViews"; + public static final int UPGRADE_STEP_DESCRIPTION_LENGTH = 200; /** * @return The Table descriptor of UpgradeAudit @@ -49,7 +50,7 @@ public static Table upgradeAuditTable() { return table(UPGRADE_AUDIT_NAME) .columns( column("upgradeUUID", DataType.STRING, 100).primaryKey(), - column("description", DataType.STRING, 200).nullable(), + column("description", DataType.STRING, UPGRADE_STEP_DESCRIPTION_LENGTH).nullable(), column("appliedTime", DataType.DECIMAL, 14).nullable(), column("status", DataType.STRING, 10).nullable(), column("server", DataType.STRING, 100).nullable(), diff --git a/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestAuditRecordHelper.java b/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestAuditRecordHelper.java index 274851485..915a1cbc1 100755 --- a/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestAuditRecordHelper.java +++ b/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestAuditRecordHelper.java @@ -17,15 +17,21 @@ import static com.google.common.collect.FluentIterable.from; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import java.text.ParseException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.NoSuchElementException; import java.util.UUID; +import org.alfasoftware.morf.metadata.Column; import org.alfasoftware.morf.metadata.Schema; +import org.alfasoftware.morf.metadata.Table; import org.alfasoftware.morf.sql.InsertStatement; import org.alfasoftware.morf.sql.element.AliasedField; import org.alfasoftware.morf.sql.element.Cast; @@ -49,9 +55,14 @@ public void testAddAuditRecord() throws ParseException { // given SchemaChangeVisitor visitor = mock(SchemaChangeVisitor.class); Schema schema = mock(Schema.class); + Table table = mock(Table.class); + List columns = Arrays.asList(mock(Column.class), mock(Column.class), mock(Column.class), mock(Column.class), mock(Column.class), mock(Column.class)); + UUID uuid = UUID.randomUUID(); String description = "Description"; given(schema.tableExists("UpgradeAudit")).willReturn(true); + given(schema.getTable("UpgradeAudit")).willReturn(table); + given(table.columns()).willReturn(columns); // when AuditRecordHelper.addAuditRecord(visitor, schema, uuid, description); @@ -82,13 +93,38 @@ public void createAuditInsertStatement() throws Exception { } + + /** + * Verifies that the {@link AuditRecordHelper#createAuditInsertStatement(UUID, String)} returns a correct + * {@link InsertStatement}. + */ + @Test + public void createAuditInsertStatementLongDescription() throws Exception { + // given + UUID uuid = UUID.randomUUID(); + String str10 = "0123456789"; + String str40 = str10 + str10 + str10 + str10; + String str200 = str40 + str40 + str40 + str40 + str40; + String str210 = str200 + str10; + + // when an overlength description is passed + InsertStatement statement = AuditRecordHelper.createAuditInsertStatement(uuid, str210); + + // then it is trimmed to 200 characters + assertAuditInsertStatement(uuid, str200, statement); + } + + + + + private void assertAuditInsertStatement(UUID uuid, String description, InsertStatement statement) { assertEquals("Table name", "UpgradeAudit", statement.getTable().getName()); assertEquals("UUID ", uuid.toString(), getValueWithAlias(statement, "upgradeUUID").getValue()); assertEquals("UUID ", description, getValueWithAlias(statement, "description").getValue()); Cast nowCastRepresentation = getCastWithAlias(statement, "appliedTime"); - assertEquals("Wraped in integer date function with now function as argument", FunctionType.DATE_TO_YYYYMMDDHHMMSS.toString() + "(" + FunctionType.NOW + "())", nowCastRepresentation.getExpression().toString()); + assertEquals("Wrapped in integer date function with now function as argument", FunctionType.DATE_TO_YYYYMMDDHHMMSS.toString() + "(" + FunctionType.NOW + "())", nowCastRepresentation.getExpression().toString()); } diff --git a/morf-integration-test/src/test/java/org/alfasoftware/morf/upgrade/TestFullDeployment.java b/morf-integration-test/src/test/java/org/alfasoftware/morf/upgrade/TestFullDeployment.java index 8706a1232..25698ef58 100755 --- a/morf-integration-test/src/test/java/org/alfasoftware/morf/upgrade/TestFullDeployment.java +++ b/morf-integration-test/src/test/java/org/alfasoftware/morf/upgrade/TestFullDeployment.java @@ -24,11 +24,14 @@ import java.sql.Connection; import java.sql.ResultSet; +import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; +import java.util.List; import javax.sql.DataSource; +import com.google.common.collect.ImmutableList; import org.alfasoftware.morf.guicesupport.InjectMembersRule; import org.alfasoftware.morf.guicesupport.MorfModule; import org.alfasoftware.morf.jdbc.ConnectionResources; @@ -38,6 +41,7 @@ import org.alfasoftware.morf.testing.DatabaseSchemaManager; import org.alfasoftware.morf.testing.TestingDataSourceModule; import org.alfasoftware.morf.upgrade.Deployment.DeploymentFactory; +import org.alfasoftware.morf.upgrade.upgrade.AddExtraLoggingToUpgradeAuditTable; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -126,4 +130,78 @@ protected void configure() { connection.close(); } } + + + + /** + * Tests full deployment with two simple domain classes + * @throws SQLException If database access fails. + */ + @Test + public void testTwoClassDeploymentWithUpgradeSteps() throws SQLException { + Schema targetSchema = schema( + table("UpgradeAudit").columns( + column("upgradeUUID", DataType.STRING, 100).primaryKey(), + column("description", DataType.STRING, 200).nullable(), + column("appliedTime", DataType.DECIMAL, 14).nullable() + ), + table("FirstTestBean").columns( + column("identifier", DataType.DECIMAL, 10).nullable(), + column("stringColumn", DataType.STRING, 10).nullable(), + column("doubleColumn", DataType.DECIMAL, 13, 2) + ), + table("SecondTestBean").columns( + column("identifier", DataType.DECIMAL, 10).nullable(), + column("intColumn", DataType.DECIMAL, 10).nullable() + ) + ); + + final List> upgradeStepList = ImmutableList.of( + AddExtraLoggingToUpgradeAuditTable.class + ); + + // Try accessing the new database + Connection connection = dataSource.getConnection(); + try { + // -- Set up the database... + // + DeploymentFactory deploymentFactory = Guice.createInjector(new MorfModule(), new AbstractModule() { + @Override + protected void configure() { + bind(SqlDialect.class).toInstance(connectionResources.sqlDialect()); + bind(DataSource.class).toInstance(connectionResources.getDataSource()); // TODO Need to discuss more widely about what we want to do here + bind(ConnectionResources.class).toInstance(connectionResources); + } + }).getInstance(DeploymentFactory.class); + + deploymentFactory.create(connectionResources).deploy(targetSchema); + + + Upgrade.performUpgrade(targetSchema, upgradeStepList, connectionResources, new ViewDeploymentValidator.AlwaysValidate()); + + String schemaNamePrefix = connectionResources.sqlDialect().schemaNamePrefix(); + + // A simple query + Statement statement = connection.createStatement(); + assertFalse("Empty select results", statement.executeQuery("select * from "+schemaNamePrefix+"FirstTestBean").next()); + + // An insert followed by a read + statement.execute("insert into "+schemaNamePrefix+"SecondTestBean values(0, 33)"); + ResultSet resultSet = statement.executeQuery("select * from "+schemaNamePrefix+"SecondTestBean"); + assertTrue("Second result set has a record", resultSet.next()); + assertEquals("Column value", 33, resultSet.getInt("intColumn")); + assertFalse("Second result set has exactly one record", resultSet.next()); + + ResultSet resultSetUpgradeAudit = statement.executeQuery("select * from "+schemaNamePrefix+"UpgradeAudit"); + ResultSetMetaData resultSetMetaData = resultSetUpgradeAudit.getMetaData(); + + assertEquals("Column Count", 6, resultSetMetaData.getColumnCount()); + } finally { + upgradeStatusTableService.tidyUp(connectionResources.getDataSource()); + connection.close(); + } + } + + + } From 4009ee7f12e595cf4ca0a314bfca637a57755e5e Mon Sep 17 00:00:00 2001 From: Jon Knight Date: Tue, 4 Jul 2023 09:06:31 +0100 Subject: [PATCH 03/10] Add additional Upgrade Audit columns to TestFulLDeployment's UpgradeAutdit implementation --- .../org/alfasoftware/morf/upgrade/TestFullDeployment.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/morf-integration-test/src/test/java/org/alfasoftware/morf/upgrade/TestFullDeployment.java b/morf-integration-test/src/test/java/org/alfasoftware/morf/upgrade/TestFullDeployment.java index 25698ef58..06fa18ffb 100755 --- a/morf-integration-test/src/test/java/org/alfasoftware/morf/upgrade/TestFullDeployment.java +++ b/morf-integration-test/src/test/java/org/alfasoftware/morf/upgrade/TestFullDeployment.java @@ -41,6 +41,7 @@ import org.alfasoftware.morf.testing.DatabaseSchemaManager; import org.alfasoftware.morf.testing.TestingDataSourceModule; import org.alfasoftware.morf.upgrade.Deployment.DeploymentFactory; +import org.alfasoftware.morf.upgrade.db.DatabaseUpgradeTableContribution; import org.alfasoftware.morf.upgrade.upgrade.AddExtraLoggingToUpgradeAuditTable; import org.junit.After; import org.junit.Before; @@ -142,8 +143,11 @@ public void testTwoClassDeploymentWithUpgradeSteps() throws SQLException { Schema targetSchema = schema( table("UpgradeAudit").columns( column("upgradeUUID", DataType.STRING, 100).primaryKey(), - column("description", DataType.STRING, 200).nullable(), - column("appliedTime", DataType.DECIMAL, 14).nullable() + column("description", DataType.STRING, DatabaseUpgradeTableContribution.UPGRADE_STEP_DESCRIPTION_LENGTH).nullable(), + column("appliedTime", DataType.DECIMAL, 14).nullable(), + column("status", DataType.STRING, 10).nullable(), + column("server", DataType.STRING, 100).nullable(), + column("processingTimeMs", DataType.DECIMAL, 14).nullable() ), table("FirstTestBean").columns( column("identifier", DataType.DECIMAL, 10).nullable(), From cba7d4254dc8a24946d6f56d872d926565ba3c1b Mon Sep 17 00:00:00 2001 From: Jon Knight Date: Thu, 6 Jul 2023 14:57:22 +0100 Subject: [PATCH 04/10] Add unixtime function --- .../alfasoftware/morf/jdbc/SqlDialect.java | 20 +- .../morf/sql/element/Function.java | 13 ++ .../morf/sql/element/FunctionType.java | 8 +- .../upgrade/HumanReadableStatementHelper.java | 1 + .../morf/sql/element/TestFunctionDetail.java | 14 ++ .../morf/jdbc/h2/TestH2Dialect.java | 8 + .../morf/integration/TestSqlStatements.java | 32 +++ .../morf/jdbc/mysql/MySqlDialect.java | 11 +- .../morf/jdbc/mysql/TestMySqlDialect.java | 10 + .../morf/jdbc/oracle/OracleDialect.java | 7 + .../morf/jdbc/oracle/TestOracleDialect.java | 8 + .../postgresql/TestPostgreSQLDialect.java | 8 + .../morf/jdbc/sqlserver/SqlServerDialect.java | 8 + .../jdbc/sqlserver/TestSqlServerDialect.java | 213 +++++++++--------- .../morf/jdbc/AbstractSqlDialectTest.java | 17 ++ 15 files changed, 273 insertions(+), 105 deletions(-) diff --git a/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java b/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java index 4709f33ae..7c035bd1b 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java @@ -2078,6 +2078,11 @@ protected String getSqlFrom(Function function) { } return getSqlForRowNumber(); + case UNIX_TIME: + if (!function.getArguments().isEmpty()) { + throw new IllegalArgumentException("The UNIXTIME function should have zero arguments. This function has " + function.getArguments().size()); + } + return getSqlForUnixTime(); default: throw new UnsupportedOperationException("This database does not currently support the [" + function.getType() + "] function"); } @@ -2417,13 +2422,26 @@ protected String getLeastFunctionName() { /** * Produce SQL for getting the row number of the row in the partition * - * @return a string representation of the SQL for finding the last day of the month. + * @return a string representation of the SQL for getting the row number of the row in the partition. */ protected String getSqlForRowNumber(){ return "ROW_NUMBER()"; } + + /** + * Produce SQL for getting the unix time + * + * @return a long representation of the unix time + */ + protected String getSqlForUnixTime(){ + // Postgres syntax, but works in H2 + return "trunc(extract(epoch from now() at time zone 'UTC')*1000)"; + } + + + /** * Gets the function name required to perform a substring command. *

diff --git a/morf-core/src/main/java/org/alfasoftware/morf/sql/element/Function.java b/morf-core/src/main/java/org/alfasoftware/morf/sql/element/Function.java index 3f6073584..35319e6db 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/sql/element/Function.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/sql/element/Function.java @@ -645,6 +645,19 @@ public FunctionType getType() { } + + /** + * Helper method to create an instance of the "unixtime" SQL function. + * + * @return an instance of a unixtime function + */ + public static Function unixtime() { + return new Function(FunctionType.UNIX_TIME); + } + + + + /** * @see org.alfasoftware.morf.sql.element.AliasedField#deepCopyInternal(DeepCopyTransformation) */ diff --git a/morf-core/src/main/java/org/alfasoftware/morf/sql/element/FunctionType.java b/morf-core/src/main/java/org/alfasoftware/morf/sql/element/FunctionType.java index 95884d002..7c97c1938 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/sql/element/FunctionType.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/sql/element/FunctionType.java @@ -226,5 +226,11 @@ public enum FunctionType { /** * Calculates the row number on a partition. Generally used as a Window function */ - ROW_NUMBER + ROW_NUMBER, + + /** + * Unix time, milliseconds since 1st January 1970 UTC + */ + + UNIX_TIME } \ No newline at end of file diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/HumanReadableStatementHelper.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/HumanReadableStatementHelper.java index 0d6e8dd6d..1a62c9162 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/HumanReadableStatementHelper.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/HumanReadableStatementHelper.java @@ -140,6 +140,7 @@ public FunctionTypeMetaData(final String prefix, final String suffix, final Stri .put(FunctionType.TRIM, new FunctionTypeMetaData("trimmed ", "", "", false, false)) .put(FunctionType.UPPER, new FunctionTypeMetaData("upper case ", "", "", false, false)) .put(FunctionType.LAST_DAY_OF_MONTH, new FunctionTypeMetaData("last day of month ", "", "", false, false)) + .put(FunctionType.UNIX_TIME, new FunctionTypeMetaData("unix time in milliseconds ", "", "", false, false)) .build(); /** diff --git a/morf-core/src/test/java/org/alfasoftware/morf/sql/element/TestFunctionDetail.java b/morf-core/src/test/java/org/alfasoftware/morf/sql/element/TestFunctionDetail.java index b3571b697..2ddfbd96e 100644 --- a/morf-core/src/test/java/org/alfasoftware/morf/sql/element/TestFunctionDetail.java +++ b/morf-core/src/test/java/org/alfasoftware/morf/sql/element/TestFunctionDetail.java @@ -285,4 +285,18 @@ public void testUpperCase() { assertTrue("First argument should be a field reference", firstArgument instanceof FieldReference); assertEquals("First argument should have correct name", "agreementNumber", ((FieldReference) firstArgument).getName()); } + + + /** + * Tests indirect usage of the unixtime function + */ + @Test + public void testUnixTime() { + Function function = Function.unixtime(); + + assertEquals("Function should be of type UNIX_TIME", FunctionType.UNIX_TIME, function.getType()); + assertNotNull("Function should have empty arguments", function.getArguments()); + assertEquals("Function should have no arguments", 0, function.getArguments().size()); + } + } \ No newline at end of file diff --git a/morf-h2/src/test/java/org/alfasoftware/morf/jdbc/h2/TestH2Dialect.java b/morf-h2/src/test/java/org/alfasoftware/morf/jdbc/h2/TestH2Dialect.java index c2399d745..80dfc02cb 100755 --- a/morf-h2/src/test/java/org/alfasoftware/morf/jdbc/h2/TestH2Dialect.java +++ b/morf-h2/src/test/java/org/alfasoftware/morf/jdbc/h2/TestH2Dialect.java @@ -1221,6 +1221,14 @@ protected String expectedRowNumber() { } + /** + * @return The expected SQL for the unix time stamp in milliseconds + */ + @Override + protected String expectedUnixTime() { + return "trunc(extract(epoch from now() at time zone 'UTC')*1000)"; + } + /** * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#tableName(java.lang.String) */ diff --git a/morf-integration-test/src/test/java/org/alfasoftware/morf/integration/TestSqlStatements.java b/morf-integration-test/src/test/java/org/alfasoftware/morf/integration/TestSqlStatements.java index 764db8292..d1807692b 100755 --- a/morf-integration-test/src/test/java/org/alfasoftware/morf/integration/TestSqlStatements.java +++ b/morf-integration-test/src/test/java/org/alfasoftware/morf/integration/TestSqlStatements.java @@ -81,6 +81,7 @@ import static org.alfasoftware.morf.sql.element.Function.sum; import static org.alfasoftware.morf.sql.element.Function.sumDistinct; import static org.alfasoftware.morf.sql.element.Function.trim; +import static org.alfasoftware.morf.sql.element.Function.unixtime; import static org.alfasoftware.morf.sql.element.Function.upperCase; import static org.alfasoftware.morf.sql.element.Function.yyyymmddToDate; import static org.hamcrest.Matchers.allOf; @@ -2785,6 +2786,37 @@ public Void process(ResultSet resultSet) throws SQLException { } + + /** + * Tests execute now function. + * + * @throws SQLException if something goes wrong. + */ + @Test + public void testUnixTime() throws SQLException { + SelectStatement select = select(unixtime()); + + SqlScriptExecutor executor = sqlScriptExecutorProvider.get(new LoggingSqlScriptVisitor()); + + executor.executeQuery(convertStatementToSQL(select), connection, new ResultSetProcessor() { + @Override + public Void process(ResultSet resultSet) throws SQLException { + resultSet.next(); + + final long databaseTime = resultSet.getLong(1); + + log.info("Current database time: " + databaseTime); + + assertTrue("Database unix time is set and has a value after July 6th 2023 ", databaseTime > 1688648895428L); + + return null; + } + }); + } + + + + @Test public void testExecuteSqlStatementWithParams() { InsertStatement insert = insert().into(tableRef("ParamStatementsTest")).values( diff --git a/morf-mysql/src/main/java/org/alfasoftware/morf/jdbc/mysql/MySqlDialect.java b/morf-mysql/src/main/java/org/alfasoftware/morf/jdbc/mysql/MySqlDialect.java index 1bc379de5..7c7278c67 100755 --- a/morf-mysql/src/main/java/org/alfasoftware/morf/jdbc/mysql/MySqlDialect.java +++ b/morf-mysql/src/main/java/org/alfasoftware/morf/jdbc/mysql/MySqlDialect.java @@ -376,7 +376,7 @@ private String checkMaxIdAutonumberStatement(Table table,Column autoIncrementCol * MySQL defaults to fetching * all records into memory when a JDBC query is executed, which causes OOM * errors when used with large data sets (Cryo and ETLs being prime offenders). Ideally - * we would use a nice big paging size here (like 200 as used in {@link OracleDialect}) + * we would use a nice big paging size here (like 200 as used in OracleDialect) * but as noted in the link above, MySQL only supports one record at a time or all at * once, with nothing in between. As a result, we default to one record for bulk loads * as the only safe choice. @@ -660,6 +660,15 @@ protected String getSqlForNow(Function function) { /** + * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForUnixTime() + */ + @Override + protected String getSqlForUnixTime() { + return "CAST( 1000*UNIX_TIMESTAMP(current_timestamp(3)) AS UNSIGNED INTEGER)"; + } + + + /** * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForDaysBetween(org.alfasoftware.morf.sql.element.AliasedField, org.alfasoftware.morf.sql.element.AliasedField) */ @Override diff --git a/morf-mysql/src/test/java/org/alfasoftware/morf/jdbc/mysql/TestMySqlDialect.java b/morf-mysql/src/test/java/org/alfasoftware/morf/jdbc/mysql/TestMySqlDialect.java index f993bf6cb..a0e52296e 100755 --- a/morf-mysql/src/test/java/org/alfasoftware/morf/jdbc/mysql/TestMySqlDialect.java +++ b/morf-mysql/src/test/java/org/alfasoftware/morf/jdbc/mysql/TestMySqlDialect.java @@ -924,6 +924,16 @@ protected String expectedNow() { } + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedUnixTime() + */ + @Override + protected String expectedUnixTime() { + return "CAST( 1000*UNIX_TIMESTAMP(current_timestamp(3)) AS UNSIGNED INTEGER)"; + } + + /** * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDropViewStatements() */ diff --git a/morf-oracle/src/main/java/org/alfasoftware/morf/jdbc/oracle/OracleDialect.java b/morf-oracle/src/main/java/org/alfasoftware/morf/jdbc/oracle/OracleDialect.java index c599e2162..9f369e917 100755 --- a/morf-oracle/src/main/java/org/alfasoftware/morf/jdbc/oracle/OracleDialect.java +++ b/morf-oracle/src/main/java/org/alfasoftware/morf/jdbc/oracle/OracleDialect.java @@ -1418,6 +1418,13 @@ protected String getSqlForLastDayOfMonth(AliasedField date) { return "LAST_DAY(" + getSqlFrom(date) + ")"; } + /** + * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForUnixTime() + */ + @Override + protected String getSqlForUnixTime() { + return "EXTRACT(DAY FROM(sys_extract_utc(systimestamp) - to_timestamp('1970-01-01', 'YYYY-MM-DD'))) * 86400000+ to_number(TO_CHAR(sys_extract_utc(systimestamp), 'SSSSSFF3'))"; + } /** * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForAnalyseTable(Table) diff --git a/morf-oracle/src/test/java/org/alfasoftware/morf/jdbc/oracle/TestOracleDialect.java b/morf-oracle/src/test/java/org/alfasoftware/morf/jdbc/oracle/TestOracleDialect.java index 8916de9cb..9e3d623d6 100755 --- a/morf-oracle/src/test/java/org/alfasoftware/morf/jdbc/oracle/TestOracleDialect.java +++ b/morf-oracle/src/test/java/org/alfasoftware/morf/jdbc/oracle/TestOracleDialect.java @@ -1175,6 +1175,14 @@ protected String expectedNow() { } + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedUnixTime() + */ + @Override + protected String expectedUnixTime() { + return "EXTRACT(DAY FROM(sys_extract_utc(systimestamp) - to_timestamp('1970-01-01', 'YYYY-MM-DD'))) * 86400000+ to_number(TO_CHAR(sys_extract_utc(systimestamp), 'SSSSSFF3'))"; + } + /** * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDaysBetween() */ diff --git a/morf-postgresql/src/test/java/org/alfasoftware/morf/jdbc/postgresql/TestPostgreSQLDialect.java b/morf-postgresql/src/test/java/org/alfasoftware/morf/jdbc/postgresql/TestPostgreSQLDialect.java index df67ce941..cd0510770 100644 --- a/morf-postgresql/src/test/java/org/alfasoftware/morf/jdbc/postgresql/TestPostgreSQLDialect.java +++ b/morf-postgresql/src/test/java/org/alfasoftware/morf/jdbc/postgresql/TestPostgreSQLDialect.java @@ -985,6 +985,14 @@ protected String expectedNow() { return "NOW()"; } + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedUnixTime() + */ + @Override + protected String expectedUnixTime() { + return "trunc(extract(epoch from now() at time zone 'UTC')*1000)"; + } + /** * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDropViewStatements() diff --git a/morf-sqlserver/src/main/java/org/alfasoftware/morf/jdbc/sqlserver/SqlServerDialect.java b/morf-sqlserver/src/main/java/org/alfasoftware/morf/jdbc/sqlserver/SqlServerDialect.java index e3c448e3d..c77fe48ae 100755 --- a/morf-sqlserver/src/main/java/org/alfasoftware/morf/jdbc/sqlserver/SqlServerDialect.java +++ b/morf-sqlserver/src/main/java/org/alfasoftware/morf/jdbc/sqlserver/SqlServerDialect.java @@ -811,6 +811,14 @@ protected String getSqlForNow(Function function) { return "GETUTCDATE()"; } + /** + * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForUnixTime() + */ + @Override + protected String getSqlForUnixTime() { + return "DATEDIFF_BIG(MILLISECOND,'1970-01-01 00:00:00.000', SYSUTCDATETIME())"; + } + /** * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForDaysBetween(org.alfasoftware.morf.sql.element.AliasedField, org.alfasoftware.morf.sql.element.AliasedField) diff --git a/morf-sqlserver/src/test/java/org/alfasoftware/morf/jdbc/sqlserver/TestSqlServerDialect.java b/morf-sqlserver/src/test/java/org/alfasoftware/morf/jdbc/sqlserver/TestSqlServerDialect.java index ad4bc1268..6691d6da2 100755 --- a/morf-sqlserver/src/test/java/org/alfasoftware/morf/jdbc/sqlserver/TestSqlServerDialect.java +++ b/morf-sqlserver/src/test/java/org/alfasoftware/morf/jdbc/sqlserver/TestSqlServerDialect.java @@ -41,8 +41,8 @@ */ public class TestSqlServerDialect extends AbstractSqlDialectTest { - @SuppressWarnings({"unchecked","rawtypes"}) - private final ArgumentCaptor> listCaptor = ArgumentCaptor.forClass((Class>)(Class)List.class); + @SuppressWarnings({"unchecked", "rawtypes"}) + private final ArgumentCaptor> listCaptor = ArgumentCaptor.forClass((Class>) (Class) List.class); /** * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#createTestDialect() @@ -62,15 +62,15 @@ protected SqlDialect createTestDialect() { @Override protected List expectedCreateTableStatements() { return Arrays - .asList( - "CREATE TABLE TESTSCHEMA.Test ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT Test_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS, [intField] INTEGER, [floatField] NUMERIC(13,2) NOT NULL, [dateField] DATE, [booleanField] BIT, [charField] NVARCHAR(1) COLLATE SQL_Latin1_General_CP1_CS_AS, [blobField] IMAGE, [bigIntegerField] BIGINT CONSTRAINT Test_bigIntegerField_DF DEFAULT 12345, [clobField] NVARCHAR(MAX) COLLATE SQL_Latin1_General_CP1_CS_AS, CONSTRAINT [Test_PK] PRIMARY KEY ([id]))", - "CREATE UNIQUE NONCLUSTERED INDEX Test_NK ON TESTSCHEMA.Test ([stringField])", - "CREATE UNIQUE NONCLUSTERED INDEX Test_1 ON TESTSCHEMA.Test ([intField], [floatField])", - "CREATE TABLE TESTSCHEMA.Alternate ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT Alternate_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS, CONSTRAINT [Alternate_PK] PRIMARY KEY ([id]))", - "CREATE INDEX Alternate_1 ON TESTSCHEMA.Alternate ([stringField])", - "CREATE TABLE TESTSCHEMA.NonNull ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT NonNull_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL, [intField] NUMERIC(8,0) NOT NULL, [booleanField] BIT NOT NULL, [dateField] DATE NOT NULL, [blobField] IMAGE NOT NULL, CONSTRAINT [NonNull_PK] PRIMARY KEY ([id]))", - "CREATE TABLE TESTSCHEMA.CompositePrimaryKey ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT CompositePrimaryKey_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL, [secondPrimaryKey] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL, CONSTRAINT [CompositePrimaryKey_PK] PRIMARY KEY ([id], [secondPrimaryKey]))", - "CREATE TABLE TESTSCHEMA.AutoNumber ([intField] BIGINT NOT NULL IDENTITY(5, 1), CONSTRAINT [AutoNumber_PK] PRIMARY KEY ([intField]))" + .asList( + "CREATE TABLE TESTSCHEMA.Test ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT Test_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS, [intField] INTEGER, [floatField] NUMERIC(13,2) NOT NULL, [dateField] DATE, [booleanField] BIT, [charField] NVARCHAR(1) COLLATE SQL_Latin1_General_CP1_CS_AS, [blobField] IMAGE, [bigIntegerField] BIGINT CONSTRAINT Test_bigIntegerField_DF DEFAULT 12345, [clobField] NVARCHAR(MAX) COLLATE SQL_Latin1_General_CP1_CS_AS, CONSTRAINT [Test_PK] PRIMARY KEY ([id]))", + "CREATE UNIQUE NONCLUSTERED INDEX Test_NK ON TESTSCHEMA.Test ([stringField])", + "CREATE UNIQUE NONCLUSTERED INDEX Test_1 ON TESTSCHEMA.Test ([intField], [floatField])", + "CREATE TABLE TESTSCHEMA.Alternate ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT Alternate_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS, CONSTRAINT [Alternate_PK] PRIMARY KEY ([id]))", + "CREATE INDEX Alternate_1 ON TESTSCHEMA.Alternate ([stringField])", + "CREATE TABLE TESTSCHEMA.NonNull ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT NonNull_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL, [intField] NUMERIC(8,0) NOT NULL, [booleanField] BIT NOT NULL, [dateField] DATE NOT NULL, [blobField] IMAGE NOT NULL, CONSTRAINT [NonNull_PK] PRIMARY KEY ([id]))", + "CREATE TABLE TESTSCHEMA.CompositePrimaryKey ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT CompositePrimaryKey_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL, [secondPrimaryKey] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL, CONSTRAINT [CompositePrimaryKey_PK] PRIMARY KEY ([id], [secondPrimaryKey]))", + "CREATE TABLE TESTSCHEMA.AutoNumber ([intField] BIGINT NOT NULL IDENTITY(5, 1), CONSTRAINT [AutoNumber_PK] PRIMARY KEY ([intField]))" ); } @@ -84,13 +84,13 @@ protected List expectedCreateTableStatements() { @Override protected List expectedCreateTemporaryTableStatements() { return Arrays - .asList( - "CREATE TABLE TESTSCHEMA.#TempTest ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT #TempTest_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS, [intField] INTEGER, [floatField] NUMERIC(13,2) NOT NULL, [dateField] DATE, [booleanField] BIT, [charField] NVARCHAR(1) COLLATE SQL_Latin1_General_CP1_CS_AS, [blobField] IMAGE, [bigIntegerField] BIGINT CONSTRAINT #TempTest_bigIntegerField_DF DEFAULT 12345, [clobField] NVARCHAR(MAX) COLLATE SQL_Latin1_General_CP1_CS_AS, CONSTRAINT [TempTest_PK] PRIMARY KEY ([id]))", - "CREATE UNIQUE NONCLUSTERED INDEX TempTest_NK ON TESTSCHEMA.#TempTest ([stringField])", - "CREATE INDEX TempTest_1 ON TESTSCHEMA.#TempTest ([intField], [floatField])", - "CREATE TABLE TESTSCHEMA.#TempAlternate ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT #TempAlternate_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS, CONSTRAINT [TempAlternate_PK] PRIMARY KEY ([id]))", - "CREATE INDEX TempAlternate_1 ON TESTSCHEMA.#TempAlternate ([stringField])", - "CREATE TABLE TESTSCHEMA.#TempNonNull ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT #TempNonNull_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL, [intField] NUMERIC(8,0) NOT NULL, [booleanField] BIT NOT NULL, [dateField] DATE NOT NULL, [blobField] IMAGE NOT NULL, CONSTRAINT [TempNonNull_PK] PRIMARY KEY ([id]))"); + .asList( + "CREATE TABLE TESTSCHEMA.#TempTest ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT #TempTest_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS, [intField] INTEGER, [floatField] NUMERIC(13,2) NOT NULL, [dateField] DATE, [booleanField] BIT, [charField] NVARCHAR(1) COLLATE SQL_Latin1_General_CP1_CS_AS, [blobField] IMAGE, [bigIntegerField] BIGINT CONSTRAINT #TempTest_bigIntegerField_DF DEFAULT 12345, [clobField] NVARCHAR(MAX) COLLATE SQL_Latin1_General_CP1_CS_AS, CONSTRAINT [TempTest_PK] PRIMARY KEY ([id]))", + "CREATE UNIQUE NONCLUSTERED INDEX TempTest_NK ON TESTSCHEMA.#TempTest ([stringField])", + "CREATE INDEX TempTest_1 ON TESTSCHEMA.#TempTest ([intField], [floatField])", + "CREATE TABLE TESTSCHEMA.#TempAlternate ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT #TempAlternate_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS, CONSTRAINT [TempAlternate_PK] PRIMARY KEY ([id]))", + "CREATE INDEX TempAlternate_1 ON TESTSCHEMA.#TempAlternate ([stringField])", + "CREATE TABLE TESTSCHEMA.#TempNonNull ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT #TempNonNull_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL, [intField] NUMERIC(8,0) NOT NULL, [booleanField] BIT NOT NULL, [dateField] DATE NOT NULL, [blobField] IMAGE NOT NULL, CONSTRAINT [TempNonNull_PK] PRIMARY KEY ([id]))"); } @@ -100,9 +100,9 @@ protected List expectedCreateTemporaryTableStatements() { @Override protected List expectedCreateTableStatementsWithLongTableName() { return Arrays.asList( - "CREATE TABLE TESTSCHEMA.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS, [intField] NUMERIC(8,0), [floatField] NUMERIC(13,2) NOT NULL, [dateField] DATE, [booleanField] BIT, [charField] NVARCHAR(1) COLLATE SQL_Latin1_General_CP1_CS_AS, CONSTRAINT [tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation_PK] PRIMARY KEY ([id]))", - "CREATE UNIQUE NONCLUSTERED INDEX Test_NK ON TESTSCHEMA.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation ([stringField])", - "CREATE INDEX Test_1 ON TESTSCHEMA.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation ([intField], [floatField])" + "CREATE TABLE TESTSCHEMA.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation ([id] BIGINT NOT NULL, [version] INTEGER CONSTRAINT tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation_version_DF DEFAULT 0, [stringField] NVARCHAR(3) COLLATE SQL_Latin1_General_CP1_CS_AS, [intField] NUMERIC(8,0), [floatField] NUMERIC(13,2) NOT NULL, [dateField] DATE, [booleanField] BIT, [charField] NVARCHAR(1) COLLATE SQL_Latin1_General_CP1_CS_AS, CONSTRAINT [tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation_PK] PRIMARY KEY ([id]))", + "CREATE UNIQUE NONCLUSTERED INDEX Test_NK ON TESTSCHEMA.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation ([stringField])", + "CREATE INDEX Test_1 ON TESTSCHEMA.tableWithANameThatExceedsTwentySevenCharactersToMakeSureSchemaNameDoesNotGetFactoredIntoOracleNameTruncation ([intField], [floatField])" ); } @@ -203,10 +203,10 @@ protected String expectedParameterisedInsertStatementWithTableInDifferentSchema( @Override protected List expectedAutoGenerateIdStatement() { return Arrays.asList( - "DELETE FROM TESTSCHEMA.idvalues where name = 'Test'", - "INSERT INTO TESTSCHEMA.idvalues (name, "+ID_INCREMENTOR_TABLE_COLUMN_VALUE+") VALUES('Test', (SELECT COALESCE(MAX(id) + 1, 1) AS CurrentValue FROM TESTSCHEMA.Test))", - "INSERT INTO TESTSCHEMA.Test (version, stringField, id) SELECT version, stringField, (SELECT COALESCE("+ID_INCREMENTOR_TABLE_COLUMN_VALUE+", 0) FROM TESTSCHEMA.idvalues WHERE (name = 'Test')) + Other.id FROM TESTSCHEMA.Other" - ); + "DELETE FROM TESTSCHEMA.idvalues where name = 'Test'", + "INSERT INTO TESTSCHEMA.idvalues (name, " + ID_INCREMENTOR_TABLE_COLUMN_VALUE + ") VALUES('Test', (SELECT COALESCE(MAX(id) + 1, 1) AS CurrentValue FROM TESTSCHEMA.Test))", + "INSERT INTO TESTSCHEMA.Test (version, stringField, id) SELECT version, stringField, (SELECT COALESCE(" + ID_INCREMENTOR_TABLE_COLUMN_VALUE + ", 0) FROM TESTSCHEMA.idvalues WHERE (name = 'Test')) + Other.id FROM TESTSCHEMA.Other" + ); } @@ -216,46 +216,43 @@ protected List expectedAutoGenerateIdStatement() { @Override protected List expectedInsertWithIdAndVersion() { return Arrays.asList( - "DELETE FROM TESTSCHEMA.idvalues where name = 'Test'", - "INSERT INTO TESTSCHEMA.idvalues (name, "+ID_INCREMENTOR_TABLE_COLUMN_VALUE+") VALUES('Test', (SELECT COALESCE(MAX(id) + 1, 1) AS CurrentValue FROM TESTSCHEMA.Test))", - "INSERT INTO TESTSCHEMA.Test (stringField, id, version) SELECT stringField, (SELECT COALESCE("+ID_INCREMENTOR_TABLE_COLUMN_VALUE+", 0) FROM TESTSCHEMA.idvalues WHERE (name = 'Test')) + Other.id, 0 AS version FROM TESTSCHEMA.Other" - ); + "DELETE FROM TESTSCHEMA.idvalues where name = 'Test'", + "INSERT INTO TESTSCHEMA.idvalues (name, " + ID_INCREMENTOR_TABLE_COLUMN_VALUE + ") VALUES('Test', (SELECT COALESCE(MAX(id) + 1, 1) AS CurrentValue FROM TESTSCHEMA.Test))", + "INSERT INTO TESTSCHEMA.Test (stringField, id, version) SELECT stringField, (SELECT COALESCE(" + ID_INCREMENTOR_TABLE_COLUMN_VALUE + ", 0) FROM TESTSCHEMA.idvalues WHERE (name = 'Test')) + Other.id, 0 AS version FROM TESTSCHEMA.Other" + ); } private List expectedPreInsertStatements() { return ImmutableList.of( - "SET IDENTITY_INSERT TESTSCHEMA.AutoNumber ON" - ); + "SET IDENTITY_INSERT TESTSCHEMA.AutoNumber ON" + ); } - - private void verifyPostInsertStatements(List executedStatements) { - assertThat(executedStatements,contains( - "SET IDENTITY_INSERT TESTSCHEMA.AutoNumber OFF", - - "IF EXISTS (SELECT 1 FROM TESTSCHEMA.AutoNumber)\n" + - "BEGIN\n" + - " DBCC CHECKIDENT (\"TESTSCHEMA.AutoNumber\", RESEED, 4)\n" + - " DBCC CHECKIDENT (\"TESTSCHEMA.AutoNumber\", RESEED)\n" + - "END\n" + - "ELSE\n" + - "BEGIN\n" + - " DBCC CHECKIDENT (\"TESTSCHEMA.AutoNumber\", RESEED, 5)\n" + - "END" + assertThat(executedStatements, contains( + "SET IDENTITY_INSERT TESTSCHEMA.AutoNumber OFF", + + "IF EXISTS (SELECT 1 FROM TESTSCHEMA.AutoNumber)\n" + + "BEGIN\n" + + " DBCC CHECKIDENT (\"TESTSCHEMA.AutoNumber\", RESEED, 4)\n" + + " DBCC CHECKIDENT (\"TESTSCHEMA.AutoNumber\", RESEED)\n" + + "END\n" + + "ELSE\n" + + "BEGIN\n" + + " DBCC CHECKIDENT (\"TESTSCHEMA.AutoNumber\", RESEED, 5)\n" + + "END" )); } - /** * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#verifyPostInsertStatementsInsertingUnderAutonumLimit(org.alfasoftware.morf.jdbc.SqlScriptExecutor, java.sql.Connection) */ @Override protected void verifyPostInsertStatementsInsertingUnderAutonumLimit(SqlScriptExecutor sqlScriptExecutor, Connection connection) { - verify(sqlScriptExecutor).execute(listCaptor.capture(),eq(connection)); + verify(sqlScriptExecutor).execute(listCaptor.capture(), eq(connection)); verifyPostInsertStatements(listCaptor.getValue()); verifyNoMoreInteractions(sqlScriptExecutor); } @@ -266,7 +263,7 @@ protected void verifyPostInsertStatementsInsertingUnderAutonumLimit(SqlScriptExe */ @Override protected void verifyPostInsertStatementsNotInsertingUnderAutonumLimit(SqlScriptExecutor sqlScriptExecutor, Connection connection) { - verify(sqlScriptExecutor).execute(listCaptor.capture(),eq(connection)); + verify(sqlScriptExecutor).execute(listCaptor.capture(), eq(connection)); verifyPostInsertStatements(listCaptor.getValue()); verifyNoMoreInteractions(sqlScriptExecutor); } @@ -305,10 +302,10 @@ protected String tableName(String baseName) { @Override protected List expectedSpecifiedValueInsert() { return Arrays.asList( - "DELETE FROM TESTSCHEMA.idvalues where name = 'Test'", - "INSERT INTO TESTSCHEMA.idvalues (name, "+ID_INCREMENTOR_TABLE_COLUMN_VALUE+") VALUES('Test', (SELECT COALESCE(MAX(id) + 1, 1) AS CurrentValue FROM TESTSCHEMA.Test))", - "INSERT INTO TESTSCHEMA.Test (stringField, intField, floatField, dateField, booleanField, charField, id, version, blobField, bigIntegerField, clobField) VALUES ('Escap''d', 7, 11.25, 20100405, 1, 'X', (SELECT COALESCE("+ID_INCREMENTOR_TABLE_COLUMN_VALUE+", 1) FROM TESTSCHEMA.idvalues WHERE (name = 'Test')), 0, null, 12345, null)" - ); + "DELETE FROM TESTSCHEMA.idvalues where name = 'Test'", + "INSERT INTO TESTSCHEMA.idvalues (name, " + ID_INCREMENTOR_TABLE_COLUMN_VALUE + ") VALUES('Test', (SELECT COALESCE(MAX(id) + 1, 1) AS CurrentValue FROM TESTSCHEMA.Test))", + "INSERT INTO TESTSCHEMA.Test (stringField, intField, floatField, dateField, booleanField, charField, id, version, blobField, bigIntegerField, clobField) VALUES ('Escap''d', 7, 11.25, 20100405, 1, 'X', (SELECT COALESCE(" + ID_INCREMENTOR_TABLE_COLUMN_VALUE + ", 1) FROM TESTSCHEMA.idvalues WHERE (name = 'Test')), 0, null, 12345, null)" + ); } @@ -318,9 +315,9 @@ protected List expectedSpecifiedValueInsert() { @Override protected List expectedSpecifiedValueInsertWithTableInDifferentSchema() { return Arrays.asList( - "DELETE FROM TESTSCHEMA.idvalues where name = 'Test'", - "INSERT INTO TESTSCHEMA.idvalues (name, "+ID_INCREMENTOR_TABLE_COLUMN_VALUE+") VALUES('Test', (SELECT COALESCE(MAX(id) + 1, 1) AS CurrentValue FROM MYSCHEMA.Test))", - "INSERT INTO MYSCHEMA.Test (stringField, intField, floatField, dateField, booleanField, charField, id, version, blobField, bigIntegerField, clobField) VALUES ('Escap''d', 7, 11.25, 20100405, 1, 'X', (SELECT COALESCE("+ID_INCREMENTOR_TABLE_COLUMN_VALUE+", 1) FROM TESTSCHEMA.idvalues WHERE (name = 'Test')), 0, null, 12345, null)" + "DELETE FROM TESTSCHEMA.idvalues where name = 'Test'", + "INSERT INTO TESTSCHEMA.idvalues (name, " + ID_INCREMENTOR_TABLE_COLUMN_VALUE + ") VALUES('Test', (SELECT COALESCE(MAX(id) + 1, 1) AS CurrentValue FROM MYSCHEMA.Test))", + "INSERT INTO MYSCHEMA.Test (stringField, intField, floatField, dateField, booleanField, charField, id, version, blobField, bigIntegerField, clobField) VALUES ('Escap''d', 7, 11.25, 20100405, 1, 'X', (SELECT COALESCE(" + ID_INCREMENTOR_TABLE_COLUMN_VALUE + ", 1) FROM TESTSCHEMA.idvalues WHERE (name = 'Test')), 0, null, 12345, null)" ); } @@ -339,7 +336,7 @@ protected String expectedParameterisedInsertStatementWithNoColumnValues() { */ @Override protected String expectedEmptyStringInsertStatement() { - return "INSERT INTO TESTSCHEMA.Test (stringField, id, version, intField, floatField, dateField, booleanField, charField, blobField, bigIntegerField, clobField) VALUES (NULL, (SELECT COALESCE("+ID_INCREMENTOR_TABLE_COLUMN_VALUE+", 1) FROM TESTSCHEMA.idvalues WHERE (name = 'Test')), 0, 0, 0, null, 0, NULL, null, 12345, null)"; + return "INSERT INTO TESTSCHEMA.Test (stringField, id, version, intField, floatField, dateField, booleanField, charField, blobField, bigIntegerField, clobField) VALUES (NULL, (SELECT COALESCE(" + ID_INCREMENTOR_TABLE_COLUMN_VALUE + ", 1) FROM TESTSCHEMA.idvalues WHERE (name = 'Test')), 0, 0, 0, null, 0, NULL, null, 12345, null)"; } @@ -540,8 +537,8 @@ protected List expectedAlterTableAddStringColumnStatement() { @Override protected List expectedAlterTableAlterStringColumnStatement() { return Arrays.asList("DROP INDEX Test_NK ON TESTSCHEMA.Test", - "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN stringField NVARCHAR(6) COLLATE SQL_Latin1_General_CP1_CS_AS", - "CREATE UNIQUE NONCLUSTERED INDEX Test_NK ON TESTSCHEMA.Test ([stringField])"); + "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN stringField NVARCHAR(6) COLLATE SQL_Latin1_General_CP1_CS_AS", + "CREATE UNIQUE NONCLUSTERED INDEX Test_NK ON TESTSCHEMA.Test ([stringField])"); } @@ -560,8 +557,8 @@ protected List expectedAlterTableAddDecimalColumnStatement() { @Override protected List expectedAlterTableAlterDecimalColumnStatement() { return Arrays.asList("DROP INDEX Test_1 ON TESTSCHEMA.Test", - "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN floatField NUMERIC(14,3)", - "CREATE UNIQUE NONCLUSTERED INDEX Test_1 ON TESTSCHEMA.Test ([intField], [floatField])"); + "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN floatField NUMERIC(14,3)", + "CREATE UNIQUE NONCLUSTERED INDEX Test_1 ON TESTSCHEMA.Test ([intField], [floatField])"); } @@ -580,8 +577,8 @@ protected List expectedAlterTableAddBigIntegerColumnStatement() { @Override protected List expectedAlterTableAlterBigIntegerColumnStatement() { return Arrays.asList( - SqlServerDialect.dropDefaultForColumnSql.replace("{table}", "Test").replace("{column}", "bigIntegerField"), - "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN bigIntegerField BIGINT"); + SqlServerDialect.dropDefaultForColumnSql.replace("{table}", "Test").replace("{column}", "bigIntegerField"), + "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN bigIntegerField BIGINT"); } @@ -600,7 +597,7 @@ protected List expectedAlterTableAddBlobColumnStatement() { @Override protected List expectedAlterTableAlterBlobColumnStatement() { return Arrays.asList( - "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN blobField IMAGE NOT NULL"); + "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN blobField IMAGE NOT NULL"); } @@ -619,9 +616,9 @@ protected List expectedAlterTableAddColumnWithDefaultStatement() { @Override protected List expectedAlterTableAlterColumnWithDefaultStatement() { return Arrays.asList( - SqlServerDialect.dropDefaultForColumnSql.replace("{table}", "Test").replace("{column}", "bigIntegerField"), - "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN bigIntegerField BIGINT CONSTRAINT Test_bigIntegerField_DF DEFAULT 54321 WITH VALUES" - ); + SqlServerDialect.dropDefaultForColumnSql.replace("{table}", "Test").replace("{column}", "bigIntegerField"), + "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN bigIntegerField BIGINT CONSTRAINT Test_bigIntegerField_DF DEFAULT 54321 WITH VALUES" + ); } @@ -633,7 +630,7 @@ protected List expectedAlterTableAlterColumnWithDefaultStatement() { @Override protected List expectedAlterTableAlterBooleanColumnStatement() { return Arrays.asList( - "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN booleanField BIT NOT NULL"); + "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN booleanField BIT NOT NULL"); } @@ -661,9 +658,9 @@ protected List expectedAlterTableAddIntegerColumnStatement() { @Override protected List expectedAlterTableAlterIntegerColumnStatement() { return Arrays.asList( - "DROP INDEX Test_1 ON TESTSCHEMA.Test", - "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN intField INTEGER NOT NULL", - "CREATE UNIQUE NONCLUSTERED INDEX Test_1 ON TESTSCHEMA.Test ([intField], [floatField])" + "DROP INDEX Test_1 ON TESTSCHEMA.Test", + "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN intField INTEGER NOT NULL", + "CREATE UNIQUE NONCLUSTERED INDEX Test_1 ON TESTSCHEMA.Test ([intField], [floatField])" ); } @@ -683,7 +680,7 @@ protected List expectedAlterTableAddDateColumnStatement() { @Override protected List expectedAlterTableAlterDateColumnStatement() { return Arrays.asList( - "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN dateField DATE NOT NULL"); + "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN dateField DATE NOT NULL"); } @@ -702,7 +699,7 @@ protected List expectedAlterTableAddColumnNotNullableStatement() { @Override protected List expectedAlterTableAlterColumnFromNullableToNotNullableStatement() { return Arrays.asList( - "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN dateField DATE NOT NULL"); + "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN dateField DATE NOT NULL"); } @@ -712,8 +709,8 @@ protected List expectedAlterTableAlterColumnFromNullableToNotNullableSta @Override protected List expectedAlterTableAlterColumnFromNotNullableToNotNullableStatement() { return Arrays.asList("DROP INDEX Test_1 ON TESTSCHEMA.Test", - "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN floatField NUMERIC(20,3) NOT NULL", - "CREATE UNIQUE NONCLUSTERED INDEX Test_1 ON TESTSCHEMA.Test ([intField], [floatField])"); + "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN floatField NUMERIC(20,3) NOT NULL", + "CREATE UNIQUE NONCLUSTERED INDEX Test_1 ON TESTSCHEMA.Test ([intField], [floatField])"); } @@ -723,8 +720,8 @@ protected List expectedAlterTableAlterColumnFromNotNullableToNotNullable @Override protected List expectedAlterTableAlterColumnFromNotNullableToNullableStatement() { return Arrays.asList("DROP INDEX Test_1 ON TESTSCHEMA.Test", - "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN floatField NUMERIC(20,3)", - "CREATE UNIQUE NONCLUSTERED INDEX Test_1 ON TESTSCHEMA.Test ([intField], [floatField])"); + "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN floatField NUMERIC(20,3)", + "CREATE UNIQUE NONCLUSTERED INDEX Test_1 ON TESTSCHEMA.Test ([intField], [floatField])"); } @@ -779,9 +776,9 @@ protected List expectedIndexDropStatements() { @Override protected List expectedAlterColumnMakePrimaryStatements() { return Arrays.asList( - "ALTER TABLE TESTSCHEMA.Test DROP CONSTRAINT [Test_PK]", - "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN dateField DATE", - "ALTER TABLE TESTSCHEMA.Test ADD CONSTRAINT [Test_PK] PRIMARY KEY ([id], [dateField])" + "ALTER TABLE TESTSCHEMA.Test DROP CONSTRAINT [Test_PK]", + "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN dateField DATE", + "ALTER TABLE TESTSCHEMA.Test ADD CONSTRAINT [Test_PK] PRIMARY KEY ([id], [dateField])" ); } @@ -792,9 +789,9 @@ protected List expectedAlterColumnMakePrimaryStatements() { @Override protected List expectedAlterPrimaryKeyColumnCompositeKeyStatements() { return Arrays.asList( - "ALTER TABLE TESTSCHEMA.CompositePrimaryKey DROP CONSTRAINT [CompositePrimaryKey_PK]", - "ALTER TABLE TESTSCHEMA.CompositePrimaryKey ALTER COLUMN secondPrimaryKey NVARCHAR(5) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL", - "ALTER TABLE TESTSCHEMA.CompositePrimaryKey ADD CONSTRAINT [CompositePrimaryKey_PK] PRIMARY KEY ([id], [secondPrimaryKey])" + "ALTER TABLE TESTSCHEMA.CompositePrimaryKey DROP CONSTRAINT [CompositePrimaryKey_PK]", + "ALTER TABLE TESTSCHEMA.CompositePrimaryKey ALTER COLUMN secondPrimaryKey NVARCHAR(5) COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL", + "ALTER TABLE TESTSCHEMA.CompositePrimaryKey ADD CONSTRAINT [CompositePrimaryKey_PK] PRIMARY KEY ([id], [secondPrimaryKey])" ); } @@ -805,9 +802,9 @@ protected List expectedAlterPrimaryKeyColumnCompositeKeyStatements() { @Override protected List expectedAlterRemoveColumnFromCompositeKeyStatements() { return Arrays.asList( - "ALTER TABLE TESTSCHEMA.CompositePrimaryKey DROP CONSTRAINT [CompositePrimaryKey_PK]", - "ALTER TABLE TESTSCHEMA.CompositePrimaryKey ALTER COLUMN secondPrimaryKey NVARCHAR(5) COLLATE SQL_Latin1_General_CP1_CS_AS", - "ALTER TABLE TESTSCHEMA.CompositePrimaryKey ADD CONSTRAINT [CompositePrimaryKey_PK] PRIMARY KEY ([id])"); + "ALTER TABLE TESTSCHEMA.CompositePrimaryKey DROP CONSTRAINT [CompositePrimaryKey_PK]", + "ALTER TABLE TESTSCHEMA.CompositePrimaryKey ALTER COLUMN secondPrimaryKey NVARCHAR(5) COLLATE SQL_Latin1_General_CP1_CS_AS", + "ALTER TABLE TESTSCHEMA.CompositePrimaryKey ADD CONSTRAINT [CompositePrimaryKey_PK] PRIMARY KEY ([id])"); } @@ -817,10 +814,10 @@ protected List expectedAlterRemoveColumnFromCompositeKeyStatements() { @Override protected List expectedAlterPrimaryKeyColumnStatements() { return Arrays.asList( - "EXEC sp_rename 'TESTSCHEMA.Test.id', 'renamedId', 'COLUMN'", - "ALTER TABLE TESTSCHEMA.Test DROP CONSTRAINT [Test_PK]", - "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN renamedId BIGINT NOT NULL", - "ALTER TABLE TESTSCHEMA.Test ADD CONSTRAINT [Test_PK] PRIMARY KEY ([renamedId])"); + "EXEC sp_rename 'TESTSCHEMA.Test.id', 'renamedId', 'COLUMN'", + "ALTER TABLE TESTSCHEMA.Test DROP CONSTRAINT [Test_PK]", + "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN renamedId BIGINT NOT NULL", + "ALTER TABLE TESTSCHEMA.Test ADD CONSTRAINT [Test_PK] PRIMARY KEY ([renamedId])"); } @@ -830,8 +827,8 @@ protected List expectedAlterPrimaryKeyColumnStatements() { @Override protected List expectedAlterColumnRenamingAndChangingNullability() { return Arrays.asList( - "EXEC sp_rename 'TESTSCHEMA.Other.floatField', 'blahField', 'COLUMN'", - "ALTER TABLE TESTSCHEMA.Other ALTER COLUMN blahField NUMERIC(20,3)"); + "EXEC sp_rename 'TESTSCHEMA.Other.floatField', 'blahField', 'COLUMN'", + "ALTER TABLE TESTSCHEMA.Other ALTER COLUMN blahField NUMERIC(20,3)"); } @@ -841,7 +838,7 @@ protected List expectedAlterColumnRenamingAndChangingNullability() { @Override protected List expectedAlterColumnChangingLengthAndCase() { return Arrays.asList("EXEC sp_rename 'TESTSCHEMA.Other.floatField', 'FloatField', 'COLUMN'", - "ALTER TABLE TESTSCHEMA.Other ALTER COLUMN FloatField NUMERIC(20,3) NOT NULL"); + "ALTER TABLE TESTSCHEMA.Other ALTER COLUMN FloatField NUMERIC(20,3) NOT NULL"); } @@ -869,8 +866,8 @@ protected List expectedAlterTableAddStringColumnWithDefaultStatement() { @Override protected List expectedAlterTableDropColumnWithDefaultStatement() { return ImmutableList.of( - SqlServerDialect.dropDefaultForColumnSql.replace("{table}", "Test").replace("{column}", "bigIntegerField"), - "ALTER TABLE TESTSCHEMA.Test DROP COLUMN bigIntegerField" + SqlServerDialect.dropDefaultForColumnSql.replace("{table}", "Test").replace("{column}", "bigIntegerField"), + "ALTER TABLE TESTSCHEMA.Test DROP COLUMN bigIntegerField" ); } @@ -881,18 +878,19 @@ protected List expectedAlterTableDropColumnWithDefaultStatement() { @Override protected List expectedChangeIndexFollowedByChangeOfAssociatedColumnStatement() { return Arrays.asList( - // dropIndexStatements & addIndexStatements - "DROP INDEX Test_1 ON TESTSCHEMA.Test", - "CREATE INDEX Test_1 ON TESTSCHEMA.Test ([intField])", - // changeColumnStatements - "DROP INDEX Test_1 ON TESTSCHEMA.Test", - "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN intField INTEGER NOT NULL", - "CREATE INDEX Test_1 ON TESTSCHEMA.Test ([INTFIELD])"); + // dropIndexStatements & addIndexStatements + "DROP INDEX Test_1 ON TESTSCHEMA.Test", + "CREATE INDEX Test_1 ON TESTSCHEMA.Test ([intField])", + // changeColumnStatements + "DROP INDEX Test_1 ON TESTSCHEMA.Test", + "ALTER TABLE TESTSCHEMA.Test ALTER COLUMN intField INTEGER NOT NULL", + "CREATE INDEX Test_1 ON TESTSCHEMA.Test ([INTFIELD])"); } /** * {@inheritDoc} + * * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedAutonumberUpdate() */ @Override @@ -903,6 +901,7 @@ protected List expectedAutonumberUpdate() { /** * {@inheritDoc} + * * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedUpdateWithSelectMinimum() */ @Override @@ -915,6 +914,7 @@ protected String expectedUpdateWithSelectMinimum() { /** * {@inheritDoc} + * * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedUpdateUsingAliasedDestinationTable() */ @Override @@ -938,7 +938,7 @@ protected String expectedUpdateUsingTargetTableInDifferentSchema() { @Override protected String expectedUpdateUsingSourceTableInDifferentSchema() { return "UPDATE " + tableName("FloatingRateRate") + " SET settlementFrequency = (SELECT settlementFrequency FROM " + - "MYSCHEMA.FloatingRateDetail B WHERE (A.floatingRateDetailId = B.id)) FROM " + tableName("FloatingRateRate") + " A"; + "MYSCHEMA.FloatingRateDetail B WHERE (A.floatingRateDetailId = B.id)) FROM " + tableName("FloatingRateRate") + " A"; } @@ -978,6 +978,15 @@ protected String expectedNow() { } + + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedUnixTime() + */ + @Override + protected String expectedUnixTime(){ + return "DATEDIFF_BIG(MILLISECOND,'1970-01-01 00:00:00.000', SYSUTCDATETIME())"; + } + /** * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDaysBetween() */ diff --git a/morf-testsupport/src/main/java/org/alfasoftware/morf/jdbc/AbstractSqlDialectTest.java b/morf-testsupport/src/main/java/org/alfasoftware/morf/jdbc/AbstractSqlDialectTest.java index d8475d7c9..8b0c4c00c 100755 --- a/morf-testsupport/src/main/java/org/alfasoftware/morf/jdbc/AbstractSqlDialectTest.java +++ b/morf-testsupport/src/main/java/org/alfasoftware/morf/jdbc/AbstractSqlDialectTest.java @@ -87,6 +87,7 @@ import static org.alfasoftware.morf.sql.element.Function.sum; import static org.alfasoftware.morf.sql.element.Function.sumDistinct; import static org.alfasoftware.morf.sql.element.Function.trim; +import static org.alfasoftware.morf.sql.element.Function.unixtime; import static org.alfasoftware.morf.sql.element.Function.upperCase; import static org.alfasoftware.morf.sql.element.Function.yyyymmddToDate; import static org.junit.Assert.assertArrayEquals; @@ -2576,6 +2577,16 @@ public void testNow() { } + /** + * Test that unixtime functionality behaves as expected. + */ + @Test + public void testUnixTime() { + String result = testDialect.getSqlFrom(unixtime()); + assertEquals(expectedUnixTime(), result); + } + + /** * Test that AddDays functionality behaves as expected. */ @@ -5351,6 +5362,12 @@ protected List expectedPreInsertStatementsNotInsertingUnderAutonumLimit( protected abstract String expectedNow(); + /** + * @return The expected SQL for now function returning UTC timestamp. + */ + protected abstract String expectedUnixTime(); + + /** * @return The expected SQL for adding days */ From 4323b97859469bff5397fde6ef212f6e36e3a009 Mon Sep 17 00:00:00 2001 From: Jon Knight Date: Thu, 6 Jul 2023 18:24:07 +0100 Subject: [PATCH 05/10] Add clientHost function to allow setting the client running UpgradeSteps in SQL --- .../alfasoftware/morf/jdbc/SqlDialect.java | 56 ++++++++------ .../morf/sql/element/Function.java | 19 +++-- .../morf/sql/element/FunctionType.java | 7 +- .../morf/upgrade/AuditRecordHelper.java | 76 ++++++------------- .../GraphBasedUpgradeSchemaChangeVisitor.java | 6 +- .../upgrade/HumanReadableStatementHelper.java | 29 +++---- .../morf/upgrade/InlineTableUpgrader.java | 6 +- .../morf/upgrade/SchemaChangeSequence.java | 12 +-- .../morf/upgrade/SchemaChangeVisitor.java | 36 ++++----- .../morf/upgrade/UpgradeStepStatus.java | 3 +- .../db/DatabaseUpgradeTableContribution.java | 3 +- .../AddExtraLoggingToUpgradeAuditTable.java | 11 +-- .../morf/sql/element/TestFunctionDetail.java | 13 ++++ .../morf/upgrade/TestAuditRecordHelper.java | 32 +++++++- .../upgrade/upgrade/TestUpgradeSteps.java | 11 +++ 15 files changed, 178 insertions(+), 142 deletions(-) diff --git a/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java b/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java index 7c035bd1b..8b2d1333c 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java @@ -184,7 +184,7 @@ public SqlDialect(String schemaName) { * @return The statements required to deploy the table and its indexes. */ public Collection tableDeploymentStatements(Table table) { - Builder statements = ImmutableList.builder(); + Builder statements = ImmutableList.builder(); statements.addAll(internalTableDeploymentStatements(table)); @@ -215,16 +215,15 @@ public Collection viewDeploymentStatements(View view) { List statements = new ArrayList<>(); // Create the table deployment statement - StringBuilder createTableStatement = new StringBuilder(); - createTableStatement.append("CREATE "); - createTableStatement.append("VIEW "); - createTableStatement.append(schemaNamePrefix()); - createTableStatement.append(view.getName()); - createTableStatement.append(" AS ("); - createTableStatement.append(convertStatementToSQL(view.getSelectStatement())); - createTableStatement.append(")"); + String createTableStatement = "CREATE " + + "VIEW " + + schemaNamePrefix() + + view.getName() + + " AS (" + + convertStatementToSQL(view.getSelectStatement()) + + ")"; - statements.add(createTableStatement.toString()); + statements.add(createTableStatement); return statements; } @@ -1195,10 +1194,7 @@ protected String getSqlForOrderByField(AliasedField currentOrderByField) { return getSqlForOrderByField((FieldReference) currentOrderByField); } - StringBuilder result = new StringBuilder(getSqlFrom(currentOrderByField)); - result.append(" ").append(defaultNullOrder()); - - return result.toString().trim(); + return (getSqlFrom(currentOrderByField) + " " + defaultNullOrder()).trim(); } @@ -1325,7 +1321,7 @@ protected void appendJoin(StringBuilder result, Join join, String innerJoinKeywo } else { // MySql supports no ON criteria and ON TRUE, but the other platforms // don't, so just keep things simple. - result.append(String.format(" ON 1=1")); + result.append(" ON 1=1"); } } @@ -1568,7 +1564,7 @@ protected String getSqlFrom(Criterion criterion) { result.append(getOperatorLine(criterion, "<=")); break; case LIKE: - result.append(getOperatorLine(criterion, "LIKE") + likeEscapeSuffix()); + result.append(getOperatorLine(criterion, "LIKE")).append(likeEscapeSuffix()); break; case ISNULL: result.append(String.format("%s IS NULL", getSqlFrom(criterion.getField()))); @@ -2080,9 +2076,16 @@ protected String getSqlFrom(Function function) { case UNIX_TIME: if (!function.getArguments().isEmpty()) { - throw new IllegalArgumentException("The UNIXTIME function should have zero arguments. This function has " + function.getArguments().size()); + throw new IllegalArgumentException("The UNIX_TIME function should have zero arguments. This function has " + function.getArguments().size()); } return getSqlForUnixTime(); + + case CLIENT_HOST: + if (!function.getArguments().isEmpty()) { + throw new IllegalArgumentException("The CLIENT_HOST function should have zero arguments. This function has " + function.getArguments().size()); + } + return getSqlForClientHost(); + default: throw new UnsupportedOperationException("This database does not currently support the [" + function.getType() + "] function"); } @@ -2194,7 +2197,7 @@ protected String getSqlForCoalesce(Function function) { * @return a string representation of the SQL */ protected String getSqlForGreatest(Function function) { - return getGreatestFunctionName() + '(' + Joiner.on(", ").join(function.getArguments().stream().map(f -> getSqlFrom(f)).iterator()) + ')'; + return getGreatestFunctionName() + '(' + Joiner.on(", ").join(function.getArguments().stream().map(this::getSqlFrom).iterator()) + ')'; } @@ -2205,7 +2208,7 @@ protected String getSqlForGreatest(Function function) { * @return a string representation of the SQL */ protected String getSqlForLeast(Function function) { - return getLeastFunctionName() + '(' + Joiner.on(", ").join(function.getArguments().stream().map(f -> getSqlFrom(f)).iterator()) + ')'; + return getLeastFunctionName() + '(' + Joiner.on(", ").join(function.getArguments().stream().map(this::getSqlFrom).iterator()) + ')'; } @@ -2429,11 +2432,10 @@ protected String getSqlForRowNumber(){ } - /** * Produce SQL for getting the unix time * - * @return a long representation of the unix time + * @return A string representation of the SQL for the unix time */ protected String getSqlForUnixTime(){ // Postgres syntax, but works in H2 @@ -2441,6 +2443,16 @@ protected String getSqlForUnixTime(){ } + /** + * Produce SQL for getting the client host + * + * @return A string representation of the SQL for the client host + */ + protected String getSqlForClientHost(){ + // Postgres syntax. Return the IP Address + return "inet_client_addr()"; + } + /** * Gets the function name required to perform a substring command. @@ -4210,7 +4222,7 @@ protected Iterable getMergeStatementUpdateExpressions(MergeStateme .toSet(); List listOfKeyFieldsWithUpdateExpression = FluentIterable.from(onUpdateExpressions.keySet()) - .filter(a -> keyFields.contains(a)) + .filter(keyFields::contains) .toList(); if (!listOfKeyFieldsWithUpdateExpression.isEmpty()) { diff --git a/morf-core/src/main/java/org/alfasoftware/morf/sql/element/Function.java b/morf-core/src/main/java/org/alfasoftware/morf/sql/element/Function.java index 35319e6db..88a6b103c 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/sql/element/Function.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/sql/element/Function.java @@ -328,11 +328,11 @@ public static Function addMonths(AliasedField expression, AliasedField number) { * * * - * - * - * - * - * + * + * + * + * + * *
Database rounding references
DatabaseDatabase Manual
Oraclehttp://docs.oracle.com/cd/B19306_01/server.102/b14200/functions135.htm
MySQLhttp://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html#function_round
SQLServerhttp://technet.microsoft.com/en-us/library/ms175003.aspx
Db2400http://publib.boulder.ibm.com/infocenter/db2luw/v9/index.jsp?topic=%2Fcom.ibm.db2.udb.admin.doc%2Fdoc%2Fr0000845.htm
H2http://www.h2database.com/html/functions.html#round
OracleManual
MySQLManual
SQLServerManual
Db2400Manual
H2Manual
* * @param expression the expression to evaluate @@ -655,7 +655,14 @@ public static Function unixtime() { return new Function(FunctionType.UNIX_TIME); } - + /** + * Helper method to create an instance of the "clientHost" SQL function. + * + * @return an instance of a clientHost function + */ + public static Function clientHost() { + return new Function(FunctionType.CLIENT_HOST); + } /** diff --git a/morf-core/src/main/java/org/alfasoftware/morf/sql/element/FunctionType.java b/morf-core/src/main/java/org/alfasoftware/morf/sql/element/FunctionType.java index 7c97c1938..9f031456d 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/sql/element/FunctionType.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/sql/element/FunctionType.java @@ -232,5 +232,10 @@ public enum FunctionType { * Unix time, milliseconds since 1st January 1970 UTC */ - UNIX_TIME + UNIX_TIME, + + /** + * Identifiable label for the client machine + */ + CLIENT_HOST } \ No newline at end of file diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java index 5d02ec2e1..c86eafdba 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java @@ -19,9 +19,10 @@ import static org.alfasoftware.morf.sql.SqlUtils.field; import static org.alfasoftware.morf.sql.SqlUtils.literal; import static org.alfasoftware.morf.sql.SqlUtils.tableRef; -import static org.alfasoftware.morf.sql.element.Function.addDays; +import static org.alfasoftware.morf.sql.element.Function.clientHost; import static org.alfasoftware.morf.sql.element.Function.dateToYyyyMMddHHmmss; import static org.alfasoftware.morf.sql.element.Function.now; +import static org.alfasoftware.morf.sql.element.Function.unixtime; import static org.alfasoftware.morf.upgrade.db.DatabaseUpgradeTableContribution.UPGRADE_STEP_DESCRIPTION_LENGTH; import java.net.InetAddress; @@ -47,27 +48,6 @@ public class AuditRecordHelper { // variable to track whether the status, server and processingTimeMs columns have been added private static boolean extendedUpgradeAuditTableRowsPresent; - /* - //TODO tidy this into proper JavaDocs. - - -UpgradeAudit Table Schema: - - column("upgradeUUID", DataType.STRING, 100).primaryKey(), - column("description", DataType.STRING, 200).nullable(), - column("appliedTime", DataType.DECIMAL, 14).nullable(), - column("status", DataType.STRING, 10).nullable(), - column("server", DataType.STRING, 100).nullable(), - column("processingTimeMs", DataType.DECIMAL, 14).nullable() - - Initial insert sets upgradeUUID, description, server, appliedTime, status = SCHEDULED - - Update appliedTime, server status = RUNNING when start running - - On result set STATUS = COMPLETED/FAILED and set processingTimeMs - - */ - /** * Add the audit record, writing out the SQL for the insert. * @@ -108,7 +88,7 @@ public static void addAuditRecord(SchemaChangeVisitor visitor, Schema schema, UU if ( ! extendedUpgradeAuditTableRowsPresent ) { // adding the extended tables expanded the table from 3 columns to 6. If we have more than 5 columns // we can assume that we have the extended fields - if(schema.getTable("UpgradeAudit").columns().size() > 5) { + if(schema.getTable("UpgradeAudit").columns().size() > 6) { extendedUpgradeAuditTableRowsPresent = true; } else { // If we don't have the expanded fields we can no-op as UpgradeAudit records are only inserted after we @@ -117,23 +97,21 @@ public static void addAuditRecord(SchemaChangeVisitor visitor, Schema schema, UU } } - - InsertStatement auditRecord = createExtendedAuditInsertStatement(uuid, description); visitor.visit(new ExecuteStatement(auditRecord)); } - public static void updateRunningAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid) { + public static void updateStartedAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid) { // There's no point adding an UpgradeAudit row if the table isn't there. if (!schema.tableExists("UpgradeAudit")) return; if ( ! extendedUpgradeAuditTableRowsPresent ) { - // adding the extended tables expanded the table from 3 columns to 6. If we have more than 5 columns + // adding the extended tables expanded the table from 3 columns to 7. If we have more than 6 columns // we can assume that we have the extended fields - if(schema.getTable("UpgradeAudit").columns().size() > 5) { + if(schema.getTable("UpgradeAudit").columns().size() > 6) { extendedUpgradeAuditTableRowsPresent = true; } else { // If we don't have the expanded fields we can no-op as UpgradeAudit records are only inserted after we @@ -142,21 +120,21 @@ public static void updateRunningAuditRecord(SchemaChangeVisitor visitor, Schema } } - UpdateStatement auditRecord = createRunningAuditUpdateStatement(uuid); + UpdateStatement auditRecord = createStartedAuditUpdateStatement(uuid); visitor.visit(new ExecuteStatement(auditRecord)); } - public static void updateFinishedAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid, long processingTimeMs, boolean success, String description) { + public static void updateFinishedAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid, String description) { // There's no point adding an UpgradeAudit row if the table isn't there. if (!schema.tableExists("UpgradeAudit")) return; if ( ! extendedUpgradeAuditTableRowsPresent ) { - // adding the extended tables expanded the table from 3 columns to 6. If we have more than 5 columns + // adding the extended tables expanded the table from 3 columns to 7. If we have more than 6 columns // we can assume that we have the extended fields - if(schema.getTable("UpgradeAudit").columns().size() > 5) { + if(schema.getTable("UpgradeAudit").columns().size() > 6) { extendedUpgradeAuditTableRowsPresent = true; // Just extended so will not have a UpgradeAudit table record. So we need to add one, so it can be upgraded afterwards addAuditRecord(visitor, schema, uuid, description); @@ -166,7 +144,7 @@ public static void updateFinishedAuditRecord(SchemaChangeVisitor visitor, Schema } } - UpdateStatement auditRecord = createFinishedAuditUpdateStatement(uuid, processingTimeMs, success); + UpdateStatement auditRecord = createFinishedAuditUpdateStatement(uuid); visitor.visit(new ExecuteStatement(auditRecord)); } @@ -194,7 +172,7 @@ private static String getServerName() { // Initial insert sets upgradeUUID, description, server, appliedTime, status = SCHEDULED public static InsertStatement createExtendedAuditInsertStatement(UUID uuid, String description) { - InsertStatement auditRecord = new InsertStatement().into( + return new InsertStatement().into( tableRef("UpgradeAudit")).values( literal(uuid.toString()).as("upgradeUUID"), literal(description.length() > UPGRADE_STEP_DESCRIPTION_LENGTH ? description.substring(0, UPGRADE_STEP_DESCRIPTION_LENGTH) : description).as("description"), @@ -202,31 +180,24 @@ public static InsertStatement createExtendedAuditInsertStatement(UUID uuid, Stri literal(UpgradeStepStatus.SCHEDULED.name()).as("status"), new FieldLiteral(getServerName()).as("server") ); - return auditRecord; } - public static UpdateStatement createRunningAuditUpdateStatement(UUID uuid) { - UpdateStatement auditRecord = new UpdateStatement(new TableReference(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME)) + public static UpdateStatement createStartedAuditUpdateStatement(UUID uuid) { + return new UpdateStatement(new TableReference(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME)) .set(cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as("appliedTime")) - .set(literal(UpgradeStepStatus.RUNNING.name()).as("status")) - .set(literal(getServerName()).as("server")) + .set(literal(UpgradeStepStatus.STARTED.name()).as("status")) + .set(clientHost().as("server")) + .set(unixtime().as("startTimeMs")) .where(field("upgradeUUID").eq(uuid.toString())); - return auditRecord; } - public static UpdateStatement createFinishedAuditUpdateStatement(UUID uuid, long processingTimeMs, boolean success) { - UpdateStatement auditRecord = new UpdateStatement(new TableReference(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME)) - .set(literal(processingTimeMs).as("processingTimeMs")) - .set(literal(success ? UpgradeStepStatus.COMPLETED.name() : UpgradeStepStatus.FAILED.name()).as("status")) + public static UpdateStatement createFinishedAuditUpdateStatement(UUID uuid) { + return new UpdateStatement(new TableReference(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME)) + .set(unixtime().minus(field("startTimeMs")).as("processingTimeMs")) + .set(literal( UpgradeStepStatus.COMPLETED.name()).as("status")) .where(field("upgradeUUID").eq(uuid.toString())); - return auditRecord; } -// column("status", DataType.STRING, 10).nullable(), -// column("server", DataType.STRING, 100).nullable(), -// column("processingTimeMs", DataType.DECIMAL, 14).nullable() - - /** * Returns an {@link InsertStatement} used to be added to the upgrade audit table. @@ -237,14 +208,11 @@ public static UpdateStatement createFinishedAuditUpdateStatement(UUID uuid, long */ public static InsertStatement createAuditInsertStatement(UUID uuid, String description) { - - - InsertStatement auditRecord = new InsertStatement().into( + return new InsertStatement().into( new TableReference("UpgradeAudit")).values( new FieldLiteral(uuid.toString()).as("upgradeUUID"), new FieldLiteral(description.length() > UPGRADE_STEP_DESCRIPTION_LENGTH ? description.substring(0, UPGRADE_STEP_DESCRIPTION_LENGTH) : description).as("description"), cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as("appliedTime") ); - return auditRecord; } } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java index d90e07379..799e5f30f 100644 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java @@ -182,12 +182,12 @@ public void addAuditRecord(UUID uuid, String description) { @Override public void updateRunningAuditRecord(UUID uuid) { - AuditRecordHelper.updateRunningAuditRecord(this, sourceSchema, uuid); + AuditRecordHelper.updateStartedAuditRecord(this, sourceSchema, uuid); } @Override - public void updateFinishedAuditRecord(UUID uuid, long processingTimeMs, boolean success, String description) { - AuditRecordHelper.updateFinishedAuditRecord(this, sourceSchema, uuid, processingTimeMs, success, description); + public void updateFinishedAuditRecord(UUID uuid, String description) { + AuditRecordHelper.updateFinishedAuditRecord(this, sourceSchema, uuid, description); } /** diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/HumanReadableStatementHelper.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/HumanReadableStatementHelper.java index 1a62c9162..049c7ca7b 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/HumanReadableStatementHelper.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/HumanReadableStatementHelper.java @@ -141,6 +141,7 @@ public FunctionTypeMetaData(final String prefix, final String suffix, final Stri .put(FunctionType.UPPER, new FunctionTypeMetaData("upper case ", "", "", false, false)) .put(FunctionType.LAST_DAY_OF_MONTH, new FunctionTypeMetaData("last day of month ", "", "", false, false)) .put(FunctionType.UNIX_TIME, new FunctionTypeMetaData("unix time in milliseconds ", "", "", false, false)) + .put(FunctionType.CLIENT_HOST, new FunctionTypeMetaData("client hostname ", "", "", false, false)) .build(); /** @@ -178,12 +179,10 @@ private static String generateColumnDefinitionString(final Column definition) { * @return a string containing the human-readable version of the action */ public static String generateChangePrimaryKeyColumnsString(String tableName, List oldPrimaryKeyColumns, List newPrimaryKeyColumns) { - StringBuilder changePrimaryKeyColumnsBuilder = new StringBuilder(); - changePrimaryKeyColumnsBuilder.append(String.format("Change primary key columns on %s from %s to %s", - tableName, - "(" + Joiner.on(", ").join(oldPrimaryKeyColumns) + ")", - "(" + Joiner.on(", ").join(newPrimaryKeyColumns) + ")")); - return changePrimaryKeyColumnsBuilder.toString(); + return String.format("Change primary key columns on %s from %s to %s", + tableName, + "(" + Joiner.on(", ").join(oldPrimaryKeyColumns) + ")", + "(" + Joiner.on(", ").join(newPrimaryKeyColumns) + ")"); } @@ -193,11 +192,9 @@ public static String generateChangePrimaryKeyColumnsString(String tableName, Lis * @return a string containing the human-readable version of the action */ public static String generateChangePrimaryKeyColumnsString(String tableName, List newPrimaryKeyColumns) { - StringBuilder changePrimaryKeyColumnsBuilder = new StringBuilder(); - changePrimaryKeyColumnsBuilder.append(String.format("Change primary key columns on %s to become %s", - tableName, - "(" + Joiner.on(", ").join(newPrimaryKeyColumns) + ")")); - return changePrimaryKeyColumnsBuilder.toString(); + return String.format("Change primary key columns on %s to become %s", + tableName, + "(" + Joiner.on(", ").join(newPrimaryKeyColumns) + ")"); } @@ -391,11 +388,9 @@ public static String generateAnalyseTableFromString(String tableName) { * @return a string containing the human-readable version of the action */ public static String generateRenameTableString(String from, String to) { - StringBuilder renameTableBuilder = new StringBuilder(); - renameTableBuilder.append(String.format("Rename table %s to %s", - from, - to)); - return renameTableBuilder.toString(); + return String.format("Rename table %s to %s", + from, + to); } @@ -969,7 +964,7 @@ private static String generateSelectStatementString(final AbstractSelectStatemen if (prefix) { sb.append("select"); } - if ((AbstractSelectStatement)statement instanceof SelectFirstStatement) { + if (statement instanceof SelectFirstStatement) { if (sb.length() > 0) { sb.append(' '); } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java index 53da9195f..0a6bb878b 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java @@ -250,12 +250,12 @@ public void addAuditRecord(UUID uuid, String description) { @Override public void updateRunningAuditRecord(UUID uuid) { - AuditRecordHelper.updateRunningAuditRecord(this,currentSchema, uuid); + AuditRecordHelper.updateStartedAuditRecord(this,currentSchema, uuid); } @Override - public void updateFinishedAuditRecord(UUID uuid, long processingTimeMs, boolean success, String description) { - AuditRecordHelper.updateFinishedAuditRecord(this, currentSchema, uuid, processingTimeMs, success, description); + public void updateFinishedAuditRecord(UUID uuid, String description) { + AuditRecordHelper.updateFinishedAuditRecord(this, currentSchema, uuid, description); } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java index 22e13c754..5a408f0fb 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java @@ -147,9 +147,6 @@ public void applyTo(SchemaChangeVisitor visitor) { for (UpgradeStepWithChanges changesForStep : allChanges) { try { - // Start timer for this UpgradeStep - Instant startInstant = Instant.now(); - //TODO roll up line below into visitor.startStep // Update Audit record to show upgrade step is running @@ -164,10 +161,9 @@ public void applyTo(SchemaChangeVisitor visitor) { } // Update Audit Record will successful run - visitor.updateFinishedAuditRecord(changesForStep.getUUID(), new Interval(startInstant, Instant.now()).toDurationMillis(), true, changesForStep.getDescription()); + visitor.updateFinishedAuditRecord(changesForStep.getUUID(), changesForStep.getDescription()); } catch (Exception e) { // Set Audit Record to failed then throw runtime exception - visitor.updateFinishedAuditRecord(changesForStep.getUUID(), 0, false, changesForStep.getDescription()); throw new RuntimeException("Failed to apply step: [" + changesForStep.getUpgradeClass() + "]", e); } } @@ -415,8 +411,8 @@ private static class UpgradeStepWithChanges { /** - * @param delegate - * @param changes + * @param delegate Upgrade Step + * @param changes List of Schema Changes */ UpgradeStepWithChanges(UpgradeStep delegate, List changes) { super(); @@ -529,7 +525,7 @@ public void updateRunningAuditRecord(java.util.UUID uuid) { } @Override - public void updateFinishedAuditRecord(java.util.UUID uuid, long processingTimeMs, boolean success, String description) { + public void updateFinishedAuditRecord(java.util.UUID uuid, String description) { // no-op here. We don't need to record the UUIDs until we actually apply the changes. } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java index dc6f90a08..0b7819837 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java @@ -35,7 +35,7 @@ public interface SchemaChangeVisitor { * * @param addColumn instance of {@link AddColumn} to visit. */ - public void visit(AddColumn addColumn); + void visit(AddColumn addColumn); /** @@ -43,7 +43,7 @@ public interface SchemaChangeVisitor { * * @param addTable instance of {@link AddTable} to visit. */ - public void visit(AddTable addTable); + void visit(AddTable addTable); /** @@ -51,7 +51,7 @@ public interface SchemaChangeVisitor { * * @param removeTable instance of {@link RemoveTable} to visit. */ - public void visit(RemoveTable removeTable); + void visit(RemoveTable removeTable); /** @@ -59,7 +59,7 @@ public interface SchemaChangeVisitor { * * @param addIndex instance of {@link AddIndex} to visit. */ - public void visit(AddIndex addIndex); + void visit(AddIndex addIndex); /** @@ -67,7 +67,7 @@ public interface SchemaChangeVisitor { * * @param changeColumn instance of {@link ChangeColumn} to visit. */ - public void visit(ChangeColumn changeColumn); + void visit(ChangeColumn changeColumn); /** @@ -75,7 +75,7 @@ public interface SchemaChangeVisitor { * * @param removeColumn instance of {@link RemoveColumn} to visit. */ - public void visit(RemoveColumn removeColumn); + void visit(RemoveColumn removeColumn); /** @@ -83,7 +83,7 @@ public interface SchemaChangeVisitor { * * @param removeIndex instance of {@link RemoveIndex} to visit. */ - public void visit(RemoveIndex removeIndex); + void visit(RemoveIndex removeIndex); /** @@ -91,7 +91,7 @@ public interface SchemaChangeVisitor { * * @param changeIndex instance of {@link ChangeIndex} to visit. */ - public void visit(ChangeIndex changeIndex); + void visit(ChangeIndex changeIndex); /** @@ -99,7 +99,7 @@ public interface SchemaChangeVisitor { * * @param renameIndex instance of {@link RenameIndex} to visit. */ - public void visit(RenameIndex renameIndex); + void visit(RenameIndex renameIndex); @@ -108,7 +108,7 @@ public interface SchemaChangeVisitor { * * @param executeStatement instance of {@link ExecuteStatement} to visit. */ - public void visit(ExecuteStatement executeStatement); + void visit(ExecuteStatement executeStatement); /** @@ -116,14 +116,14 @@ public interface SchemaChangeVisitor { * * @param renameTable instance of {@link RenameTable} to visit. */ - public void visit(RenameTable renameTable); + void visit(RenameTable renameTable); /** * Perform visit operation on a {@link ChangePrimaryKeyColumns} instance. * * @param renameTable instance of {@link ChangePrimaryKeyColumns} to visit. */ - public void visit(ChangePrimaryKeyColumns renameTable); + void visit(ChangePrimaryKeyColumns renameTable); /** @@ -131,7 +131,7 @@ public interface SchemaChangeVisitor { * * @param addTableFrom instance of {@link AddTableFrom} to visit. */ - public void visit(AddTableFrom addTableFrom); + void visit(AddTableFrom addTableFrom); /** @@ -139,7 +139,7 @@ public interface SchemaChangeVisitor { * * @param analyseTable instance of {@link AnalyseTable} to visit. */ - public void visit(AnalyseTable analyseTable); + void visit(AnalyseTable analyseTable); /** @@ -148,14 +148,14 @@ public interface SchemaChangeVisitor { * @param uuid The UUID of the step which has been applied * @param description The description of the step. */ - public void addAuditRecord(java.util.UUID uuid, String description); + void addAuditRecord(java.util.UUID uuid, String description); //TODO Javadocs - public void updateRunningAuditRecord(UUID uuid); + void updateRunningAuditRecord(UUID uuid); //TODO Javadocs - public void updateFinishedAuditRecord(UUID uuid, long processingTimeMs, boolean success, String description); + void updateFinishedAuditRecord(UUID uuid, String description); /** @@ -163,5 +163,5 @@ public interface SchemaChangeVisitor { * * @param upgradeClass The upgrade step being started. */ - public void startStep(Class upgradeClass); + void startStep(Class upgradeClass); } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/UpgradeStepStatus.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/UpgradeStepStatus.java index aaf8fb242..eb1d19270 100644 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/UpgradeStepStatus.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/UpgradeStepStatus.java @@ -2,7 +2,6 @@ public enum UpgradeStepStatus { SCHEDULED, - RUNNING, - FAILED, + STARTED, COMPLETED } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/db/DatabaseUpgradeTableContribution.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/db/DatabaseUpgradeTableContribution.java index 52397cd1c..ec2d96013 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/db/DatabaseUpgradeTableContribution.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/db/DatabaseUpgradeTableContribution.java @@ -54,7 +54,8 @@ public static Table upgradeAuditTable() { column("appliedTime", DataType.DECIMAL, 14).nullable(), column("status", DataType.STRING, 10).nullable(), column("server", DataType.STRING, 100).nullable(), - column("processingTimeMs", DataType.DECIMAL, 14).nullable() + column("processingTimeMs", DataType.DECIMAL, 14).nullable(), + column("startTimeMs", DataType.DECIMAL, 18).nullable() ); } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/upgrade/AddExtraLoggingToUpgradeAuditTable.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/upgrade/AddExtraLoggingToUpgradeAuditTable.java index a7846d3fd..4404e8096 100644 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/upgrade/AddExtraLoggingToUpgradeAuditTable.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/upgrade/AddExtraLoggingToUpgradeAuditTable.java @@ -22,11 +22,6 @@ @UUID("47832d23-f1e1-422f-b6de-b76e57517334") public class AddExtraLoggingToUpgradeAuditTable implements UpgradeStep { - private final String statusColumn = "status"; - private final String serverColumn = "server"; - private final String processingTimeMsColumn = "processingTimeMs"; - - @Override public String getJiraId() { return "MORF-72"; @@ -40,12 +35,18 @@ public String getDescription() { @Override public void execute(SchemaEditor schema, DataEditor data) { + String statusColumn = "status"; schema.addColumn(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME, column(statusColumn, DataType.STRING, 10).nullable()); + String serverColumn = "server"; schema.addColumn(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME, column(serverColumn, DataType.STRING, 100).nullable()); + String processingTimeMsColumn = "processingTimeMs"; schema.addColumn(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME, column(processingTimeMsColumn, DataType.DECIMAL, 14).nullable()); + String startTimeMsColumn = "startTimeMs"; + schema.addColumn(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME, + column(startTimeMsColumn, DataType.DECIMAL, 18).nullable()); data.executeStatement( new UpdateStatement(new TableReference(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME)) diff --git a/morf-core/src/test/java/org/alfasoftware/morf/sql/element/TestFunctionDetail.java b/morf-core/src/test/java/org/alfasoftware/morf/sql/element/TestFunctionDetail.java index 2ddfbd96e..0eca8af5b 100644 --- a/morf-core/src/test/java/org/alfasoftware/morf/sql/element/TestFunctionDetail.java +++ b/morf-core/src/test/java/org/alfasoftware/morf/sql/element/TestFunctionDetail.java @@ -299,4 +299,17 @@ public void testUnixTime() { assertEquals("Function should have no arguments", 0, function.getArguments().size()); } + /** + * Tests indirect usage of the unixtime function + */ + @Test + public void testClientHost() { + Function function = Function.clientHost(); + + assertEquals("Function should be of type CLIENT_HOST", FunctionType.CLIENT_HOST, function.getType()); + assertNotNull("Function should have empty arguments", function.getArguments()); + assertEquals("Function should have no arguments", 0, function.getArguments().size()); + } + + } \ No newline at end of file diff --git a/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestAuditRecordHelper.java b/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestAuditRecordHelper.java index 915a1cbc1..9ee12829e 100755 --- a/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestAuditRecordHelper.java +++ b/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestAuditRecordHelper.java @@ -17,9 +17,11 @@ import static com.google.common.collect.FluentIterable.from; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import java.text.ParseException; @@ -56,7 +58,7 @@ public void testAddAuditRecord() throws ParseException { SchemaChangeVisitor visitor = mock(SchemaChangeVisitor.class); Schema schema = mock(Schema.class); Table table = mock(Table.class); - List columns = Arrays.asList(mock(Column.class), mock(Column.class), mock(Column.class), mock(Column.class), mock(Column.class), mock(Column.class)); + List columns = Arrays.asList(mock(Column.class), mock(Column.class), mock(Column.class), mock(Column.class), mock(Column.class), mock(Column.class), mock(Column.class)); UUID uuid = UUID.randomUUID(); String description = "Description"; @@ -75,6 +77,32 @@ public void testAddAuditRecord() throws ParseException { } + /** + * Tests the upgrade audit record is not created if the UpgradeAudit table is still in the old 3 column format + */ + @Test + public void testAddAuditRecordPreExpansion() { + // given + SchemaChangeVisitor visitor = mock(SchemaChangeVisitor.class); + Schema schema = mock(Schema.class); + Table table = mock(Table.class); + List columns = Arrays.asList(mock(Column.class), mock(Column.class), mock(Column.class)); + + UUID uuid = UUID.randomUUID(); + String description = "Description"; + given(schema.tableExists("UpgradeAudit")).willReturn(true); + given(schema.getTable("UpgradeAudit")).willReturn(table); + given(table.columns()).willReturn(columns); + + // when + AuditRecordHelper.addAuditRecord(visitor, schema, uuid, description); + + // then + ArgumentCaptor argument = ArgumentCaptor.forClass(ExecuteStatement.class); + assert(argument.getAllValues().size()==0); + } + + /** * Verifies that the {@link AuditRecordHelper#createAuditInsertStatement(UUID, String)} returns a correct * {@link InsertStatement}. @@ -124,7 +152,7 @@ private void assertAuditInsertStatement(UUID uuid, String description, InsertSta assertEquals("UUID ", description, getValueWithAlias(statement, "description").getValue()); Cast nowCastRepresentation = getCastWithAlias(statement, "appliedTime"); - assertEquals("Wrapped in integer date function with now function as argument", FunctionType.DATE_TO_YYYYMMDDHHMMSS.toString() + "(" + FunctionType.NOW + "())", nowCastRepresentation.getExpression().toString()); + assertEquals("Wrapped in integer date function with now function as argument", FunctionType.DATE_TO_YYYYMMDDHHMMSS + "(" + FunctionType.NOW + "())", nowCastRepresentation.getExpression().toString()); } diff --git a/morf-core/src/test/java/org/alfasoftware/morf/upgrade/upgrade/TestUpgradeSteps.java b/morf-core/src/test/java/org/alfasoftware/morf/upgrade/upgrade/TestUpgradeSteps.java index 90ad3d10f..3a159528e 100644 --- a/morf-core/src/test/java/org/alfasoftware/morf/upgrade/upgrade/TestUpgradeSteps.java +++ b/morf-core/src/test/java/org/alfasoftware/morf/upgrade/upgrade/TestUpgradeSteps.java @@ -33,6 +33,17 @@ public void testCreateDeployedViews() { verify(schema, times(1)).addTable(any()); } + @Test + public void testAddExtraLoggingToUpgradeAuditTable() { + AddExtraLoggingToUpgradeAuditTable upgradeStep = new AddExtraLoggingToUpgradeAuditTable(); + testUpgradeStep(upgradeStep); + SchemaEditor schema = mock(SchemaEditor.class); + DataEditor dataEditor = mock(DataEditor.class); + upgradeStep.execute(schema, dataEditor); + verify(schema, times(4)).addColumn(any(), any()); + } + + @Test public void testRecreateOracleSequences() { RecreateOracleSequences upgradeStep = new RecreateOracleSequences(); From 5711773fbdcd0eb3b4edd11e6d88eed831bd32f5 Mon Sep 17 00:00:00 2001 From: Jon Knight Date: Fri, 7 Jul 2023 12:02:39 +0100 Subject: [PATCH 06/10] Add clientHost function to allow setting the client running UpgradeSteps in SQL --- .../alfasoftware/morf/jdbc/SqlDialect.java | 20 ++++------ .../morf/sql/element/Function.java | 6 +-- .../morf/sql/element/FunctionType.java | 2 +- .../morf/upgrade/AuditRecordHelper.java | 6 +-- .../upgrade/HumanReadableStatementHelper.java | 2 +- .../alfasoftware/morf/jdbc/h2/H2Dialect.java | 18 +++++++++ .../morf/jdbc/h2/TestH2Dialect.java | 10 ++++- .../morf/examples/TestStartHere.java | 1 + .../morf/integration/TestSqlStatements.java | 35 ++++++++++++++-- .../morf/upgrade/TestFullDeployment.java | 5 ++- .../morf/jdbc/mysql/MySqlDialect.java | 13 +++++- .../morf/jdbc/mysql/TestMySqlDialect.java | 14 +++++-- .../morf/jdbc/oracle/OracleDialect.java | 15 ++++++- .../morf/jdbc/oracle/TestOracleDialect.java | 14 ++++++- .../jdbc/postgresql/PostgreSQLDialect.java | 16 ++++++++ .../postgresql/TestPostgreSQLDialect.java | 14 ++++++- .../morf/jdbc/sqlserver/SqlServerDialect.java | 16 ++++++-- .../jdbc/sqlserver/TestSqlServerDialect.java | 15 +++++-- .../morf/jdbc/AbstractSqlDialectTest.java | 40 +++++++++++++------ 19 files changed, 205 insertions(+), 57 deletions(-) diff --git a/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java b/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java index 8b2d1333c..71bb7f21d 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java @@ -2074,11 +2074,11 @@ protected String getSqlFrom(Function function) { } return getSqlForRowNumber(); - case UNIX_TIME: + case CURRENT_UNIX_TIME_MILLISECONDS: if (!function.getArguments().isEmpty()) { - throw new IllegalArgumentException("The UNIX_TIME function should have zero arguments. This function has " + function.getArguments().size()); + throw new IllegalArgumentException("The CURRENT_UNIX_TIME_MILLISECONDS function should have zero arguments. This function has " + function.getArguments().size()); } - return getSqlForUnixTime(); + return getSqlForCurrentUnixTimeMilliseconds(); case CLIENT_HOST: if (!function.getArguments().isEmpty()) { @@ -2433,14 +2433,11 @@ protected String getSqlForRowNumber(){ /** - * Produce SQL for getting the unix time + * Produce SQL for getting the current unix time in milliseconds * - * @return A string representation of the SQL for the unix time + * @return A string representation of the SQL for the current unix time in milliseconds */ - protected String getSqlForUnixTime(){ - // Postgres syntax, but works in H2 - return "trunc(extract(epoch from now() at time zone 'UTC')*1000)"; - } + abstract protected String getSqlForCurrentUnixTimeMilliseconds(); /** @@ -2448,10 +2445,7 @@ protected String getSqlForUnixTime(){ * * @return A string representation of the SQL for the client host */ - protected String getSqlForClientHost(){ - // Postgres syntax. Return the IP Address - return "inet_client_addr()"; - } + abstract protected String getSqlForClientHost(); /** diff --git a/morf-core/src/main/java/org/alfasoftware/morf/sql/element/Function.java b/morf-core/src/main/java/org/alfasoftware/morf/sql/element/Function.java index 88a6b103c..5d32160d7 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/sql/element/Function.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/sql/element/Function.java @@ -647,12 +647,12 @@ public FunctionType getType() { /** - * Helper method to create an instance of the "unixtime" SQL function. + * Helper method to create an instance of the "currentUnixTimeMilliseconds" SQL function. * * @return an instance of a unixtime function */ - public static Function unixtime() { - return new Function(FunctionType.UNIX_TIME); + public static Function currentUnixTimeMilliseconds() { + return new Function(FunctionType.CURRENT_UNIX_TIME_MILLISECONDS); } /** diff --git a/morf-core/src/main/java/org/alfasoftware/morf/sql/element/FunctionType.java b/morf-core/src/main/java/org/alfasoftware/morf/sql/element/FunctionType.java index 9f031456d..2d63eeb35 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/sql/element/FunctionType.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/sql/element/FunctionType.java @@ -232,7 +232,7 @@ public enum FunctionType { * Unix time, milliseconds since 1st January 1970 UTC */ - UNIX_TIME, + CURRENT_UNIX_TIME_MILLISECONDS, /** * Identifiable label for the client machine diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java index c86eafdba..fce47511c 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java @@ -22,7 +22,7 @@ import static org.alfasoftware.morf.sql.element.Function.clientHost; import static org.alfasoftware.morf.sql.element.Function.dateToYyyyMMddHHmmss; import static org.alfasoftware.morf.sql.element.Function.now; -import static org.alfasoftware.morf.sql.element.Function.unixtime; +import static org.alfasoftware.morf.sql.element.Function.currentUnixTimeMilliseconds; import static org.alfasoftware.morf.upgrade.db.DatabaseUpgradeTableContribution.UPGRADE_STEP_DESCRIPTION_LENGTH; import java.net.InetAddress; @@ -187,13 +187,13 @@ public static UpdateStatement createStartedAuditUpdateStatement(UUID uuid) { .set(cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as("appliedTime")) .set(literal(UpgradeStepStatus.STARTED.name()).as("status")) .set(clientHost().as("server")) - .set(unixtime().as("startTimeMs")) + .set(currentUnixTimeMilliseconds().as("startTimeMs")) .where(field("upgradeUUID").eq(uuid.toString())); } public static UpdateStatement createFinishedAuditUpdateStatement(UUID uuid) { return new UpdateStatement(new TableReference(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME)) - .set(unixtime().minus(field("startTimeMs")).as("processingTimeMs")) + .set(currentUnixTimeMilliseconds().minus(field("startTimeMs")).as("processingTimeMs")) .set(literal( UpgradeStepStatus.COMPLETED.name()).as("status")) .where(field("upgradeUUID").eq(uuid.toString())); } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/HumanReadableStatementHelper.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/HumanReadableStatementHelper.java index 049c7ca7b..9466559b7 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/HumanReadableStatementHelper.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/HumanReadableStatementHelper.java @@ -140,7 +140,7 @@ public FunctionTypeMetaData(final String prefix, final String suffix, final Stri .put(FunctionType.TRIM, new FunctionTypeMetaData("trimmed ", "", "", false, false)) .put(FunctionType.UPPER, new FunctionTypeMetaData("upper case ", "", "", false, false)) .put(FunctionType.LAST_DAY_OF_MONTH, new FunctionTypeMetaData("last day of month ", "", "", false, false)) - .put(FunctionType.UNIX_TIME, new FunctionTypeMetaData("unix time in milliseconds ", "", "", false, false)) + .put(FunctionType.CURRENT_UNIX_TIME_MILLISECONDS, new FunctionTypeMetaData("current unix time in milliseconds ", "", "", false, false)) .put(FunctionType.CLIENT_HOST, new FunctionTypeMetaData("client hostname ", "", "", false, false)) .build(); diff --git a/morf-h2/src/main/java/org/alfasoftware/morf/jdbc/h2/H2Dialect.java b/morf-h2/src/main/java/org/alfasoftware/morf/jdbc/h2/H2Dialect.java index 0a5465c54..dcbf59015 100755 --- a/morf-h2/src/main/java/org/alfasoftware/morf/jdbc/h2/H2Dialect.java +++ b/morf-h2/src/main/java/org/alfasoftware/morf/jdbc/h2/H2Dialect.java @@ -489,6 +489,24 @@ public Collection renameTableStatements(Table from, Table to) { } + /** + * @see SqlDialect#getSqlForClientHost() + */ + @Override + protected String getSqlForClientHost() { + return "CAST(SESSION_ID() AS VARCHAR2)"; + } + + + /** + * @see SqlDialect#getSqlForCurrentUnixTimeMilliseconds() + */ + @Override + protected String getSqlForCurrentUnixTimeMilliseconds(){ + return "trunc(extract(epoch from now() at time zone 'UTC')*1000)"; + } + + /** * TODO * The following is a workaround to a bug in H2 version 1.4.200 whereby the MERGE...USING statement does not release the source select statement diff --git a/morf-h2/src/test/java/org/alfasoftware/morf/jdbc/h2/TestH2Dialect.java b/morf-h2/src/test/java/org/alfasoftware/morf/jdbc/h2/TestH2Dialect.java index 80dfc02cb..916405e22 100755 --- a/morf-h2/src/test/java/org/alfasoftware/morf/jdbc/h2/TestH2Dialect.java +++ b/morf-h2/src/test/java/org/alfasoftware/morf/jdbc/h2/TestH2Dialect.java @@ -1225,10 +1225,18 @@ protected String expectedRowNumber() { * @return The expected SQL for the unix time stamp in milliseconds */ @Override - protected String expectedUnixTime() { + protected String expectedCurrentUnixTimeMilliseconds() { return "trunc(extract(epoch from now() at time zone 'UTC')*1000)"; } + /** + * @return The expected SQL for the client host + */ + @Override + protected String expectedClientHost() { + return "CAST(SESSION_ID() AS VARCHAR2)"; + } + /** * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#tableName(java.lang.String) */ diff --git a/morf-integration-test/src/test/java/org/alfasoftware/morf/examples/TestStartHere.java b/morf-integration-test/src/test/java/org/alfasoftware/morf/examples/TestStartHere.java index 407113414..4eb9374d3 100644 --- a/morf-integration-test/src/test/java/org/alfasoftware/morf/examples/TestStartHere.java +++ b/morf-integration-test/src/test/java/org/alfasoftware/morf/examples/TestStartHere.java @@ -33,6 +33,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; +import java.sql.SQLException; import java.util.Collection; import java.util.HashSet; import java.util.function.Consumer; diff --git a/morf-integration-test/src/test/java/org/alfasoftware/morf/integration/TestSqlStatements.java b/morf-integration-test/src/test/java/org/alfasoftware/morf/integration/TestSqlStatements.java index d1807692b..61589ba3c 100755 --- a/morf-integration-test/src/test/java/org/alfasoftware/morf/integration/TestSqlStatements.java +++ b/morf-integration-test/src/test/java/org/alfasoftware/morf/integration/TestSqlStatements.java @@ -52,6 +52,7 @@ import static org.alfasoftware.morf.sql.element.Function.average; import static org.alfasoftware.morf.sql.element.Function.averageDistinct; import static org.alfasoftware.morf.sql.element.Function.blobLength; +import static org.alfasoftware.morf.sql.element.Function.clientHost; import static org.alfasoftware.morf.sql.element.Function.coalesce; import static org.alfasoftware.morf.sql.element.Function.count; import static org.alfasoftware.morf.sql.element.Function.countDistinct; @@ -81,7 +82,7 @@ import static org.alfasoftware.morf.sql.element.Function.sum; import static org.alfasoftware.morf.sql.element.Function.sumDistinct; import static org.alfasoftware.morf.sql.element.Function.trim; -import static org.alfasoftware.morf.sql.element.Function.unixtime; +import static org.alfasoftware.morf.sql.element.Function.currentUnixTimeMilliseconds; import static org.alfasoftware.morf.sql.element.Function.upperCase; import static org.alfasoftware.morf.sql.element.Function.yyyymmddToDate; import static org.hamcrest.Matchers.allOf; @@ -2788,13 +2789,13 @@ public Void process(ResultSet resultSet) throws SQLException { /** - * Tests execute now function. + * Tests execute current unix time in milliseconds function. * * @throws SQLException if something goes wrong. */ @Test - public void testUnixTime() throws SQLException { - SelectStatement select = select(unixtime()); + public void testCurrentUnixTimeMilliseconds() throws SQLException { + SelectStatement select = select(currentUnixTimeMilliseconds()); SqlScriptExecutor executor = sqlScriptExecutorProvider.get(new LoggingSqlScriptVisitor()); @@ -2815,6 +2816,32 @@ public Void process(ResultSet resultSet) throws SQLException { } + /** + * Tests execute unix time function. + * + * @throws SQLException if something goes wrong. + */ + @Test + public void testClientHost() throws SQLException { + SelectStatement select = select(clientHost()); + + SqlScriptExecutor executor = sqlScriptExecutorProvider.get(new LoggingSqlScriptVisitor()); + + executor.executeQuery(convertStatementToSQL(select), connection, new ResultSetProcessor() { + @Override + public Void process(ResultSet resultSet) throws SQLException { + resultSet.next(); + + final String clientHost = resultSet.getString(1); + + log.info("Current host: " + clientHost); + + assertFalse("Client Host returns a non-empty string", clientHost.isEmpty()); + + return null; + } + }); + } @Test diff --git a/morf-integration-test/src/test/java/org/alfasoftware/morf/upgrade/TestFullDeployment.java b/morf-integration-test/src/test/java/org/alfasoftware/morf/upgrade/TestFullDeployment.java index 06fa18ffb..65354c8d8 100755 --- a/morf-integration-test/src/test/java/org/alfasoftware/morf/upgrade/TestFullDeployment.java +++ b/morf-integration-test/src/test/java/org/alfasoftware/morf/upgrade/TestFullDeployment.java @@ -147,7 +147,8 @@ public void testTwoClassDeploymentWithUpgradeSteps() throws SQLException { column("appliedTime", DataType.DECIMAL, 14).nullable(), column("status", DataType.STRING, 10).nullable(), column("server", DataType.STRING, 100).nullable(), - column("processingTimeMs", DataType.DECIMAL, 14).nullable() + column("processingTimeMs", DataType.DECIMAL, 14).nullable(), + column("startTimeMs", DataType.DECIMAL, 18).nullable() ), table("FirstTestBean").columns( column("identifier", DataType.DECIMAL, 10).nullable(), @@ -199,7 +200,7 @@ protected void configure() { ResultSet resultSetUpgradeAudit = statement.executeQuery("select * from "+schemaNamePrefix+"UpgradeAudit"); ResultSetMetaData resultSetMetaData = resultSetUpgradeAudit.getMetaData(); - assertEquals("Column Count", 6, resultSetMetaData.getColumnCount()); + assertEquals("Column Count", 7, resultSetMetaData.getColumnCount()); } finally { upgradeStatusTableService.tidyUp(connectionResources.getDataSource()); connection.close(); diff --git a/morf-mysql/src/main/java/org/alfasoftware/morf/jdbc/mysql/MySqlDialect.java b/morf-mysql/src/main/java/org/alfasoftware/morf/jdbc/mysql/MySqlDialect.java index 7c7278c67..f7af1d3c8 100755 --- a/morf-mysql/src/main/java/org/alfasoftware/morf/jdbc/mysql/MySqlDialect.java +++ b/morf-mysql/src/main/java/org/alfasoftware/morf/jdbc/mysql/MySqlDialect.java @@ -660,14 +660,23 @@ protected String getSqlForNow(Function function) { /** - * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForUnixTime() + * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForCurrentUnixTimeMilliseconds() */ @Override - protected String getSqlForUnixTime() { + protected String getSqlForCurrentUnixTimeMilliseconds() { return "CAST( 1000*UNIX_TIMESTAMP(current_timestamp(3)) AS UNSIGNED INTEGER)"; } + /** + * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForClientHost() + */ + @Override + protected String getSqlForClientHost() { + return "SUBSTRING_INDEX(USER(), '@', -1)"; + } + + /** * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForDaysBetween(org.alfasoftware.morf.sql.element.AliasedField, org.alfasoftware.morf.sql.element.AliasedField) */ diff --git a/morf-mysql/src/test/java/org/alfasoftware/morf/jdbc/mysql/TestMySqlDialect.java b/morf-mysql/src/test/java/org/alfasoftware/morf/jdbc/mysql/TestMySqlDialect.java index a0e52296e..a9c43ab7e 100755 --- a/morf-mysql/src/test/java/org/alfasoftware/morf/jdbc/mysql/TestMySqlDialect.java +++ b/morf-mysql/src/test/java/org/alfasoftware/morf/jdbc/mysql/TestMySqlDialect.java @@ -924,16 +924,24 @@ protected String expectedNow() { } - /** - * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedUnixTime() + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedCurrentUnixTimeMilliseconds() */ @Override - protected String expectedUnixTime() { + protected String expectedCurrentUnixTimeMilliseconds() { return "CAST( 1000*UNIX_TIMESTAMP(current_timestamp(3)) AS UNSIGNED INTEGER)"; } + /** + * @see AbstractSqlDialectTest#expectedClientHost() + */ + @Override + protected String expectedClientHost() { + return "SUBSTRING_INDEX(USER(), '@', -1)"; + } + + /** * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDropViewStatements() */ diff --git a/morf-oracle/src/main/java/org/alfasoftware/morf/jdbc/oracle/OracleDialect.java b/morf-oracle/src/main/java/org/alfasoftware/morf/jdbc/oracle/OracleDialect.java index 9f369e917..b0cae4788 100755 --- a/morf-oracle/src/main/java/org/alfasoftware/morf/jdbc/oracle/OracleDialect.java +++ b/morf-oracle/src/main/java/org/alfasoftware/morf/jdbc/oracle/OracleDialect.java @@ -1418,14 +1418,25 @@ protected String getSqlForLastDayOfMonth(AliasedField date) { return "LAST_DAY(" + getSqlFrom(date) + ")"; } + /** - * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForUnixTime() + * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForCurrentUnixTimeMilliseconds() */ @Override - protected String getSqlForUnixTime() { + protected String getSqlForCurrentUnixTimeMilliseconds() { return "EXTRACT(DAY FROM(sys_extract_utc(systimestamp) - to_timestamp('1970-01-01', 'YYYY-MM-DD'))) * 86400000+ to_number(TO_CHAR(sys_extract_utc(systimestamp), 'SSSSSFF3'))"; } + + /** + * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForClientHost() + */ + @Override + protected String getSqlForClientHost() { + return "SYS_CONTEXT('USERENV','HOST')"; + } + + /** * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForAnalyseTable(Table) */ diff --git a/morf-oracle/src/test/java/org/alfasoftware/morf/jdbc/oracle/TestOracleDialect.java b/morf-oracle/src/test/java/org/alfasoftware/morf/jdbc/oracle/TestOracleDialect.java index 9e3d623d6..e32197909 100755 --- a/morf-oracle/src/test/java/org/alfasoftware/morf/jdbc/oracle/TestOracleDialect.java +++ b/morf-oracle/src/test/java/org/alfasoftware/morf/jdbc/oracle/TestOracleDialect.java @@ -1176,13 +1176,23 @@ protected String expectedNow() { /** - * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedUnixTime() + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedCurrentUnixTimeMilliseconds() */ @Override - protected String expectedUnixTime() { + protected String expectedCurrentUnixTimeMilliseconds() { return "EXTRACT(DAY FROM(sys_extract_utc(systimestamp) - to_timestamp('1970-01-01', 'YYYY-MM-DD'))) * 86400000+ to_number(TO_CHAR(sys_extract_utc(systimestamp), 'SSSSSFF3'))"; } + + /** + * @see AbstractSqlDialectTest#expectedClientHost() () + */ + @Override + protected String expectedClientHost() { + return "SYS_CONTEXT('USERENV','HOST')"; + } + + /** * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDaysBetween() */ diff --git a/morf-postgresql/src/main/java/org/alfasoftware/morf/jdbc/postgresql/PostgreSQLDialect.java b/morf-postgresql/src/main/java/org/alfasoftware/morf/jdbc/postgresql/PostgreSQLDialect.java index d78805e75..b85341a38 100644 --- a/morf-postgresql/src/main/java/org/alfasoftware/morf/jdbc/postgresql/PostgreSQLDialect.java +++ b/morf-postgresql/src/main/java/org/alfasoftware/morf/jdbc/postgresql/PostgreSQLDialect.java @@ -552,6 +552,22 @@ protected String getSqlForRound(Function function) { return "ROUND((" + getSqlFrom(function.getArguments().get(0)) + ") :: NUMERIC, " + getSqlFrom(function.getArguments().get(1)) + ")"; } + /** + * @see SqlDialect#getSqlForCurrentUnixTimeMilliseconds() + */ + @Override + protected String getSqlForCurrentUnixTimeMilliseconds(){ + return "trunc(extract(epoch from now() at time zone 'UTC')*1000)"; + } + + /** + * @see SqlDialect#getSqlForClientHost() + */ + @Override + protected String getSqlForClientHost() { + return "inet_client_addr()"; + } + @Override protected String getSqlFrom(MergeStatement statement) { diff --git a/morf-postgresql/src/test/java/org/alfasoftware/morf/jdbc/postgresql/TestPostgreSQLDialect.java b/morf-postgresql/src/test/java/org/alfasoftware/morf/jdbc/postgresql/TestPostgreSQLDialect.java index cd0510770..6a347466b 100644 --- a/morf-postgresql/src/test/java/org/alfasoftware/morf/jdbc/postgresql/TestPostgreSQLDialect.java +++ b/morf-postgresql/src/test/java/org/alfasoftware/morf/jdbc/postgresql/TestPostgreSQLDialect.java @@ -985,15 +985,25 @@ protected String expectedNow() { return "NOW()"; } + /** - * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedUnixTime() + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedCurrentUnixTimeMilliseconds() */ @Override - protected String expectedUnixTime() { + protected String expectedCurrentUnixTimeMilliseconds() { return "trunc(extract(epoch from now() at time zone 'UTC')*1000)"; } + /** + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedClientHost() + */ + @Override + protected String expectedClientHost() { + return "inet_client_addr()"; + } + + /** * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDropViewStatements() */ diff --git a/morf-sqlserver/src/main/java/org/alfasoftware/morf/jdbc/sqlserver/SqlServerDialect.java b/morf-sqlserver/src/main/java/org/alfasoftware/morf/jdbc/sqlserver/SqlServerDialect.java index c77fe48ae..8f6542a70 100755 --- a/morf-sqlserver/src/main/java/org/alfasoftware/morf/jdbc/sqlserver/SqlServerDialect.java +++ b/morf-sqlserver/src/main/java/org/alfasoftware/morf/jdbc/sqlserver/SqlServerDialect.java @@ -811,16 +811,26 @@ protected String getSqlForNow(Function function) { return "GETUTCDATE()"; } + /** - * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForUnixTime() + * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForCurrentUnixTimeMilliseconds() */ @Override - protected String getSqlForUnixTime() { + protected String getSqlForCurrentUnixTimeMilliseconds() { return "DATEDIFF_BIG(MILLISECOND,'1970-01-01 00:00:00.000', SYSUTCDATETIME())"; } - /** + /** + * @see SqlDialect#getSqlForClientHost() () + */ + @Override + protected String getSqlForClientHost() { + return "HOST_NAME()"; + } + + + /** * @see org.alfasoftware.morf.jdbc.SqlDialect#getSqlForDaysBetween(org.alfasoftware.morf.sql.element.AliasedField, org.alfasoftware.morf.sql.element.AliasedField) */ @Override diff --git a/morf-sqlserver/src/test/java/org/alfasoftware/morf/jdbc/sqlserver/TestSqlServerDialect.java b/morf-sqlserver/src/test/java/org/alfasoftware/morf/jdbc/sqlserver/TestSqlServerDialect.java index 6691d6da2..6ff629c7a 100755 --- a/morf-sqlserver/src/test/java/org/alfasoftware/morf/jdbc/sqlserver/TestSqlServerDialect.java +++ b/morf-sqlserver/src/test/java/org/alfasoftware/morf/jdbc/sqlserver/TestSqlServerDialect.java @@ -978,15 +978,24 @@ protected String expectedNow() { } - /** - * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedUnixTime() + * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedCurrentUnixTimeMilliseconds() */ @Override - protected String expectedUnixTime(){ + protected String expectedCurrentUnixTimeMilliseconds(){ return "DATEDIFF_BIG(MILLISECOND,'1970-01-01 00:00:00.000', SYSUTCDATETIME())"; } + + /** + * @see AbstractSqlDialectTest#expectedClientHost() () + */ + @Override + protected String expectedClientHost(){ + return "HOST_NAME()"; + } + + /** * @see org.alfasoftware.morf.jdbc.AbstractSqlDialectTest#expectedDaysBetween() */ diff --git a/morf-testsupport/src/main/java/org/alfasoftware/morf/jdbc/AbstractSqlDialectTest.java b/morf-testsupport/src/main/java/org/alfasoftware/morf/jdbc/AbstractSqlDialectTest.java index 8b0c4c00c..5264cd185 100755 --- a/morf-testsupport/src/main/java/org/alfasoftware/morf/jdbc/AbstractSqlDialectTest.java +++ b/morf-testsupport/src/main/java/org/alfasoftware/morf/jdbc/AbstractSqlDialectTest.java @@ -58,6 +58,7 @@ import static org.alfasoftware.morf.sql.element.Function.addMonths; import static org.alfasoftware.morf.sql.element.Function.average; import static org.alfasoftware.morf.sql.element.Function.averageDistinct; +import static org.alfasoftware.morf.sql.element.Function.clientHost; import static org.alfasoftware.morf.sql.element.Function.coalesce; import static org.alfasoftware.morf.sql.element.Function.count; import static org.alfasoftware.morf.sql.element.Function.countDistinct; @@ -87,7 +88,7 @@ import static org.alfasoftware.morf.sql.element.Function.sum; import static org.alfasoftware.morf.sql.element.Function.sumDistinct; import static org.alfasoftware.morf.sql.element.Function.trim; -import static org.alfasoftware.morf.sql.element.Function.unixtime; +import static org.alfasoftware.morf.sql.element.Function.currentUnixTimeMilliseconds; import static org.alfasoftware.morf.sql.element.Function.upperCase; import static org.alfasoftware.morf.sql.element.Function.yyyymmddToDate; import static org.junit.Assert.assertArrayEquals; @@ -2578,15 +2579,24 @@ public void testNow() { /** - * Test that unixtime functionality behaves as expected. + * Test that current unix time in milliseconds functionality behaves as expected. */ @Test - public void testUnixTime() { - String result = testDialect.getSqlFrom(unixtime()); - assertEquals(expectedUnixTime(), result); + public void testCurrentUnixTimeMilliseconds() { + String result = testDialect.getSqlFrom(currentUnixTimeMilliseconds()); + assertEquals(expectedCurrentUnixTimeMilliseconds(), result); } + /** + * Test that client host functionality behaves as expected. + */ + @Test + public void testClientHost() { + String result = testDialect.getSqlFrom(clientHost()); + assertEquals(expectedClientHost(), result); + } + /** * Test that AddDays functionality behaves as expected. */ @@ -5339,19 +5349,19 @@ protected List expectedPreInsertStatementsNotInsertingUnderAutonumLimit( /** - * @return The expected SQL for conversion of YYYYMMDD into a date + * @return The expected SQL for conversion of YYYYMMDD into a date. */ protected abstract String expectedYYYYMMDDToDate(); /** - * @return The expected SQL for conversion of a date to a YYYYMMDD integer + * @return The expected SQL for conversion of a date to a YYYYMMDD integer. */ protected abstract String expectedDateToYyyymmdd(); /** - * @return The expected SQL for conversion of a date to a YYYYMMDDHHmmss integer + * @return The expected SQL for conversion of a date to a YYYYMMDDHHmmss integer. */ protected abstract String expectedDateToYyyymmddHHmmss(); @@ -5363,19 +5373,25 @@ protected List expectedPreInsertStatementsNotInsertingUnderAutonumLimit( /** - * @return The expected SQL for now function returning UTC timestamp. + * @return The expected SQL for current unix time in milliseconds function returning milliseconds since 01/01/1970 UTC. + */ + protected abstract String expectedCurrentUnixTimeMilliseconds(); + + + /** + * @return The expected SQL for client host function. */ - protected abstract String expectedUnixTime(); + protected abstract String expectedClientHost(); /** - * @return The expected SQL for adding days + * @return The expected SQL for adding days. */ protected abstract String expectedAddDays(); /** - * @return The expected SQL for adding days + * @return The expected SQL for adding days. */ protected abstract String expectedAddMonths(); From 511a3fde769e3c6b3ac596c911caceb5076f8fa1 Mon Sep 17 00:00:00 2001 From: Jon Knight Date: Fri, 7 Jul 2023 12:03:22 +0100 Subject: [PATCH 07/10] Amend unixtime function name --- .../morf/sql/element/TestFunctionDetail.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/morf-core/src/test/java/org/alfasoftware/morf/sql/element/TestFunctionDetail.java b/morf-core/src/test/java/org/alfasoftware/morf/sql/element/TestFunctionDetail.java index 0eca8af5b..a02af5178 100644 --- a/morf-core/src/test/java/org/alfasoftware/morf/sql/element/TestFunctionDetail.java +++ b/morf-core/src/test/java/org/alfasoftware/morf/sql/element/TestFunctionDetail.java @@ -288,19 +288,19 @@ public void testUpperCase() { /** - * Tests indirect usage of the unixtime function + * Tests indirect usage of the CurrentUnixTimeMilliseconds function */ @Test - public void testUnixTime() { - Function function = Function.unixtime(); + public void testCurrentUnixTimeMilliseconds() { + Function function = Function.currentUnixTimeMilliseconds(); - assertEquals("Function should be of type UNIX_TIME", FunctionType.UNIX_TIME, function.getType()); + assertEquals("Function should be of type CURRENT_UNIX_TIME_MILLISECONDS", FunctionType.CURRENT_UNIX_TIME_MILLISECONDS, function.getType()); assertNotNull("Function should have empty arguments", function.getArguments()); assertEquals("Function should have no arguments", 0, function.getArguments().size()); } /** - * Tests indirect usage of the unixtime function + * Tests indirect usage of the CurrentUnixTimeMilliseconds function */ @Test public void testClientHost() { From 83c483a3fddc4c1210b3de56d732e15bab24d32c Mon Sep 17 00:00:00 2001 From: Jon Knight Date: Tue, 11 Jul 2023 15:05:18 +0100 Subject: [PATCH 08/10] JavaDocs and other minor tidying --- .../org/alfasoftware/morf/jdbc/SqlDialect.java | 4 ++-- .../GraphBasedUpgradeSchemaChangeVisitor.java | 2 +- .../morf/upgrade/InlineTableUpgrader.java | 2 +- .../morf/upgrade/SchemaChangeSequence.java | 6 ++---- .../morf/upgrade/SchemaChangeVisitor.java | 17 +++++++++++++---- .../alfasoftware/morf/jdbc/MockDialect.java | 18 ++++++++++++++++++ .../alfasoftware/morf/upgrade/TestUpgrade.java | 3 ++- 7 files changed, 39 insertions(+), 13 deletions(-) diff --git a/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java b/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java index 71bb7f21d..c9303d7ac 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/jdbc/SqlDialect.java @@ -2437,7 +2437,7 @@ protected String getSqlForRowNumber(){ * * @return A string representation of the SQL for the current unix time in milliseconds */ - abstract protected String getSqlForCurrentUnixTimeMilliseconds(); + protected abstract String getSqlForCurrentUnixTimeMilliseconds(); /** @@ -2445,7 +2445,7 @@ protected String getSqlForRowNumber(){ * * @return A string representation of the SQL for the client host */ - abstract protected String getSqlForClientHost(); + protected abstract String getSqlForClientHost(); /** diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java index 799e5f30f..4ccc596d9 100644 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/GraphBasedUpgradeSchemaChangeVisitor.java @@ -181,7 +181,7 @@ public void addAuditRecord(UUID uuid, String description) { } @Override - public void updateRunningAuditRecord(UUID uuid) { + public void updateStartedAuditRecord(UUID uuid) { AuditRecordHelper.updateStartedAuditRecord(this, sourceSchema, uuid); } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java index 0a6bb878b..bab57e7f2 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/InlineTableUpgrader.java @@ -249,7 +249,7 @@ public void addAuditRecord(UUID uuid, String description) { @Override - public void updateRunningAuditRecord(UUID uuid) { + public void updateStartedAuditRecord(UUID uuid) { AuditRecordHelper.updateStartedAuditRecord(this,currentSchema, uuid); } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java index 5a408f0fb..fb9c0fca2 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeSequence.java @@ -32,8 +32,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; -import org.joda.time.Instant; -import org.joda.time.Interval; /** * Tracks a sequence of {@link SchemaChange}s as various {@link SchemaEditor} @@ -150,7 +148,7 @@ public void applyTo(SchemaChangeVisitor visitor) { //TODO roll up line below into visitor.startStep // Update Audit record to show upgrade step is running - visitor.updateRunningAuditRecord(changesForStep.getUUID()); + visitor.updateStartedAuditRecord(changesForStep.getUUID()); // Run prerequisites visitor.startStep(changesForStep.getUpgradeClass()); @@ -520,7 +518,7 @@ public void addAuditRecord(java.util.UUID uuid, String description) { } @Override - public void updateRunningAuditRecord(java.util.UUID uuid) { + public void updateStartedAuditRecord(java.util.UUID uuid) { // no-op here. We don't need to record the UUIDs until we actually apply the changes. } diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java index 0b7819837..a8ef7b30a 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/SchemaChangeVisitor.java @@ -143,7 +143,7 @@ public interface SchemaChangeVisitor { /** - * Add the UUID audit record. + * Add the UUID audit record in {@link UpgradeStepStatus#SCHEDULED} status * * @param uuid The UUID of the step which has been applied * @param description The description of the step. @@ -151,10 +151,19 @@ public interface SchemaChangeVisitor { void addAuditRecord(java.util.UUID uuid, String description); -//TODO Javadocs - void updateRunningAuditRecord(UUID uuid); + /** + * Update an existing audit record to {@link UpgradeStepStatus#STARTED} status + * + * @param uuid The UUID of the step which has been applied + */ + void updateStartedAuditRecord(UUID uuid); - //TODO Javadocs + /** + * Update the UUID audit record to {@link UpgradeStepStatus#SCHEDULED} status + * + * @param uuid The UUID of the step which has been applied + * @param description The description of the step. + */ void updateFinishedAuditRecord(UUID uuid, String description); diff --git a/morf-core/src/test/java/org/alfasoftware/morf/jdbc/MockDialect.java b/morf-core/src/test/java/org/alfasoftware/morf/jdbc/MockDialect.java index a2cdbbd36..1025cc67a 100755 --- a/morf-core/src/test/java/org/alfasoftware/morf/jdbc/MockDialect.java +++ b/morf-core/src/test/java/org/alfasoftware/morf/jdbc/MockDialect.java @@ -313,6 +313,24 @@ protected String getSqlForLastDayOfMonth(AliasedField date) { } + /** + * @see SqlDialect#getSqlForClientHost() + */ + @Override + protected String getSqlForClientHost() { + return "'CLIENT'"; + } + + + /** + * @see SqlDialect#getSqlForCurrentUnixTimeMilliseconds() + */ + @Override + protected String getSqlForCurrentUnixTimeMilliseconds() { + return "123456789"; + } + + /** * @see org.alfasoftware.morf.jdbc.SqlDialect#tableNameWithSchemaName(org.alfasoftware.morf.sql.element.TableReference) */ diff --git a/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestUpgrade.java b/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestUpgrade.java index af76ec5a4..eebfac4c9 100755 --- a/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestUpgrade.java +++ b/morf-core/src/test/java/org/alfasoftware/morf/upgrade/TestUpgrade.java @@ -888,7 +888,8 @@ private static Table upgradeAudit() { column("appliedTime", DataType.BIG_INTEGER).nullable(), column("status", DataType.STRING, 10).nullable(), column("server", DataType.STRING, 100).nullable(), - column("processingTimeMs", DataType.DECIMAL, 14).nullable() + column("processingTimeMs", DataType.DECIMAL, 14).nullable(), + column("startTimeMs", DataType.DECIMAL, 18).nullable() ); } From 878cdc3169d1356fe8289bc3c4c84134bbd98f1a Mon Sep 17 00:00:00 2001 From: Jon Knight Date: Tue, 11 Jul 2023 15:40:47 +0100 Subject: [PATCH 09/10] Fix Code smells from Sonar --- .../morf/upgrade/AuditRecordHelper.java | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java index fce47511c..8d2be7c1f 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java @@ -42,6 +42,11 @@ */ public class AuditRecordHelper { + private static final String upgradeAuditTableName = "UpgradeAudit"; + private static final String upgradeUuidColumnName = "upgradeUUID"; + private static final String appliedTimeColumnName = "appliedTime"; + private static final String statusColumnName = "status"; + private static final String serverColumnName = "server"; private static String serverName; @@ -50,6 +55,7 @@ public class AuditRecordHelper { /** * Add the audit record, writing out the SQL for the insert. + * @deprecated * * @see org.alfasoftware.morf.upgrade.SchemaChangeVisitor#addAuditRecord(java.util.UUID, java.lang.String) * @@ -61,7 +67,7 @@ public class AuditRecordHelper { @Deprecated public static void addAuditRecordNoStatus(SchemaChangeVisitor visitor, Schema schema, UUID uuid, String description) { // There's no point adding an UpgradeAudit row if the table isn't there. - if (!schema.tableExists("UpgradeAudit")) + if (!schema.tableExists(upgradeAuditTableName)) return; InsertStatement auditRecord = createAuditInsertStatement(uuid, description); @@ -82,13 +88,13 @@ public static void addAuditRecordNoStatus(SchemaChangeVisitor visitor, Schema sc */ public static void addAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid, String description) { // There's no point adding an UpgradeAudit row if the table isn't there. - if (!schema.tableExists("UpgradeAudit")) + if (!schema.tableExists(upgradeAuditTableName)) return; if ( ! extendedUpgradeAuditTableRowsPresent ) { // adding the extended tables expanded the table from 3 columns to 6. If we have more than 5 columns // we can assume that we have the extended fields - if(schema.getTable("UpgradeAudit").columns().size() > 6) { + if(schema.getTable(upgradeAuditTableName).columns().size() > 6) { extendedUpgradeAuditTableRowsPresent = true; } else { // If we don't have the expanded fields we can no-op as UpgradeAudit records are only inserted after we @@ -105,13 +111,13 @@ public static void addAuditRecord(SchemaChangeVisitor visitor, Schema schema, UU public static void updateStartedAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid) { // There's no point adding an UpgradeAudit row if the table isn't there. - if (!schema.tableExists("UpgradeAudit")) + if (!schema.tableExists(upgradeAuditTableName)) return; if ( ! extendedUpgradeAuditTableRowsPresent ) { // adding the extended tables expanded the table from 3 columns to 7. If we have more than 6 columns // we can assume that we have the extended fields - if(schema.getTable("UpgradeAudit").columns().size() > 6) { + if(schema.getTable(upgradeAuditTableName).columns().size() > 6) { extendedUpgradeAuditTableRowsPresent = true; } else { // If we don't have the expanded fields we can no-op as UpgradeAudit records are only inserted after we @@ -128,13 +134,13 @@ public static void updateStartedAuditRecord(SchemaChangeVisitor visitor, Schema public static void updateFinishedAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid, String description) { // There's no point adding an UpgradeAudit row if the table isn't there. - if (!schema.tableExists("UpgradeAudit")) + if (!schema.tableExists(upgradeAuditTableName)) return; if ( ! extendedUpgradeAuditTableRowsPresent ) { // adding the extended tables expanded the table from 3 columns to 7. If we have more than 6 columns // we can assume that we have the extended fields - if(schema.getTable("UpgradeAudit").columns().size() > 6) { + if(schema.getTable(upgradeAuditTableName).columns().size() > 6) { extendedUpgradeAuditTableRowsPresent = true; // Just extended so will not have a UpgradeAudit table record. So we need to add one, so it can be upgraded afterwards addAuditRecord(visitor, schema, uuid, description); @@ -173,29 +179,29 @@ private static String getServerName() { // Initial insert sets upgradeUUID, description, server, appliedTime, status = SCHEDULED public static InsertStatement createExtendedAuditInsertStatement(UUID uuid, String description) { return new InsertStatement().into( - tableRef("UpgradeAudit")).values( - literal(uuid.toString()).as("upgradeUUID"), + tableRef(upgradeAuditTableName)).values( + literal(uuid.toString()).as(upgradeUuidColumnName), literal(description.length() > UPGRADE_STEP_DESCRIPTION_LENGTH ? description.substring(0, UPGRADE_STEP_DESCRIPTION_LENGTH) : description).as("description"), - cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as("appliedTime"), - literal(UpgradeStepStatus.SCHEDULED.name()).as("status"), - new FieldLiteral(getServerName()).as("server") + cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as(appliedTimeColumnName), + literal(UpgradeStepStatus.SCHEDULED.name()).as(statusColumnName), + new FieldLiteral(getServerName()).as(serverColumnName) ); } public static UpdateStatement createStartedAuditUpdateStatement(UUID uuid) { return new UpdateStatement(new TableReference(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME)) - .set(cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as("appliedTime")) - .set(literal(UpgradeStepStatus.STARTED.name()).as("status")) - .set(clientHost().as("server")) + .set(cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as(appliedTimeColumnName)) + .set(literal(UpgradeStepStatus.STARTED.name()).as(statusColumnName)) + .set(clientHost().as(serverColumnName)) .set(currentUnixTimeMilliseconds().as("startTimeMs")) - .where(field("upgradeUUID").eq(uuid.toString())); + .where(field(upgradeUuidColumnName).eq(uuid.toString())); } public static UpdateStatement createFinishedAuditUpdateStatement(UUID uuid) { return new UpdateStatement(new TableReference(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME)) .set(currentUnixTimeMilliseconds().minus(field("startTimeMs")).as("processingTimeMs")) - .set(literal( UpgradeStepStatus.COMPLETED.name()).as("status")) - .where(field("upgradeUUID").eq(uuid.toString())); + .set(literal( UpgradeStepStatus.COMPLETED.name()).as(statusColumnName)) + .where(field(upgradeUuidColumnName).eq(uuid.toString())); } @@ -209,10 +215,10 @@ public static UpdateStatement createFinishedAuditUpdateStatement(UUID uuid) { public static InsertStatement createAuditInsertStatement(UUID uuid, String description) { return new InsertStatement().into( - new TableReference("UpgradeAudit")).values( - new FieldLiteral(uuid.toString()).as("upgradeUUID"), + new TableReference(upgradeAuditTableName)).values( + new FieldLiteral(uuid.toString()).as(upgradeUuidColumnName), new FieldLiteral(description.length() > UPGRADE_STEP_DESCRIPTION_LENGTH ? description.substring(0, UPGRADE_STEP_DESCRIPTION_LENGTH) : description).as("description"), - cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as("appliedTime") + cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as(appliedTimeColumnName) ); } } From 30e66bb6a4dd9ba4e57be3415c48b070ce7a18f8 Mon Sep 17 00:00:00 2001 From: Jon Knight Date: Tue, 11 Jul 2023 16:06:46 +0100 Subject: [PATCH 10/10] Fix Code smells from Sonar --- .../morf/upgrade/AuditRecordHelper.java | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java index 8d2be7c1f..1695a7170 100755 --- a/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java +++ b/morf-core/src/main/java/org/alfasoftware/morf/upgrade/AuditRecordHelper.java @@ -42,11 +42,11 @@ */ public class AuditRecordHelper { - private static final String upgradeAuditTableName = "UpgradeAudit"; - private static final String upgradeUuidColumnName = "upgradeUUID"; - private static final String appliedTimeColumnName = "appliedTime"; - private static final String statusColumnName = "status"; - private static final String serverColumnName = "server"; + private static final String UPGRADE_AUDIT_TABLE_NAME = "UpgradeAudit"; + private static final String UPGRADE_UUID_COLUMN_NAME = "upgradeUUID"; + private static final String APPLIED_TIME_COLUMN_NAME = "appliedTime"; + private static final String STATUS_COLUMN_NAME = "status"; + private static final String SERVER_COLUMN_NAME = "server"; private static String serverName; @@ -67,7 +67,7 @@ public class AuditRecordHelper { @Deprecated public static void addAuditRecordNoStatus(SchemaChangeVisitor visitor, Schema schema, UUID uuid, String description) { // There's no point adding an UpgradeAudit row if the table isn't there. - if (!schema.tableExists(upgradeAuditTableName)) + if (!schema.tableExists(UPGRADE_AUDIT_TABLE_NAME)) return; InsertStatement auditRecord = createAuditInsertStatement(uuid, description); @@ -88,13 +88,13 @@ public static void addAuditRecordNoStatus(SchemaChangeVisitor visitor, Schema sc */ public static void addAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid, String description) { // There's no point adding an UpgradeAudit row if the table isn't there. - if (!schema.tableExists(upgradeAuditTableName)) + if (!schema.tableExists(UPGRADE_AUDIT_TABLE_NAME)) return; if ( ! extendedUpgradeAuditTableRowsPresent ) { // adding the extended tables expanded the table from 3 columns to 6. If we have more than 5 columns // we can assume that we have the extended fields - if(schema.getTable(upgradeAuditTableName).columns().size() > 6) { + if(schema.getTable(UPGRADE_AUDIT_TABLE_NAME).columns().size() > 6) { extendedUpgradeAuditTableRowsPresent = true; } else { // If we don't have the expanded fields we can no-op as UpgradeAudit records are only inserted after we @@ -111,13 +111,13 @@ public static void addAuditRecord(SchemaChangeVisitor visitor, Schema schema, UU public static void updateStartedAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid) { // There's no point adding an UpgradeAudit row if the table isn't there. - if (!schema.tableExists(upgradeAuditTableName)) + if (!schema.tableExists(UPGRADE_AUDIT_TABLE_NAME)) return; if ( ! extendedUpgradeAuditTableRowsPresent ) { // adding the extended tables expanded the table from 3 columns to 7. If we have more than 6 columns // we can assume that we have the extended fields - if(schema.getTable(upgradeAuditTableName).columns().size() > 6) { + if(schema.getTable(UPGRADE_AUDIT_TABLE_NAME).columns().size() > 6) { extendedUpgradeAuditTableRowsPresent = true; } else { // If we don't have the expanded fields we can no-op as UpgradeAudit records are only inserted after we @@ -134,13 +134,13 @@ public static void updateStartedAuditRecord(SchemaChangeVisitor visitor, Schema public static void updateFinishedAuditRecord(SchemaChangeVisitor visitor, Schema schema, UUID uuid, String description) { // There's no point adding an UpgradeAudit row if the table isn't there. - if (!schema.tableExists(upgradeAuditTableName)) + if (!schema.tableExists(UPGRADE_AUDIT_TABLE_NAME)) return; if ( ! extendedUpgradeAuditTableRowsPresent ) { // adding the extended tables expanded the table from 3 columns to 7. If we have more than 6 columns // we can assume that we have the extended fields - if(schema.getTable(upgradeAuditTableName).columns().size() > 6) { + if(schema.getTable(UPGRADE_AUDIT_TABLE_NAME).columns().size() > 6) { extendedUpgradeAuditTableRowsPresent = true; // Just extended so will not have a UpgradeAudit table record. So we need to add one, so it can be upgraded afterwards addAuditRecord(visitor, schema, uuid, description); @@ -179,29 +179,29 @@ private static String getServerName() { // Initial insert sets upgradeUUID, description, server, appliedTime, status = SCHEDULED public static InsertStatement createExtendedAuditInsertStatement(UUID uuid, String description) { return new InsertStatement().into( - tableRef(upgradeAuditTableName)).values( - literal(uuid.toString()).as(upgradeUuidColumnName), + tableRef(UPGRADE_AUDIT_TABLE_NAME)).values( + literal(uuid.toString()).as(UPGRADE_UUID_COLUMN_NAME), literal(description.length() > UPGRADE_STEP_DESCRIPTION_LENGTH ? description.substring(0, UPGRADE_STEP_DESCRIPTION_LENGTH) : description).as("description"), - cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as(appliedTimeColumnName), - literal(UpgradeStepStatus.SCHEDULED.name()).as(statusColumnName), - new FieldLiteral(getServerName()).as(serverColumnName) + cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as(APPLIED_TIME_COLUMN_NAME), + literal(UpgradeStepStatus.SCHEDULED.name()).as(STATUS_COLUMN_NAME), + new FieldLiteral(getServerName()).as(SERVER_COLUMN_NAME) ); } public static UpdateStatement createStartedAuditUpdateStatement(UUID uuid) { return new UpdateStatement(new TableReference(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME)) - .set(cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as(appliedTimeColumnName)) - .set(literal(UpgradeStepStatus.STARTED.name()).as(statusColumnName)) - .set(clientHost().as(serverColumnName)) + .set(cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as(APPLIED_TIME_COLUMN_NAME)) + .set(literal(UpgradeStepStatus.STARTED.name()).as(STATUS_COLUMN_NAME)) + .set(clientHost().as(SERVER_COLUMN_NAME)) .set(currentUnixTimeMilliseconds().as("startTimeMs")) - .where(field(upgradeUuidColumnName).eq(uuid.toString())); + .where(field(UPGRADE_UUID_COLUMN_NAME).eq(uuid.toString())); } public static UpdateStatement createFinishedAuditUpdateStatement(UUID uuid) { return new UpdateStatement(new TableReference(DatabaseUpgradeTableContribution.UPGRADE_AUDIT_NAME)) .set(currentUnixTimeMilliseconds().minus(field("startTimeMs")).as("processingTimeMs")) - .set(literal( UpgradeStepStatus.COMPLETED.name()).as(statusColumnName)) - .where(field(upgradeUuidColumnName).eq(uuid.toString())); + .set(literal( UpgradeStepStatus.COMPLETED.name()).as(STATUS_COLUMN_NAME)) + .where(field(UPGRADE_UUID_COLUMN_NAME).eq(uuid.toString())); } @@ -215,10 +215,10 @@ public static UpdateStatement createFinishedAuditUpdateStatement(UUID uuid) { public static InsertStatement createAuditInsertStatement(UUID uuid, String description) { return new InsertStatement().into( - new TableReference(upgradeAuditTableName)).values( - new FieldLiteral(uuid.toString()).as(upgradeUuidColumnName), + new TableReference(UPGRADE_AUDIT_TABLE_NAME)).values( + new FieldLiteral(uuid.toString()).as(UPGRADE_UUID_COLUMN_NAME), new FieldLiteral(description.length() > UPGRADE_STEP_DESCRIPTION_LENGTH ? description.substring(0, UPGRADE_STEP_DESCRIPTION_LENGTH) : description).as("description"), - cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as(appliedTimeColumnName) + cast(dateToYyyyMMddHHmmss(now())).asType(DataType.DECIMAL, 14).as(APPLIED_TIME_COLUMN_NAME) ); } }