From ba666a6306b1a99d85a8761e94a9f4db798830f6 Mon Sep 17 00:00:00 2001 From: Jacek Date: Mon, 2 Aug 2021 11:09:39 +0200 Subject: [PATCH] SONAR-15234 Make 9.x migrations re-entrant --- .../sql/DbPrimaryKeyConstraintFinder.java | 4 ---- .../db/migration/step/DataChange.java | 4 ++++ .../version/v91/CreateAuditTable.java | 13 ++++++++++- .../version/v91/DropCustomMetricsData.java | 14 +++++++++++ .../version/v91/DropManualMeasuresTable.java | 10 ++++++++ ...DropUserManagedColumnFromMetricsTable.java | 23 ++++++++++++++----- .../version/v91/CreateAuditTableTest.java | 12 ++++++++++ ...DropCustomMetricsLiveMeasuresDataTest.java | 20 ++++++++++++++++ ...pCustomMetricsProjectMeasuresDataTest.java | 20 ++++++++++++++++ ...UserManagedColumnFromMetricsTableTest.java | 9 ++++++++ .../v91/DropUserManagedMetricsDataTest.java | 15 ++++++++++++ .../no_user_managed_column.sql | 19 +++++++++++++++ .../no_user_managed_column.sql | 19 +++++++++++++++ .../no_user_managed_column.sql | 19 +++++++++++++++ 14 files changed, 190 insertions(+), 11 deletions(-) create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsLiveMeasuresDataTest/no_user_managed_column.sql create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsProjectMeasuresDataTest/no_user_managed_column.sql create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropUserManagedMetricsDataTest/no_user_managed_column.sql diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/DbPrimaryKeyConstraintFinder.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/DbPrimaryKeyConstraintFinder.java index 01754aebd0c..66758cb72d0 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/DbPrimaryKeyConstraintFinder.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/DbPrimaryKeyConstraintFinder.java @@ -113,10 +113,6 @@ public class DbPrimaryKeyConstraintFinder { + "WHERE table_name = '%s' and constraint_type = 'PRIMARY KEY'", tableName.toUpperCase(Locale.ENGLISH)); } - static IllegalStateException constraintNotFoundException(String tableName) { - return new IllegalStateException(format("Cannot find constraint for table '%s'", tableName)); - } - // FIXME:: this method should be moved somewhere else String getPostgresSqlSequence(String tableName, String columnName) throws SQLException { try (Connection connection = db.getDataSource().getConnection(); diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DataChange.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DataChange.java index 6fe760c69d5..177f2fc2e1a 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DataChange.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/step/DataChange.java @@ -62,6 +62,10 @@ public abstract class DataChange implements MigrationStep { return res; } + protected Database getDatabase() { + return db; + } + public static class Context { private final Database db; private final Connection readConnection; diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/CreateAuditTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/CreateAuditTable.java index eef87adac49..bf9670f8a6e 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/CreateAuditTable.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/CreateAuditTable.java @@ -19,7 +19,9 @@ */ package org.sonar.server.platform.db.migration.version.v91; +import java.sql.SQLException; import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; import org.sonar.server.platform.db.migration.def.BigIntegerColumnDef; import org.sonar.server.platform.db.migration.def.ColumnDef; import org.sonar.server.platform.db.migration.def.VarcharColumnDef; @@ -33,7 +35,6 @@ import static java.util.stream.Stream.of; import static org.sonar.server.platform.db.migration.def.BigIntegerColumnDef.newBigIntegerColumnDefBuilder; import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE; import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder; -import java.sql.SQLException; public class CreateAuditTable extends DdlChange { @@ -43,6 +44,10 @@ public class CreateAuditTable extends DdlChange { @Override public void execute(Context context) throws SQLException { + if (auditTableExists()) { + return; + } + BigIntegerColumnDef createdAtColumn = newBigIntegerColumnDefBuilder().setColumnName("created_at").setIsNullable(false).build(); var tableName = "audits"; context.execute(new CreateTableBuilder(getDialect(), tableName) @@ -70,4 +75,10 @@ public class CreateAuditTable extends DdlChange { private static VarcharColumnDef.Builder newVarcharColumnBuilder(String column) { return newVarcharColumnDefBuilder().setColumnName(column); } + + private boolean auditTableExists() throws SQLException { + try (var connection = getDatabase().getDataSource().getConnection()) { + return DatabaseUtils.tableExists("audits", connection); + } + } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsData.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsData.java index c1036f8667d..6667a0d8872 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsData.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsData.java @@ -21,6 +21,7 @@ package org.sonar.server.platform.db.migration.version.v91; import java.sql.SQLException; import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; import org.sonar.server.platform.db.migration.step.DataChange; abstract class DropCustomMetricsData extends DataChange { @@ -31,6 +32,10 @@ abstract class DropCustomMetricsData extends DataChange { @Override protected void execute(Context context) throws SQLException { + if (!checkIfUserManagedColumnExists()) { + return; + } + var massUpdate = context.prepareMassUpdate(); massUpdate.select(selectQuery()).setBoolean(1, true); massUpdate.update(updateQuery()); @@ -41,6 +46,15 @@ abstract class DropCustomMetricsData extends DataChange { }); } + private boolean checkIfUserManagedColumnExists() throws SQLException { + try (var connection = getDatabase().getDataSource().getConnection()) { + if (DatabaseUtils.tableColumnExists(connection, "metrics", "user_managed")) { + return true; + } + } + return false; + } + abstract String selectQuery(); abstract String updateQuery(); diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropManualMeasuresTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropManualMeasuresTable.java index 3856a5adce8..209126cc82f 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropManualMeasuresTable.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropManualMeasuresTable.java @@ -21,6 +21,7 @@ package org.sonar.server.platform.db.migration.version.v91; import java.sql.SQLException; import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; import org.sonar.server.platform.db.migration.sql.DropTableBuilder; import org.sonar.server.platform.db.migration.step.DdlChange; @@ -33,6 +34,15 @@ public class DropManualMeasuresTable extends DdlChange { @Override public void execute(Context context) throws SQLException { + if (!manualMeasuresExists()) { + return; + } context.execute(new DropTableBuilder(getDialect(), TABLE_NAME).build()); } + + private boolean manualMeasuresExists() throws SQLException { + try (var connection = getDatabase().getDataSource().getConnection()) { + return DatabaseUtils.tableExists(TABLE_NAME, connection); + } + } } diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTable.java index db4c4b80956..56e8bb18f10 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTable.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTable.java @@ -21,6 +21,7 @@ package org.sonar.server.platform.db.migration.version.v91; import java.sql.SQLException; import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; import org.sonar.db.dialect.MsSql; import org.sonar.server.platform.db.migration.sql.DropColumnsBuilder; import org.sonar.server.platform.db.migration.sql.DropMsSQLDefaultConstraintsBuilder; @@ -31,18 +32,28 @@ public class DropUserManagedColumnFromMetricsTable extends DdlChange { private static final String TABLE_NAME = "metrics"; private static final String COLUMN = "user_managed"; - private final Database db; - public DropUserManagedColumnFromMetricsTable(Database db) { super(db); - this.db = db; } @Override public void execute(Context context) throws SQLException { - if (MsSql.ID.equals(db.getDialect().getId())) { - context.execute(new DropMsSQLDefaultConstraintsBuilder(db).setTable(TABLE_NAME).setColumns(COLUMN).build()); + if (!checkIfUseManagedColumnExists()) { + return; + } + + if (MsSql.ID.equals(getDatabase().getDialect().getId())) { + context.execute(new DropMsSQLDefaultConstraintsBuilder(getDatabase()).setTable(TABLE_NAME).setColumns(COLUMN).build()); + } + context.execute(new DropColumnsBuilder(getDatabase().getDialect(), TABLE_NAME, COLUMN).build()); + } + + private boolean checkIfUseManagedColumnExists() throws SQLException { + try (var connection = getDatabase().getDataSource().getConnection()) { + if (DatabaseUtils.tableColumnExists(connection, TABLE_NAME, COLUMN)) { + return true; + } } - context.execute(new DropColumnsBuilder(db.getDialect(), TABLE_NAME, COLUMN).build()); + return false; } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/CreateAuditTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/CreateAuditTableTest.java index c1be0fdbd83..30a67f44027 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/CreateAuditTableTest.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/CreateAuditTableTest.java @@ -42,4 +42,16 @@ public class CreateAuditTableTest { db.assertTableExists(TABLE_NAME); db.assertIndex(TABLE_NAME, "audits_created_at", "created_at"); } + + @Test + public void migration_should_be_reentrant() throws SQLException { + db.assertTableDoesNotExist(TABLE_NAME); + + underTest.execute(); + //re-entrant + underTest.execute(); + + db.assertTableExists(TABLE_NAME); + db.assertIndex(TABLE_NAME, "audits_created_at", "created_at"); + } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsLiveMeasuresDataTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsLiveMeasuresDataTest.java index 1eb208caf58..2a63bc6f27a 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsLiveMeasuresDataTest.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsLiveMeasuresDataTest.java @@ -37,6 +37,9 @@ public class DropCustomMetricsLiveMeasuresDataTest { @Rule public final CoreDbTester db = CoreDbTester.createForSchema(DropCustomMetricsLiveMeasuresDataTest.class, "schema.sql"); + @Rule + public final CoreDbTester dbWithoutColumn = CoreDbTester.createForSchema(DropCustomMetricsLiveMeasuresDataTest.class, "no_user_managed_column.sql"); + private final DataChange underTest = new DropCustomMetricsLiveMeasuresData(db.database()); @Test @@ -124,6 +127,23 @@ public class DropCustomMetricsLiveMeasuresDataTest { .containsExactlyInAnyOrder("lm-1", "lm-2", "lm-3", "lm-4"); } + @Test + public void does_not_fail_when_no_user_managed_column() throws SQLException { + insertMetric("metric-1", false); + insertMetric("metric-2", false); + + insertLiveMeasure("lm-1", "metric-1"); + insertLiveMeasure("lm-2", "metric-1"); + insertLiveMeasure("lm-3", "metric-2"); + insertLiveMeasure("lm-4", "metric-2"); + + DataChange underTest = new DropCustomMetricsLiveMeasuresData(dbWithoutColumn.database()); + underTest.execute(); + + assertThat(db.select("select uuid from live_measures").stream().map(row -> row.get("UUID")).collect(Collectors.toList())) + .containsExactlyInAnyOrder("lm-1", "lm-2", "lm-3", "lm-4"); + } + private void insertLiveMeasure(String uuid, String metricUuid) { db.executeInsert("live_measures", "UUID", uuid, diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsProjectMeasuresDataTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsProjectMeasuresDataTest.java index 9bf44f01f6a..74988e691ff 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsProjectMeasuresDataTest.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsProjectMeasuresDataTest.java @@ -37,6 +37,9 @@ public class DropCustomMetricsProjectMeasuresDataTest { @Rule public final CoreDbTester db = CoreDbTester.createForSchema(DropCustomMetricsProjectMeasuresDataTest.class, "schema.sql"); + @Rule + public final CoreDbTester dbWithoutColumn = CoreDbTester.createForSchema(DropCustomMetricsProjectMeasuresDataTest.class, "no_user_managed_column.sql"); + private final DataChange underTest = new DropCustomMetricsProjectMeasuresData(db.database()); @Test @@ -124,6 +127,23 @@ public class DropCustomMetricsProjectMeasuresDataTest { .containsExactlyInAnyOrder("pm-1", "pm-2", "pm-3", "pm-4"); } + @Test + public void does_not_fail_when_no_user_managed_column() throws SQLException { + insertMetric("metric-1", false); + insertMetric("metric-2", false); + + insertProjectMeasure("pm-1", "metric-1"); + insertProjectMeasure("pm-2", "metric-1"); + insertProjectMeasure("pm-3", "metric-2"); + insertProjectMeasure("pm-4", "metric-2"); + + DataChange underTest = new DropCustomMetricsProjectMeasuresData(dbWithoutColumn.database()); + underTest.execute(); + + assertThat(db.select("select uuid from project_measures").stream().map(row -> row.get("UUID")).collect(Collectors.toList())) + .containsExactlyInAnyOrder("pm-1", "pm-2", "pm-3", "pm-4"); + } + private void insertProjectMeasure(String uuid, String metricUuid) { db.executeInsert("project_measures", "UUID", uuid, diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTableTest.java index 5c734fe2226..da712d80d66 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTableTest.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedColumnFromMetricsTableTest.java @@ -41,4 +41,13 @@ public class DropUserManagedColumnFromMetricsTableTest { underTest.execute(); db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); } + + @Test + public void migration_should_be_reentrant() throws SQLException { + db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.BOOLEAN, null, true); + underTest.execute(); + // re-entrant + underTest.execute(); + db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME); + } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedMetricsDataTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedMetricsDataTest.java index 98ffe2ab21e..7476e750a47 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedMetricsDataTest.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v91/DropUserManagedMetricsDataTest.java @@ -34,6 +34,9 @@ public class DropUserManagedMetricsDataTest { @Rule public final CoreDbTester db = CoreDbTester.createForSchema(DropUserManagedMetricsDataTest.class, "schema.sql"); + @Rule + public final CoreDbTester dbWithoutColumn = CoreDbTester.createForSchema(DropUserManagedMetricsDataTest.class, "no_user_managed_column.sql"); + private final DataChange underTest = new DropUserManagedMetricsData(db.database()); @Test @@ -99,6 +102,18 @@ public class DropUserManagedMetricsDataTest { assertThat(db.countRowsOfTable(TABLE_NAME)).isEqualTo(3); } + @Test + public void does_not_fail_when_no_user_managed_column() throws SQLException { + insertMetric("1", false); + insertMetric("2", false); + insertMetric("3", false); + + DataChange underTest = new DropCustomMetricsProjectMeasuresData(dbWithoutColumn.database()); + underTest.execute(); + + assertThat(db.countRowsOfTable(TABLE_NAME)).isEqualTo(3); + } + private void insertMetric(String uuid, boolean userManaged) { db.executeInsert(TABLE_NAME, "UUID", uuid, diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsLiveMeasuresDataTest/no_user_managed_column.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsLiveMeasuresDataTest/no_user_managed_column.sql new file mode 100644 index 00000000000..4003c8392cc --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsLiveMeasuresDataTest/no_user_managed_column.sql @@ -0,0 +1,19 @@ +CREATE TABLE "METRICS"( + "UUID" VARCHAR(40) NOT NULL, + "NAME" VARCHAR(64) NOT NULL, + "DESCRIPTION" VARCHAR(255), + "DIRECTION" INTEGER DEFAULT 0 NOT NULL, + "DOMAIN" VARCHAR(64), + "SHORT_NAME" VARCHAR(64), + "QUALITATIVE" BOOLEAN DEFAULT FALSE NOT NULL, + "VAL_TYPE" VARCHAR(8), + "ENABLED" BOOLEAN DEFAULT TRUE, + "WORST_VALUE" DOUBLE, + "BEST_VALUE" DOUBLE, + "OPTIMIZED_BEST_VALUE" BOOLEAN, + "HIDDEN" BOOLEAN, + "DELETE_HISTORICAL_DATA" BOOLEAN, + "DECIMAL_SCALE" INTEGER +); +ALTER TABLE "METRICS" ADD CONSTRAINT "PK_METRICS" PRIMARY KEY("UUID"); +CREATE UNIQUE INDEX "METRICS_UNIQUE_NAME" ON "METRICS"("NAME"); diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsProjectMeasuresDataTest/no_user_managed_column.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsProjectMeasuresDataTest/no_user_managed_column.sql new file mode 100644 index 00000000000..4003c8392cc --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropCustomMetricsProjectMeasuresDataTest/no_user_managed_column.sql @@ -0,0 +1,19 @@ +CREATE TABLE "METRICS"( + "UUID" VARCHAR(40) NOT NULL, + "NAME" VARCHAR(64) NOT NULL, + "DESCRIPTION" VARCHAR(255), + "DIRECTION" INTEGER DEFAULT 0 NOT NULL, + "DOMAIN" VARCHAR(64), + "SHORT_NAME" VARCHAR(64), + "QUALITATIVE" BOOLEAN DEFAULT FALSE NOT NULL, + "VAL_TYPE" VARCHAR(8), + "ENABLED" BOOLEAN DEFAULT TRUE, + "WORST_VALUE" DOUBLE, + "BEST_VALUE" DOUBLE, + "OPTIMIZED_BEST_VALUE" BOOLEAN, + "HIDDEN" BOOLEAN, + "DELETE_HISTORICAL_DATA" BOOLEAN, + "DECIMAL_SCALE" INTEGER +); +ALTER TABLE "METRICS" ADD CONSTRAINT "PK_METRICS" PRIMARY KEY("UUID"); +CREATE UNIQUE INDEX "METRICS_UNIQUE_NAME" ON "METRICS"("NAME"); diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropUserManagedMetricsDataTest/no_user_managed_column.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropUserManagedMetricsDataTest/no_user_managed_column.sql new file mode 100644 index 00000000000..4003c8392cc --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v91/DropUserManagedMetricsDataTest/no_user_managed_column.sql @@ -0,0 +1,19 @@ +CREATE TABLE "METRICS"( + "UUID" VARCHAR(40) NOT NULL, + "NAME" VARCHAR(64) NOT NULL, + "DESCRIPTION" VARCHAR(255), + "DIRECTION" INTEGER DEFAULT 0 NOT NULL, + "DOMAIN" VARCHAR(64), + "SHORT_NAME" VARCHAR(64), + "QUALITATIVE" BOOLEAN DEFAULT FALSE NOT NULL, + "VAL_TYPE" VARCHAR(8), + "ENABLED" BOOLEAN DEFAULT TRUE, + "WORST_VALUE" DOUBLE, + "BEST_VALUE" DOUBLE, + "OPTIMIZED_BEST_VALUE" BOOLEAN, + "HIDDEN" BOOLEAN, + "DELETE_HISTORICAL_DATA" BOOLEAN, + "DECIMAL_SCALE" INTEGER +); +ALTER TABLE "METRICS" ADD CONSTRAINT "PK_METRICS" PRIMARY KEY("UUID"); +CREATE UNIQUE INDEX "METRICS_UNIQUE_NAME" ON "METRICS"("NAME"); -- 2.39.5