diff --git a/network-store-server/pom.xml b/network-store-server/pom.xml index 867e2e33..5a819b77 100644 --- a/network-store-server/pom.xml +++ b/network-store-server/pom.xml @@ -107,7 +107,6 @@ org.liquibase liquibase-core - runtime org.slf4j diff --git a/network-store-server/src/main/java/com/powsybl/network/store/server/NetworkStoreRepository.java b/network-store-server/src/main/java/com/powsybl/network/store/server/NetworkStoreRepository.java index 26bef589..8ab00ca3 100644 --- a/network-store-server/src/main/java/com/powsybl/network/store/server/NetworkStoreRepository.java +++ b/network-store-server/src/main/java/com/powsybl/network/store/server/NetworkStoreRepository.java @@ -6,9 +6,9 @@ */ package com.powsybl.network.store.server; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.*; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.google.common.base.Stopwatch; import com.google.common.collect.Lists; @@ -22,7 +22,12 @@ import com.powsybl.network.store.server.dto.PermanentLimitAttributes; import com.powsybl.network.store.server.exceptions.JsonApiErrorResponseException; import com.powsybl.network.store.server.exceptions.UncheckedSqlException; +import com.powsybl.network.store.server.json.PermanentLimitSqlData; +import com.powsybl.network.store.server.json.TemporaryLimitSqlData; +import com.powsybl.network.store.server.migration.v211limits.V211LimitsMigration; +import com.powsybl.network.store.server.migration.v211limits.V211LimitsQueryCatalog; import com.powsybl.ws.commons.LogUtils; +import lombok.Getter; import org.apache.commons.lang3.mutable.MutableInt; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; @@ -31,10 +36,8 @@ import org.springframework.util.CollectionUtils; import javax.sql.DataSource; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; +import java.io.UncheckedIOException; +import java.sql.*; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -43,6 +46,7 @@ import static com.powsybl.network.store.server.QueryCatalog.*; import static com.powsybl.network.store.server.Utils.bindAttributes; import static com.powsybl.network.store.server.Utils.bindValues; +import static com.powsybl.network.store.server.migration.v211limits.V211LimitsMigration.*; /** * @author Geoffroy Jamgotchian @@ -62,6 +66,7 @@ public NetworkStoreRepository(DataSource dataSource, ObjectMapper mapper, Mappin this.extensionHandler = extensionHandler; } + @Getter private final DataSource dataSource; private final ObjectMapper mapper; @@ -280,12 +285,22 @@ public void deleteNetwork(UUID uuid) { } } // Delete of the temporary limits (which are not Identifiables objects) + //TODO: to be removed when limits are fully migrated — should be after v2.13 deployment + try (var preparedStmt = connection.prepareStatement(V211LimitsQueryCatalog.buildDeleteV211TemporaryLimitsQuery())) { + preparedStmt.setObject(1, uuid.toString()); + preparedStmt.executeUpdate(); + } try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildDeleteTemporaryLimitsQuery())) { preparedStmt.setObject(1, uuid.toString()); preparedStmt.executeUpdate(); } // Delete permanent limits (which are not Identifiables objects) + //TODO: to be removed when limits are fully migrated — should be after v2.13 deployment + try (var preparedStmt = connection.prepareStatement(V211LimitsQueryCatalog.buildDeleteV211PermanentLimitsQuery())) { + preparedStmt.setObject(1, uuid.toString()); + preparedStmt.executeUpdate(); + } try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildDeletePermanentLimitsQuery())) { preparedStmt.setObject(1, uuid.toString()); preparedStmt.executeUpdate(); @@ -340,6 +355,12 @@ public void deleteNetwork(UUID uuid, int variantNum) { } } // Delete of the temporary limits (which are not Identifiables objects) + //TODO: to be removed when limits are fully migrated — should be after v2.13 deployment + try (var preparedStmt = connection.prepareStatement(V211LimitsQueryCatalog.buildDeleteV211TemporaryLimitsVariantQuery())) { + preparedStmt.setObject(1, uuid.toString()); + preparedStmt.setInt(2, variantNum); + preparedStmt.executeUpdate(); + } try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildDeleteTemporaryLimitsVariantQuery())) { preparedStmt.setObject(1, uuid.toString()); preparedStmt.setInt(2, variantNum); @@ -347,6 +368,12 @@ public void deleteNetwork(UUID uuid, int variantNum) { } // Delete permanent limits (which are not Identifiables objects) + //TODO: to be removed when limits are fully migrated — should be after v2.13 deployment + try (var preparedStmt = connection.prepareStatement(V211LimitsQueryCatalog.buildDeleteV211PermanentLimitsVariantQuery())) { + preparedStmt.setObject(1, uuid.toString()); + preparedStmt.setInt(2, variantNum); + preparedStmt.executeUpdate(); + } try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildDeletePermanentLimitsVariantQuery())) { preparedStmt.setObject(1, uuid.toString()); preparedStmt.setInt(2, variantNum); @@ -454,6 +481,17 @@ public void cloneNetworkElements(Connection connection, UUID uuid, UUID targetUu } } // Copy of the temporary limits (which are not Identifiables objects) + //TODO: to be removed when limits are fully migrated — should be after v2.13 deployment + /////////////////////////////////////////////////////////////////////////////////////// + int insertedRows = 0; + try (var preparedStmt = connection.prepareStatement(V211LimitsQueryCatalog.buildCloneV211TemporaryLimitsQuery())) { + preparedStmt.setString(1, targetUuid.toString()); + preparedStmt.setInt(2, targetVariantNum); + preparedStmt.setString(3, uuid.toString()); + preparedStmt.setInt(4, sourceVariantNum); + insertedRows += preparedStmt.executeUpdate(); + } + /////////////////////////////////////////////////////////////////////////////////////// try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildCloneTemporaryLimitsQuery())) { preparedStmt.setString(1, targetUuid.toString()); preparedStmt.setInt(2, targetVariantNum); @@ -463,6 +501,16 @@ public void cloneNetworkElements(Connection connection, UUID uuid, UUID targetUu } // Copy of the permanent limits (which are not Identifiables objects) + //TODO: to be removed when limits are fully migrated — should be after v2.13 deployment + /////////////////////////////////////////////////////////////////////////////////////// + try (var preparedStmt = connection.prepareStatement(V211LimitsQueryCatalog.buildCloneV211PermanentLimitsQuery())) { + preparedStmt.setString(1, targetUuid.toString()); + preparedStmt.setInt(2, targetVariantNum); + preparedStmt.setString(3, uuid.toString()); + preparedStmt.setInt(4, sourceVariantNum); + insertedRows += preparedStmt.executeUpdate(); + } + /////////////////////////////////////////////////////////////////////////////////////// try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildClonePermanentLimitsQuery())) { preparedStmt.setString(1, targetUuid.toString()); preparedStmt.setInt(2, targetVariantNum); @@ -471,6 +519,13 @@ public void cloneNetworkElements(Connection connection, UUID uuid, UUID targetUu preparedStmt.execute(); } + //TODO: to be removed when limits are fully migrated — should be after v2.13 deployment + // We don't want the V2.12 code to create V2.11 limits in the database and to inflate the remainder of the limits to migrate. + // So after the clone of the V2.11 limits, if some v2.11 limits were cloned, we migrate them. + if (insertedRows > 0) { + V211LimitsMigration.migrateV211Limits(this, targetUuid, targetVariantNum); + } + // Copy of the reactive capability curve points (which are not Identifiables objects) try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildCloneReactiveCapabilityCurvePointsQuery())) { preparedStmt.setString(1, targetUuid.toString()); @@ -1799,12 +1854,30 @@ public Map> getPermanentLimitsWithInCl } public Map getLimitsInfos(UUID networkUuid, int variantNum, String columnNameForWhereClause, String valueForWhereClause) { + //TODO: to be removed when limits are fully migrated — should be after v2.13 deployment + /////////////////////////////////////////////////////////////////////////////////////// + Map> v211TemporaryLimits = getV211TemporaryLimits(this, networkUuid, variantNum, columnNameForWhereClause, valueForWhereClause); + Map> v211PermanentLimits = getV211PermanentLimits(this, networkUuid, variantNum, columnNameForWhereClause, valueForWhereClause); + if (!v211TemporaryLimits.isEmpty() || !v211PermanentLimits.isEmpty()) { + return mergeLimitsIntoLimitsInfos(v211TemporaryLimits, v211PermanentLimits); + } + /////////////////////////////////////////////////////////////////////////////////////// + Map> temporaryLimits = getTemporaryLimits(networkUuid, variantNum, columnNameForWhereClause, valueForWhereClause); Map> permanentLimits = getPermanentLimits(networkUuid, variantNum, columnNameForWhereClause, valueForWhereClause); return mergeLimitsIntoLimitsInfos(temporaryLimits, permanentLimits); } public Map getLimitsInfosWithInClause(UUID networkUuid, int variantNum, String columnNameForWhereClause, List valuesForInClause) { + //TODO: to be removed when limits are fully migrated — should be after v2.13 deployment + /////////////////////////////////////////////////////////////////////////////////////// + Map> v211TemporaryLimits = getV211TemporaryLimitsWithInClause(this, networkUuid, variantNum, columnNameForWhereClause, valuesForInClause); + Map> v211PermanentLimits = getV211PermanentLimitsWithInClause(this, networkUuid, variantNum, columnNameForWhereClause, valuesForInClause); + if (!v211TemporaryLimits.isEmpty() || !v211PermanentLimits.isEmpty()) { + return mergeLimitsIntoLimitsInfos(v211TemporaryLimits, v211PermanentLimits); + } + /////////////////////////////////////////////////////////////////////////////////////// + Map> temporaryLimits = getTemporaryLimitsWithInClause(networkUuid, variantNum, columnNameForWhereClause, valuesForInClause); Map> permanentLimits = getPermanentLimitsWithInClause(networkUuid, variantNum, columnNameForWhereClause, valuesForInClause); return mergeLimitsIntoLimitsInfos(temporaryLimits, permanentLimits); @@ -1854,27 +1927,23 @@ private Map> innerGetTemporaryLimits(P try (ResultSet resultSet = preparedStmt.executeQuery()) { Map> map = new HashMap<>(); while (resultSet.next()) { - OwnerInfo owner = new OwnerInfo(); - TemporaryLimitAttributes temporaryLimit = new TemporaryLimitAttributes(); // In order, from the QueryCatalog.buildTemporaryLimitQuery SQL query : // equipmentId, equipmentType, networkUuid, variantNum, side, limitType, name, value, acceptableDuration, fictitious owner.setEquipmentId(resultSet.getString(1)); owner.setEquipmentType(ResourceType.valueOf(resultSet.getString(2))); owner.setNetworkUuid(UUID.fromString(resultSet.getString(3))); owner.setVariantNum(resultSet.getInt(4)); - temporaryLimit.setOperationalLimitsGroupId(resultSet.getString(5)); - temporaryLimit.setSide(resultSet.getInt(6)); - temporaryLimit.setLimitType(LimitType.valueOf(resultSet.getString(7))); - temporaryLimit.setName(resultSet.getString(8)); - temporaryLimit.setValue(resultSet.getDouble(9)); - temporaryLimit.setAcceptableDuration(resultSet.getInt(10)); - temporaryLimit.setFictitious(resultSet.getBoolean(11)); - - map.computeIfAbsent(owner, k -> new ArrayList<>()); - map.get(owner).add(temporaryLimit); + String temporaryLimitData = resultSet.getString(5); + List parsedTemporaryLimitData = mapper.readValue(temporaryLimitData, new TypeReference<>() { }); + List temporaryLimits = parsedTemporaryLimitData.stream().map(TemporaryLimitSqlData::toTemporaryLimitAttributes).toList(); + if (!temporaryLimits.isEmpty()) { + map.put(owner, temporaryLimits); + } } return map; + } catch (JsonProcessingException e) { + throw new UncheckedIOException(e); } } @@ -1882,24 +1951,21 @@ private Map> innerGetPermanentLimits(P try (ResultSet resultSet = preparedStmt.executeQuery()) { Map> map = new HashMap<>(); while (resultSet.next()) { - OwnerInfo owner = new OwnerInfo(); - PermanentLimitAttributes permanentLimit = new PermanentLimitAttributes(); - // In order, from the QueryCatalog.buildTemporaryLimitQuery SQL query : - // equipmentId, equipmentType, networkUuid, variantNum, side, limitType, name, value, acceptableDuration, fictitious owner.setEquipmentId(resultSet.getString(1)); owner.setEquipmentType(ResourceType.valueOf(resultSet.getString(2))); owner.setNetworkUuid(UUID.fromString(resultSet.getString(3))); owner.setVariantNum(resultSet.getInt(4)); - permanentLimit.setOperationalLimitsGroupId(resultSet.getString(5)); - permanentLimit.setSide(resultSet.getInt(6)); - permanentLimit.setLimitType(LimitType.valueOf(resultSet.getString(7))); - permanentLimit.setValue(resultSet.getDouble(8)); - - map.computeIfAbsent(owner, k -> new ArrayList<>()); - map.get(owner).add(permanentLimit); + String permanentLimitData = resultSet.getString(5); + List parsedTemporaryLimitData = mapper.readValue(permanentLimitData, new TypeReference<>() { }); + List permanentLimits = parsedTemporaryLimitData.stream().map(PermanentLimitSqlData::toPermanentLimitAttributes).toList(); + if (!permanentLimits.isEmpty()) { + map.put(owner, permanentLimits); + } } return map; + } catch (JsonProcessingException e) { + throw new UncheckedIOException(e); } } @@ -1924,27 +1990,24 @@ protected Map limitsInfos) { Map> temporaryLimits = limitsInfos.entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getTemporaryLimits())); + insertTemporaryLimitsAttributes(temporaryLimits); + } + + public void insertTemporaryLimitsAttributes(Map> temporaryLimits) { try (var connection = dataSource.getConnection()) { try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildInsertTemporaryLimitsQuery())) { - List values = new ArrayList<>(11); + List values = new ArrayList<>(5); List>> list = new ArrayList<>(temporaryLimits.entrySet()); for (List>> subUnit : Lists.partition(list, BATCH_SIZE)) { for (Map.Entry> entry : subUnit) { - for (TemporaryLimitAttributes temporaryLimit : entry.getValue()) { + if (!entry.getValue().isEmpty()) { values.clear(); - // In order, from the QueryCatalog.buildInsertTemporaryLimitsQuery SQL query : - // equipmentId, equipmentType, networkUuid, variantNum, operationalLimitsGroupId, side, limitType, name, value, acceptableDuration, fictitious values.add(entry.getKey().getEquipmentId()); values.add(entry.getKey().getEquipmentType().toString()); values.add(entry.getKey().getNetworkUuid()); values.add(entry.getKey().getVariantNum()); - values.add(temporaryLimit.getOperationalLimitsGroupId()); - values.add(temporaryLimit.getSide()); - values.add(temporaryLimit.getLimitType().toString()); - values.add(temporaryLimit.getName()); - values.add(temporaryLimit.getValue()); - values.add(temporaryLimit.getAcceptableDuration()); - values.add(temporaryLimit.isFictitious()); + values.add(entry.getValue().stream() + .map(TemporaryLimitSqlData::of).toList()); bindValues(preparedStmt, values, mapper); preparedStmt.addBatch(); } @@ -1960,27 +2023,25 @@ public void insertTemporaryLimits(Map limitsInfos) { public void insertPermanentLimits(Map limitsInfos) { Map> permanentLimits = limitsInfos.entrySet().stream() .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getPermanentLimits())); + insertPermanentLimitsAttributes(permanentLimits); + } + + public void insertPermanentLimitsAttributes(Map> permanentLimits) { try (var connection = dataSource.getConnection()) { try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildInsertPermanentLimitsQuery())) { List values = new ArrayList<>(8); List>> list = new ArrayList<>(permanentLimits.entrySet()); for (List>> subUnit : Lists.partition(list, BATCH_SIZE)) { for (Map.Entry> entry : subUnit) { - for (PermanentLimitAttributes permanentLimit : entry.getValue()) { - values.clear(); - // In order, from the QueryCatalog.buildInsertTemporaryLimitsQuery SQL query : - // equipmentId, equipmentType, networkUuid, variantNum, operationalLimitsGroupId, side, limitType, value - values.add(entry.getKey().getEquipmentId()); - values.add(entry.getKey().getEquipmentType().toString()); - values.add(entry.getKey().getNetworkUuid()); - values.add(entry.getKey().getVariantNum()); - values.add(permanentLimit.getOperationalLimitsGroupId()); - values.add(permanentLimit.getSide()); - values.add(permanentLimit.getLimitType().toString()); - values.add(permanentLimit.getValue()); - bindValues(preparedStmt, values, mapper); - preparedStmt.addBatch(); - } + values.clear(); + values.add(entry.getKey().getEquipmentId()); + values.add(entry.getKey().getEquipmentType().toString()); + values.add(entry.getKey().getNetworkUuid()); + values.add(entry.getKey().getVariantNum()); + values.add(entry.getValue().stream() + .map(PermanentLimitSqlData::of).toList()); + bindValues(preparedStmt, values, mapper); + preparedStmt.addBatch(); } preparedStmt.executeBatch(); } @@ -2038,6 +2099,9 @@ private void insertPermanentLimitInEquipment(T equipment private void deleteTemporaryLimits(UUID networkUuid, int variantNum, List equipmentIds) { try (var connection = dataSource.getConnection()) { + //TODO: To be removed when limits are fully migrated — should be after v2.13 deployment + deleteV211TemporaryLimits(connection, networkUuid, variantNum, equipmentIds); + try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildDeleteTemporaryLimitsVariantEquipmentINQuery(equipmentIds.size()))) { preparedStmt.setString(1, networkUuid.toString()); preparedStmt.setInt(2, variantNum); @@ -2053,6 +2117,9 @@ private void deleteTemporaryLimits(UUID networkUuid, int variantNum, List equipmentIds) { try (var connection = dataSource.getConnection()) { + //TODO: To be removed when limits are fully migrated — should be after v2.13 deployment + deleteV211PermanentLimits(connection, networkUuid, variantNum, equipmentIds); + try (var preparedStmt = connection.prepareStatement(QueryCatalog.buildDeletePermanentLimitsVariantEquipmentINQuery(equipmentIds.size()))) { preparedStmt.setString(1, networkUuid.toString()); preparedStmt.setInt(2, variantNum); diff --git a/network-store-server/src/main/java/com/powsybl/network/store/server/QueryCatalog.java b/network-store-server/src/main/java/com/powsybl/network/store/server/QueryCatalog.java index 74132768..e8d1b6fd 100644 --- a/network-store-server/src/main/java/com/powsybl/network/store/server/QueryCatalog.java +++ b/network-store-server/src/main/java/com/powsybl/network/store/server/QueryCatalog.java @@ -19,33 +19,36 @@ */ public final class QueryCatalog { - static final String MINIMAL_VALUE_REQUIREMENT_ERROR = "Function should not be called without at least one value."; + public static final String MINIMAL_VALUE_REQUIREMENT_ERROR = "Function should not be called without at least one value."; static final String VARIANT_ID_COLUMN = "variantId"; static final String UUID_COLUMN = "uuid"; - static final String NETWORK_UUID_COLUMN = "networkUuid"; - static final String VARIANT_NUM_COLUMN = "variantNum"; + public static final String NETWORK_UUID_COLUMN = "networkUuid"; + public static final String VARIANT_NUM_COLUMN = "variantNum"; static final String ID_COLUMN = "id"; static final String VOLTAGE_LEVEL_ID_COLUMN = "voltageLevelId"; static final String VOLTAGE_LEVEL_ID_1_COLUMN = "voltageLevelId1"; static final String VOLTAGE_LEVEL_ID_2_COLUMN = "voltageLevelId2"; static final String VOLTAGE_LEVEL_ID_3_COLUMN = "voltageLevelId3"; - static final String NAME_COLUMN = "name"; - static final String EQUIPMENT_TYPE_COLUMN = "equipmentType"; + public static final String NAME_COLUMN = "name"; + public static final String EQUIPMENT_TYPE_COLUMN = "equipmentType"; static final String REGULATING_EQUIPMENT_TYPE_COLUMN = "regulatingEquipmentType"; static final String REGULATED_EQUIPMENT_TYPE_COLUMN = "regulatedEquipmentType"; - static final String EQUIPMENT_ID_COLUMN = "equipmentId"; + public static final String EQUIPMENT_ID_COLUMN = "equipmentId"; static final String REGULATING_EQUIPMENT_ID = "regulatingEquipmentId"; static final String INDEX_COLUMN = "index"; static final String TAPCHANGER_TYPE_COLUMN = "tapChangerType"; static final String ALPHA_COLUMN = "alpha"; - static final String OPERATIONAL_LIMITS_GROUP_ID_COLUMN = "operationalLimitsGroupId"; - static final String SELECTED_OPERATIONAL_LIMITS_GROUP_ID_COLUMN = "selectedOperationalLimitsGroupId"; + public static final String V211_TEMPORARY_LIMIT_TABLE = "v211temporarylimit"; + public static final String V211_PERMANENT_LIMIT_TABLE = "v211permanentlimit"; + static final String TEMPORARY_LIMITS_TABLE = "temporarylimits"; + static final String TEMPORARY_LIMITS_COLUMN = "temporarylimits"; + static final String PERMANENT_LIMITS_TABLE = "permanentlimits"; + static final String PERMANENT_LIMITS_COLUMN = "permanentlimits"; static final String TAP_CHANGER_STEP_TABLE = "tapChangerStep"; static final String REGULATING_POINT_TABLE = "regulatingPoint"; static final String REGULATION_MODE = "regulationMode"; - static final String SIDE_COLUMN = "side"; - static final String LIMIT_TYPE_COLUMN = "limitType"; + public static final String SIDE_COLUMN = "side"; static final String REGULATING = "regulating"; private QueryCatalog() { @@ -285,11 +288,9 @@ public static String buildCloneNetworksQuery(Collection columns) { // Temporary Limits public static String buildCloneTemporaryLimitsQuery() { - return "insert into temporarylimit(" + EQUIPMENT_ID_COLUMN + ", " + EQUIPMENT_TYPE_COLUMN + ", " + - NETWORK_UUID_COLUMN + ", " + VARIANT_NUM_COLUMN + ", " + OPERATIONAL_LIMITS_GROUP_ID_COLUMN + ", " + SIDE_COLUMN + ", " + LIMIT_TYPE_COLUMN + ", " + NAME_COLUMN + - ", value_, acceptableDuration, fictitious) " + "select " + EQUIPMENT_ID_COLUMN + ", " + - EQUIPMENT_TYPE_COLUMN + ", ?, ?, " + OPERATIONAL_LIMITS_GROUP_ID_COLUMN + ", " + SIDE_COLUMN + ", " + LIMIT_TYPE_COLUMN + ", " + NAME_COLUMN + - ", value_, acceptableDuration, fictitious from temporarylimit where " + NETWORK_UUID_COLUMN + + return "insert into " + TEMPORARY_LIMITS_TABLE + "(" + EQUIPMENT_ID_COLUMN + ", " + EQUIPMENT_TYPE_COLUMN + ", " + + NETWORK_UUID_COLUMN + ", " + VARIANT_NUM_COLUMN + ", " + TEMPORARY_LIMITS_COLUMN + ") " + "select " + EQUIPMENT_ID_COLUMN + ", " + + EQUIPMENT_TYPE_COLUMN + ", ?, ?, " + TEMPORARY_LIMITS_COLUMN + " from " + TEMPORARY_LIMITS_TABLE + " where " + NETWORK_UUID_COLUMN + " = ? and " + VARIANT_NUM_COLUMN + " = ?"; } @@ -298,11 +299,8 @@ public static String buildTemporaryLimitQuery(String columnNameForWhereClause) { EQUIPMENT_TYPE_COLUMN + ", " + NETWORK_UUID_COLUMN + ", " + VARIANT_NUM_COLUMN + ", " + - OPERATIONAL_LIMITS_GROUP_ID_COLUMN + ", " + - SIDE_COLUMN + ", " + LIMIT_TYPE_COLUMN + ", " + - NAME_COLUMN + ", " + - "value_, acceptableDuration, fictitious " + - "from temporarylimit where " + + TEMPORARY_LIMITS_COLUMN + + " from " + TEMPORARY_LIMITS_TABLE + " where " + NETWORK_UUID_COLUMN + " = ? and " + VARIANT_NUM_COLUMN + " = ? and " + columnNameForWhereClause + " = ?"; @@ -316,11 +314,8 @@ public static String buildTemporaryLimitWithInClauseQuery(String columnNameForIn EQUIPMENT_TYPE_COLUMN + ", " + NETWORK_UUID_COLUMN + ", " + VARIANT_NUM_COLUMN + ", " + - OPERATIONAL_LIMITS_GROUP_ID_COLUMN + ", " + - SIDE_COLUMN + ", " + LIMIT_TYPE_COLUMN + ", " + - NAME_COLUMN + ", " + - "value_, acceptableDuration, fictitious " + - "from temporarylimit where " + + TEMPORARY_LIMITS_COLUMN + + " from " + TEMPORARY_LIMITS_TABLE + " where " + NETWORK_UUID_COLUMN + " = ? and " + VARIANT_NUM_COLUMN + " = ? and " + columnNameForInClause + " in (" + @@ -328,20 +323,18 @@ public static String buildTemporaryLimitWithInClauseQuery(String columnNameForIn } public static String buildInsertTemporaryLimitsQuery() { - return "insert into temporarylimit(" + + return "insert into " + TEMPORARY_LIMITS_TABLE + "(" + EQUIPMENT_ID_COLUMN + ", " + EQUIPMENT_TYPE_COLUMN + ", " + NETWORK_UUID_COLUMN + ", " + - VARIANT_NUM_COLUMN + ", " + - OPERATIONAL_LIMITS_GROUP_ID_COLUMN + ", " + SIDE_COLUMN + ", " + LIMIT_TYPE_COLUMN + ", " + - NAME_COLUMN + ", value_, acceptableDuration, fictitious)" + - " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + VARIANT_NUM_COLUMN + ", " + TEMPORARY_LIMITS_COLUMN + ") " + + " values (?, ?, ?, ?, ?)"; } public static String buildDeleteTemporaryLimitsVariantEquipmentINQuery(int numberOfValues) { if (numberOfValues < 1) { throw new IllegalArgumentException(MINIMAL_VALUE_REQUIREMENT_ERROR); } - return "delete from temporarylimit where " + + return "delete from " + TEMPORARY_LIMITS_TABLE + " where " + NETWORK_UUID_COLUMN + " = ? and " + VARIANT_NUM_COLUMN + " = ? and " + EQUIPMENT_ID_COLUMN + " in (" + @@ -349,21 +342,21 @@ public static String buildDeleteTemporaryLimitsVariantEquipmentINQuery(int numbe } public static String buildDeleteTemporaryLimitsVariantQuery() { - return "delete from temporarylimit where " + + return "delete from " + TEMPORARY_LIMITS_TABLE + " where " + NETWORK_UUID_COLUMN + " = ? and " + VARIANT_NUM_COLUMN + " = ?"; } public static String buildDeleteTemporaryLimitsQuery() { - return "delete from temporarylimit where " + + return "delete from " + TEMPORARY_LIMITS_TABLE + " where " + NETWORK_UUID_COLUMN + " = ?"; } - // permanent Limits + // Permanent Limits public static String buildClonePermanentLimitsQuery() { - return "insert into permanentlimit(" + EQUIPMENT_ID_COLUMN + ", " + EQUIPMENT_TYPE_COLUMN + ", " + - NETWORK_UUID_COLUMN + ", " + VARIANT_NUM_COLUMN + ", " + OPERATIONAL_LIMITS_GROUP_ID_COLUMN + ", " + SIDE_COLUMN + ", " + LIMIT_TYPE_COLUMN + ", value_) " + "select " + EQUIPMENT_ID_COLUMN + ", " + - EQUIPMENT_TYPE_COLUMN + ", ?, ?, " + OPERATIONAL_LIMITS_GROUP_ID_COLUMN + ", " + SIDE_COLUMN + ", " + LIMIT_TYPE_COLUMN + ", value_ from permanentlimit where " + NETWORK_UUID_COLUMN + + return "insert into " + PERMANENT_LIMITS_TABLE + "(" + EQUIPMENT_ID_COLUMN + ", " + EQUIPMENT_TYPE_COLUMN + ", " + + NETWORK_UUID_COLUMN + ", " + VARIANT_NUM_COLUMN + ", " + PERMANENT_LIMITS_COLUMN + ") " + "select " + EQUIPMENT_ID_COLUMN + ", " + + EQUIPMENT_TYPE_COLUMN + ", ?, ?, " + PERMANENT_LIMITS_COLUMN + " from " + PERMANENT_LIMITS_TABLE + " where " + NETWORK_UUID_COLUMN + " = ? and " + VARIANT_NUM_COLUMN + " = ?"; } @@ -372,10 +365,8 @@ public static String buildPermanentLimitQuery(String columnNameForWhereClause) { EQUIPMENT_TYPE_COLUMN + ", " + NETWORK_UUID_COLUMN + ", " + VARIANT_NUM_COLUMN + ", " + - OPERATIONAL_LIMITS_GROUP_ID_COLUMN + ", " + - SIDE_COLUMN + ", " + LIMIT_TYPE_COLUMN + ", " + - "value_ " + - "from permanentlimit where " + + PERMANENT_LIMITS_COLUMN + + " from " + PERMANENT_LIMITS_TABLE + " where " + NETWORK_UUID_COLUMN + " = ? and " + VARIANT_NUM_COLUMN + " = ? and " + columnNameForWhereClause + " = ?"; @@ -389,10 +380,8 @@ public static String buildPermanentLimitWithInClauseQuery(String columnNameForIn EQUIPMENT_TYPE_COLUMN + ", " + NETWORK_UUID_COLUMN + ", " + VARIANT_NUM_COLUMN + ", " + - OPERATIONAL_LIMITS_GROUP_ID_COLUMN + ", " + - SIDE_COLUMN + ", " + LIMIT_TYPE_COLUMN + ", " + - "value_ " + - "from permanentlimit where " + + PERMANENT_LIMITS_COLUMN + + " from " + PERMANENT_LIMITS_TABLE + " where " + NETWORK_UUID_COLUMN + " = ? and " + VARIANT_NUM_COLUMN + " = ? and " + columnNameForInClause + " in (" + @@ -400,19 +389,18 @@ public static String buildPermanentLimitWithInClauseQuery(String columnNameForIn } public static String buildInsertPermanentLimitsQuery() { - return "insert into permanentlimit(" + + return "insert into " + PERMANENT_LIMITS_TABLE + " (" + EQUIPMENT_ID_COLUMN + ", " + EQUIPMENT_TYPE_COLUMN + ", " + NETWORK_UUID_COLUMN + ", " + - VARIANT_NUM_COLUMN + ", " + - OPERATIONAL_LIMITS_GROUP_ID_COLUMN + ", " + SIDE_COLUMN + ", " + LIMIT_TYPE_COLUMN + ", value_)" + - " values (?, ?, ?, ?, ?, ?, ?, ?)"; + VARIANT_NUM_COLUMN + ", " + PERMANENT_LIMITS_COLUMN + ") " + + " values (?, ?, ?, ?, ?)"; } public static String buildDeletePermanentLimitsVariantEquipmentINQuery(int numberOfValues) { if (numberOfValues < 1) { throw new IllegalArgumentException(MINIMAL_VALUE_REQUIREMENT_ERROR); } - return "delete from permanentlimit where " + + return "delete from " + PERMANENT_LIMITS_TABLE + " where " + NETWORK_UUID_COLUMN + " = ? and " + VARIANT_NUM_COLUMN + " = ? and " + EQUIPMENT_ID_COLUMN + " in (" + @@ -420,13 +408,13 @@ public static String buildDeletePermanentLimitsVariantEquipmentINQuery(int numbe } public static String buildDeletePermanentLimitsVariantQuery() { - return "delete from permanentlimit where " + + return "delete from " + PERMANENT_LIMITS_TABLE + " where " + NETWORK_UUID_COLUMN + " = ? and " + VARIANT_NUM_COLUMN + " = ?"; } public static String buildDeletePermanentLimitsQuery() { - return "delete from permanentlimit where " + + return "delete from " + PERMANENT_LIMITS_TABLE + " where " + NETWORK_UUID_COLUMN + " = ?"; } diff --git a/network-store-server/src/main/java/com/powsybl/network/store/server/dto/LimitsInfos.java b/network-store-server/src/main/java/com/powsybl/network/store/server/dto/LimitsInfos.java index 019adecf..fce7a0ad 100644 --- a/network-store-server/src/main/java/com/powsybl/network/store/server/dto/LimitsInfos.java +++ b/network-store-server/src/main/java/com/powsybl/network/store/server/dto/LimitsInfos.java @@ -30,7 +30,7 @@ @Schema(description = "limits attributes") public class LimitsInfos { - @Schema(description = "List of permeant limits") + @Schema(description = "List of permanent limits") private List permanentLimits = new ArrayList<>(); @Schema(description = "List of temporary limits") diff --git a/network-store-server/src/main/java/com/powsybl/network/store/server/json/PermanentLimitSqlData.java b/network-store-server/src/main/java/com/powsybl/network/store/server/json/PermanentLimitSqlData.java new file mode 100644 index 00000000..25d24cb6 --- /dev/null +++ b/network-store-server/src/main/java/com/powsybl/network/store/server/json/PermanentLimitSqlData.java @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.network.store.server.json; + +import com.powsybl.iidm.network.LimitType; +import com.powsybl.network.store.server.dto.PermanentLimitAttributes; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +/** + * @author Etienne Lesot + */ +@ToString +@Builder +@AllArgsConstructor +@Getter +public class PermanentLimitSqlData { + + private String operationalLimitsGroupId; + private double value; + private Integer side; + private LimitType limitType; + + public PermanentLimitSqlData() { + // empty constructor for Jackson + } + + public static PermanentLimitSqlData of(PermanentLimitAttributes permanentLimitAttributes) { + return PermanentLimitSqlData.builder() + .operationalLimitsGroupId(permanentLimitAttributes.getOperationalLimitsGroupId()) + .value(permanentLimitAttributes.getValue()) + .side(permanentLimitAttributes.getSide()) + .limitType(permanentLimitAttributes.getLimitType()) + .build(); + } + + public PermanentLimitAttributes toPermanentLimitAttributes() { + return PermanentLimitAttributes.builder() + .operationalLimitsGroupId(operationalLimitsGroupId) + .value(value) + .side(side) + .limitType(limitType) + .build(); + } +} diff --git a/network-store-server/src/main/java/com/powsybl/network/store/server/json/TemporaryLimitSqlData.java b/network-store-server/src/main/java/com/powsybl/network/store/server/json/TemporaryLimitSqlData.java new file mode 100644 index 00000000..d9c9e687 --- /dev/null +++ b/network-store-server/src/main/java/com/powsybl/network/store/server/json/TemporaryLimitSqlData.java @@ -0,0 +1,59 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.network.store.server.json; + +import com.powsybl.iidm.network.LimitType; +import com.powsybl.network.store.model.TemporaryLimitAttributes; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.ToString; + +/** + * @author Etienne Lesot + */ +@ToString +@Builder +@AllArgsConstructor +@Getter +public class TemporaryLimitSqlData { + private String operationalLimitsGroupId; + private Integer side; + private LimitType limitType; + private String name; + private double value; + private Integer acceptableDuration; + private boolean fictitious; + + public TemporaryLimitSqlData() { + // empty constructor for Jackson + } + + public static TemporaryLimitSqlData of(TemporaryLimitAttributes temporaryLimit) { + return TemporaryLimitSqlData.builder() + .operationalLimitsGroupId(temporaryLimit.getOperationalLimitsGroupId()) + .side(temporaryLimit.getSide()) + .limitType(temporaryLimit.getLimitType()) + .name(temporaryLimit.getName()) + .value(temporaryLimit.getValue()) + .acceptableDuration(temporaryLimit.getAcceptableDuration()) + .fictitious(temporaryLimit.isFictitious()) + .build(); + } + + public TemporaryLimitAttributes toTemporaryLimitAttributes() { + return TemporaryLimitAttributes.builder() + .operationalLimitsGroupId(operationalLimitsGroupId) + .side(side) + .limitType(limitType) + .name(name) + .value(value) + .acceptableDuration(acceptableDuration) + .fictitious(fictitious) + .build(); + } +} diff --git a/network-store-server/src/main/java/com/powsybl/network/store/server/migration/MigrationController.java b/network-store-server/src/main/java/com/powsybl/network/store/server/migration/MigrationController.java new file mode 100644 index 00000000..ac423383 --- /dev/null +++ b/network-store-server/src/main/java/com/powsybl/network/store/server/migration/MigrationController.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.network.store.server.migration; + +import com.powsybl.network.store.model.*; +import com.powsybl.network.store.server.NetworkStoreRepository; +import com.powsybl.network.store.server.migration.v211limits.V211LimitsMigration; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.*; + +/** + * @author Etienne Homer + */ +@RestController +@RequestMapping(value = "/" + NetworkStoreApi.VERSION + "/migration") +@Tag(name = "Network store migration") +public class MigrationController { + + @Autowired + private NetworkStoreRepository repository; + + @PutMapping(value = "/v211limits/{networkId}/{variantNum}") + @Operation(summary = "Migrate limits of a network") + @ApiResponses(@ApiResponse(responseCode = "200", description = "Successfully migrated limits from V2.11.0 to new model")) + public ResponseEntity migrateV211Limits(@Parameter(description = "Network ID", required = true) @PathVariable("networkId") UUID networkId, + @Parameter(description = "Variant num", required = true) @PathVariable("variantNum") int variantNum) { + V211LimitsMigration.migrateV211Limits(repository, networkId, variantNum); + return ResponseEntity.ok().build(); + } +} diff --git a/network-store-server/src/main/java/com/powsybl/network/store/server/migration/v211limits/V211LimitsMigration.java b/network-store-server/src/main/java/com/powsybl/network/store/server/migration/v211limits/V211LimitsMigration.java new file mode 100644 index 00000000..01d5e4f6 --- /dev/null +++ b/network-store-server/src/main/java/com/powsybl/network/store/server/migration/v211limits/V211LimitsMigration.java @@ -0,0 +1,284 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.network.store.server.migration.v211limits; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.base.Stopwatch; +import com.powsybl.iidm.network.LimitType; +import com.powsybl.network.store.model.ResourceType; +import com.powsybl.network.store.model.TemporaryLimitAttributes; +import com.powsybl.network.store.server.ExtensionHandler; +import com.powsybl.network.store.server.Mappings; +import com.powsybl.network.store.server.NetworkStoreRepository; +import com.powsybl.network.store.server.dto.OwnerInfo; +import com.powsybl.network.store.server.dto.PermanentLimitAttributes; +import com.powsybl.network.store.server.exceptions.UncheckedSqlException; +import liquibase.change.custom.CustomTaskChange; +import liquibase.database.Database; +import liquibase.database.jvm.JdbcConnection; +import liquibase.exception.CustomChangeException; +import liquibase.exception.ValidationErrors; +import liquibase.resource.ResourceAccessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.datasource.SingleConnectionDataSource; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.*; +import java.util.concurrent.TimeUnit; + +import static com.powsybl.network.store.server.QueryCatalog.EQUIPMENT_TYPE_COLUMN; + +/** + * @author Etienne Homer + */ +public class V211LimitsMigration implements CustomTaskChange { + + private static final Logger LOGGER = LoggerFactory.getLogger(V211LimitsMigration.class); + + private NetworkStoreRepository repository; + + public void init(Database database) { + DataSource dataSource = new SingleConnectionDataSource(((JdbcConnection) database.getConnection()).getUnderlyingConnection(), true); + ObjectMapper mapper = new ObjectMapper(); + this.repository = new NetworkStoreRepository(dataSource, mapper, new Mappings(), new ExtensionHandler(dataSource, mapper)); + } + + @Override + public void execute(Database database) throws CustomChangeException { + init(database); + JdbcConnection connection = (JdbcConnection) database.getConnection(); + List exceptions = new ArrayList<>(); + try (PreparedStatement stmt = connection.prepareStatement("select uuid, variantnum from network ")) { + ResultSet variants = stmt.executeQuery(); + while (variants.next()) { + UUID networkId = UUID.fromString(variants.getString(1)); + int variantNum = variants.getInt(2); + migrateV211LimitsQuietly(repository, networkId, variantNum, exceptions); + } + } catch (Exception e) { + throw new CustomChangeException("V2.11 limits migration : error when getting the variants list", e); + } + if (!exceptions.isEmpty()) { + throw new CustomChangeException("V2.11 limits migration failed. " + exceptions.size() + " exceptions were thrown. First exception as cause : ", exceptions.get(0)); + } + } + + @Override + public String getConfirmationMessage() { + return "V2.11 limits were successfully migrated"; + } + + @Override + public void setUp() { + LOGGER.info("Set up migration for limits"); + } + + @Override + public void setFileOpener(ResourceAccessor resourceAccessor) { + LOGGER.info("Set file opener for limits migration"); + } + + @Override + public ValidationErrors validate(Database database) { + return new ValidationErrors(); + } + + public static Map> getV211PermanentLimitsWithInClause(NetworkStoreRepository repository, UUID networkUuid, int variantNum, String columnNameForWhereClause, List valuesForInClause) { + if (valuesForInClause.isEmpty()) { + return Collections.emptyMap(); + } + try (Connection connection = repository.getDataSource().getConnection()) { + var preparedStmt = connection.prepareStatement(V211LimitsQueryCatalog.buildGetV211PermanentLimitWithInClauseQuery(columnNameForWhereClause, valuesForInClause.size())); + preparedStmt.setString(1, networkUuid.toString()); + preparedStmt.setInt(2, variantNum); + for (int i = 0; i < valuesForInClause.size(); i++) { + preparedStmt.setString(3 + i, valuesForInClause.get(i)); + } + + return innerGetV211PermanentLimits(preparedStmt); + } catch (SQLException e) { + throw new UncheckedSqlException(e); + } + } + + public static Map> getV211TemporaryLimits(NetworkStoreRepository repository, UUID networkUuid, int variantNum, String columnNameForWhereClause, String valueForWhereClause) { + try (Connection connection = repository.getDataSource().getConnection()) { + var preparedStmt = connection.prepareStatement(V211LimitsQueryCatalog.buildGetV211TemporaryLimitQuery(columnNameForWhereClause)); + preparedStmt.setString(1, networkUuid.toString()); + preparedStmt.setInt(2, variantNum); + preparedStmt.setString(3, valueForWhereClause); + + return innerGetV211TemporaryLimits(preparedStmt); + } catch (SQLException e) { + throw new UncheckedSqlException(e); + } + } + + public static Map> getV211PermanentLimits(NetworkStoreRepository repository, UUID networkUuid, int variantNum, String columnNameForWhereClause, String valueForWhereClause) { + try (Connection connection = repository.getDataSource().getConnection()) { + var preparedStmt = connection.prepareStatement(V211LimitsQueryCatalog.buildGetV211PermanentLimitQuery(columnNameForWhereClause)); + preparedStmt.setString(1, networkUuid.toString()); + preparedStmt.setInt(2, variantNum); + preparedStmt.setString(3, valueForWhereClause); + + return innerGetV211PermanentLimits(preparedStmt); + } catch (SQLException e) { + throw new UncheckedSqlException(e); + } + } + + public static Map> getV211TemporaryLimitsWithInClause(NetworkStoreRepository repository, UUID networkUuid, int variantNum, String columnNameForWhereClause, List valuesForInClause) { + if (valuesForInClause.isEmpty()) { + return Collections.emptyMap(); + } + try (Connection connection = repository.getDataSource().getConnection()) { + var preparedStmt = connection.prepareStatement(V211LimitsQueryCatalog.buildGetV211TemporaryLimitWithInClauseQuery(columnNameForWhereClause, valuesForInClause.size())); + preparedStmt.setString(1, networkUuid.toString()); + preparedStmt.setInt(2, variantNum); + for (int i = 0; i < valuesForInClause.size(); i++) { + preparedStmt.setString(3 + i, valuesForInClause.get(i)); + } + + return innerGetV211TemporaryLimits(preparedStmt); + } catch (SQLException e) { + throw new UncheckedSqlException(e); + } + } + + public static void migrateV211LimitsQuietly(NetworkStoreRepository repository, UUID networkId, int variantNum, List exceptions) { + try { + migrateV211Limits(repository, networkId, variantNum); + } catch (Exception e) { + LOGGER.error("V2.11 limits migration : failure for network " + networkId + "/variantNum=" + variantNum, e); + exceptions.add(e); + } + } + + public static void migrateV211Limits(NetworkStoreRepository repository, UUID networkId, int variantNum) { + Stopwatch stopwatch = Stopwatch.createStarted(); + + migrateV211Limits(repository, networkId, variantNum, EQUIPMENT_TYPE_COLUMN, ResourceType.LINE.toString()); + migrateV211Limits(repository, networkId, variantNum, EQUIPMENT_TYPE_COLUMN, ResourceType.TWO_WINDINGS_TRANSFORMER.toString()); + migrateV211Limits(repository, networkId, variantNum, EQUIPMENT_TYPE_COLUMN, ResourceType.THREE_WINDINGS_TRANSFORMER.toString()); + migrateV211Limits(repository, networkId, variantNum, EQUIPMENT_TYPE_COLUMN, ResourceType.DANGLING_LINE.toString()); + + stopwatch.stop(); + LOGGER.info("The limits of network {}/variantNum={} were migrated in {} ms.", networkId, variantNum, stopwatch.elapsed(TimeUnit.MILLISECONDS)); + LOGGER.info("=============================================================================================================\n\n\n\n"); + } + + private static void insertNewLimitsAndDeleteV211(NetworkStoreRepository repository, UUID networkUuid, int variantNum, Map> v211TemporaryLimits, Map> v211PermanentLimits) { + try (Connection connection = repository.getDataSource().getConnection()) { + if (!v211PermanentLimits.keySet().isEmpty()) { + repository.insertPermanentLimitsAttributes(v211PermanentLimits); + deleteV211PermanentLimits(connection, networkUuid, variantNum, v211PermanentLimits.keySet().stream().map(OwnerInfo::getEquipmentId).toList()); + } + if (!v211TemporaryLimits.keySet().isEmpty()) { + repository.insertTemporaryLimitsAttributes(v211TemporaryLimits); + deleteV211TemporaryLimits(connection, networkUuid, variantNum, v211TemporaryLimits.keySet().stream().map(OwnerInfo::getEquipmentId).toList()); + } + } catch (SQLException e) { + throw new UncheckedSqlException(e); + } + } + + public static void migrateV211Limits(NetworkStoreRepository repository, UUID networkUuid, int variantNum, String columnNameForWhereClause, String valueForWhereClause) { + Stopwatch stopwatch = Stopwatch.createStarted(); + Map> v211TemporaryLimits = getV211TemporaryLimits(repository, networkUuid, variantNum, columnNameForWhereClause, valueForWhereClause); + Map> v211PermanentLimits = getV211PermanentLimits(repository, networkUuid, variantNum, columnNameForWhereClause, valueForWhereClause); + insertNewLimitsAndDeleteV211(repository, networkUuid, variantNum, v211TemporaryLimits, v211PermanentLimits); + stopwatch.stop(); + LOGGER.info("Limits of {}S of network {}/variantNum={} migrated in {} ms.", valueForWhereClause, networkUuid, variantNum, stopwatch.elapsed(TimeUnit.MILLISECONDS)); + LOGGER.info(" The permanent limits of {} {}S were migrated.", v211PermanentLimits.keySet().size(), valueForWhereClause); + LOGGER.info(" The temporary limits of {} {}S were migrated.\n", v211TemporaryLimits.keySet().size(), valueForWhereClause); + } + + public static Map> innerGetV211TemporaryLimits(PreparedStatement preparedStmt) throws SQLException { + try (ResultSet resultSet = preparedStmt.executeQuery()) { + Map> map = new HashMap<>(); + while (resultSet.next()) { + OwnerInfo owner = new OwnerInfo(); + TemporaryLimitAttributes temporaryLimit = new TemporaryLimitAttributes(); + // In order, from the QueryCatalog.buildTemporaryLimitQuery SQL query : + // equipmentId, equipmentType, networkUuid, variantNum, side, limitType, name, value, acceptableDuration, fictitious + owner.setEquipmentId(resultSet.getString(1)); + owner.setEquipmentType(ResourceType.valueOf(resultSet.getString(2))); + owner.setNetworkUuid(UUID.fromString(resultSet.getString(3))); + owner.setVariantNum(resultSet.getInt(4)); + temporaryLimit.setOperationalLimitsGroupId(resultSet.getString(5)); + temporaryLimit.setSide(resultSet.getInt(6)); + temporaryLimit.setLimitType(LimitType.valueOf(resultSet.getString(7))); + temporaryLimit.setName(resultSet.getString(8)); + temporaryLimit.setValue(resultSet.getDouble(9)); + temporaryLimit.setAcceptableDuration(resultSet.getInt(10)); + temporaryLimit.setFictitious(resultSet.getBoolean(11)); + map.computeIfAbsent(owner, k -> new ArrayList<>()); + map.get(owner).add(temporaryLimit); + } + return map; + } + } + + public static Map> innerGetV211PermanentLimits(PreparedStatement preparedStmt) throws SQLException { + try (ResultSet resultSet = preparedStmt.executeQuery()) { + Map> map = new HashMap<>(); + while (resultSet.next()) { + OwnerInfo owner = new OwnerInfo(); + PermanentLimitAttributes permanentLimit = new PermanentLimitAttributes(); + // In order, from the QueryCatalog.buildTemporaryLimitQuery SQL query : + // equipmentId, equipmentType, networkUuid, variantNum, side, limitType, name, value, acceptableDuration, fictitious + owner.setEquipmentId(resultSet.getString(1)); + owner.setEquipmentType(ResourceType.valueOf(resultSet.getString(2))); + owner.setNetworkUuid(UUID.fromString(resultSet.getString(3))); + owner.setVariantNum(resultSet.getInt(4)); + permanentLimit.setOperationalLimitsGroupId(resultSet.getString(5)); + permanentLimit.setSide(resultSet.getInt(6)); + permanentLimit.setLimitType(LimitType.valueOf(resultSet.getString(7))); + permanentLimit.setValue(resultSet.getDouble(8)); + + map.computeIfAbsent(owner, k -> new ArrayList<>()); + map.get(owner).add(permanentLimit); + } + return map; + } + } + + public static void deleteV211TemporaryLimits(Connection connection, UUID networkUuid, int variantNum, List equipmentIds) { + try { + try (var preparedStmt = connection.prepareStatement(V211LimitsQueryCatalog.buildDeleteV211TemporaryLimitsVariantEquipmentINQuery(equipmentIds.size()))) { + preparedStmt.setString(1, networkUuid.toString()); + preparedStmt.setInt(2, variantNum); + for (int i = 0; i < equipmentIds.size(); i++) { + preparedStmt.setString(3 + i, equipmentIds.get(i)); + } + preparedStmt.executeUpdate(); + } + } catch (SQLException e) { + throw new UncheckedSqlException(e); + } + } + + public static void deleteV211PermanentLimits(Connection connection, UUID networkUuid, int variantNum, List equipmentIds) { + try { + try (var preparedStmt = connection.prepareStatement(V211LimitsQueryCatalog.buildDeleteV211PermanentLimitsVariantEquipmentINQuery(equipmentIds.size()))) { + preparedStmt.setString(1, networkUuid.toString()); + preparedStmt.setInt(2, variantNum); + for (int i = 0; i < equipmentIds.size(); i++) { + preparedStmt.setString(3 + i, equipmentIds.get(i)); + } + preparedStmt.executeUpdate(); + } + } catch (SQLException e) { + throw new UncheckedSqlException(e); + } + } +} diff --git a/network-store-server/src/main/java/com/powsybl/network/store/server/migration/v211limits/V211LimitsQueryCatalog.java b/network-store-server/src/main/java/com/powsybl/network/store/server/migration/v211limits/V211LimitsQueryCatalog.java new file mode 100644 index 00000000..a9ef4e4e --- /dev/null +++ b/network-store-server/src/main/java/com/powsybl/network/store/server/migration/v211limits/V211LimitsQueryCatalog.java @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.network.store.server.migration.v211limits; + +import static com.powsybl.network.store.server.QueryCatalog.*; + +/** + * @author Etienne Homer + */ +public final class V211LimitsQueryCatalog { + public static final String MINIMAL_VALUE_REQUIREMENT_ERROR = "Function should not be called without at least one value."; + static final String LIMIT_TYPE_COLUMN = "limitType"; + + private V211LimitsQueryCatalog() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + // Temporary Limits + public static String buildGetV211TemporaryLimitWithInClauseQuery(String columnNameForInClause, int numberOfValues) { + if (numberOfValues < 1) { + throw new IllegalArgumentException(MINIMAL_VALUE_REQUIREMENT_ERROR); + } + return "select " + EQUIPMENT_ID_COLUMN + ", " + + EQUIPMENT_TYPE_COLUMN + ", " + + NETWORK_UUID_COLUMN + ", " + + VARIANT_NUM_COLUMN + ", " + + "operationallimitsgroupid, side, limittype, name, value_, acceptableduration, fictitious" + + " from " + V211_TEMPORARY_LIMIT_TABLE + " where " + + NETWORK_UUID_COLUMN + " = ? and " + + VARIANT_NUM_COLUMN + " = ? and " + + columnNameForInClause + " in (" + + "?, ".repeat(numberOfValues - 1) + "?)"; + } + + public static String buildDeleteV211TemporaryLimitsVariantEquipmentINQuery(int numberOfValues) { + if (numberOfValues < 1) { + throw new IllegalArgumentException(MINIMAL_VALUE_REQUIREMENT_ERROR); + } + return "delete from " + V211_TEMPORARY_LIMIT_TABLE + " where " + + NETWORK_UUID_COLUMN + " = ? and " + + VARIANT_NUM_COLUMN + " = ? and " + + EQUIPMENT_ID_COLUMN + " in (" + + "?, ".repeat(numberOfValues - 1) + "?)"; + } + + public static String buildGetV211TemporaryLimitQuery(String columnNameForWhereClause) { + return "select " + EQUIPMENT_ID_COLUMN + ", " + + EQUIPMENT_TYPE_COLUMN + ", " + + NETWORK_UUID_COLUMN + ", " + + VARIANT_NUM_COLUMN + ", operationallimitsgroupid, side, limittype, name, value_, acceptableduration, fictitious" + + " from " + V211_TEMPORARY_LIMIT_TABLE + " where " + + NETWORK_UUID_COLUMN + " = ? and " + + VARIANT_NUM_COLUMN + " = ? and " + + columnNameForWhereClause + " = ?"; + } + + public static String buildCloneV211TemporaryLimitsQuery() { + return "insert into " + V211_TEMPORARY_LIMIT_TABLE + "(" + EQUIPMENT_ID_COLUMN + ", " + EQUIPMENT_TYPE_COLUMN + ", " + + NETWORK_UUID_COLUMN + ", " + VARIANT_NUM_COLUMN + ", operationalLimitsGroupId, " + SIDE_COLUMN + ", " + LIMIT_TYPE_COLUMN + ", " + NAME_COLUMN + + ", value_, acceptableDuration, fictitious) " + "select " + EQUIPMENT_ID_COLUMN + ", " + + EQUIPMENT_TYPE_COLUMN + ", ?, ?, operationalLimitsGroupId, " + SIDE_COLUMN + ", " + LIMIT_TYPE_COLUMN + ", " + NAME_COLUMN + + ", value_, acceptableDuration, fictitious from " + V211_TEMPORARY_LIMIT_TABLE + " where " + NETWORK_UUID_COLUMN + + " = ? and " + VARIANT_NUM_COLUMN + " = ?"; + } + + public static String buildDeleteV211TemporaryLimitsQuery() { + return "delete from " + V211_TEMPORARY_LIMIT_TABLE + " where " + + NETWORK_UUID_COLUMN + " = ?"; + } + + public static String buildDeleteV211TemporaryLimitsVariantQuery() { + return "delete from " + V211_TEMPORARY_LIMIT_TABLE + " where " + + NETWORK_UUID_COLUMN + " = ? and " + + VARIANT_NUM_COLUMN + " = ?"; + } + + // Permanent Limits + public static String buildDeleteV211PermanentLimitsVariantEquipmentINQuery(int numberOfValues) { + if (numberOfValues < 1) { + throw new IllegalArgumentException(MINIMAL_VALUE_REQUIREMENT_ERROR); + } + return "delete from " + V211_PERMANENT_LIMIT_TABLE + " where " + + NETWORK_UUID_COLUMN + " = ? and " + + VARIANT_NUM_COLUMN + " = ? and " + + EQUIPMENT_ID_COLUMN + " in (" + + "?, ".repeat(numberOfValues - 1) + "?)"; + } + + public static String buildGetV211PermanentLimitWithInClauseQuery(String columnNameForInClause, int numberOfValues) { + if (numberOfValues < 1) { + throw new IllegalArgumentException(MINIMAL_VALUE_REQUIREMENT_ERROR); + } + return "select " + EQUIPMENT_ID_COLUMN + ", " + + EQUIPMENT_TYPE_COLUMN + ", " + + NETWORK_UUID_COLUMN + ", " + + VARIANT_NUM_COLUMN + ", operationallimitsgroupid, side, limittype, value_ " + + " from " + V211_PERMANENT_LIMIT_TABLE + " where " + + NETWORK_UUID_COLUMN + " = ? and " + + VARIANT_NUM_COLUMN + " = ? and " + + columnNameForInClause + " in (" + + "?, ".repeat(numberOfValues - 1) + "?)"; + } + + public static String buildGetV211PermanentLimitQuery(String columnNameForWhereClause) { + return "select " + EQUIPMENT_ID_COLUMN + ", " + + EQUIPMENT_TYPE_COLUMN + ", " + + NETWORK_UUID_COLUMN + ", " + + VARIANT_NUM_COLUMN + ", operationallimitsgroupid, side, limittype, value_" + + " from " + V211_PERMANENT_LIMIT_TABLE + " where " + + NETWORK_UUID_COLUMN + " = ? and " + + VARIANT_NUM_COLUMN + " = ? and " + + columnNameForWhereClause + " = ?"; + } + + public static String buildCloneV211PermanentLimitsQuery() { + return "insert into " + V211_PERMANENT_LIMIT_TABLE + "(" + EQUIPMENT_ID_COLUMN + ", " + EQUIPMENT_TYPE_COLUMN + ", " + + NETWORK_UUID_COLUMN + ", " + VARIANT_NUM_COLUMN + ", operationalLimitsGroupId, " + SIDE_COLUMN + ", " + LIMIT_TYPE_COLUMN + ", value_) " + "select " + EQUIPMENT_ID_COLUMN + ", " + + EQUIPMENT_TYPE_COLUMN + ", ?, ?, operationalLimitsGroupId, " + SIDE_COLUMN + ", " + LIMIT_TYPE_COLUMN + ", value_ from " + V211_PERMANENT_LIMIT_TABLE + " where " + NETWORK_UUID_COLUMN + + " = ? and " + VARIANT_NUM_COLUMN + " = ?"; + } + + public static String buildDeleteV211PermanentLimitsQuery() { + return "delete from " + V211_PERMANENT_LIMIT_TABLE + " where " + + NETWORK_UUID_COLUMN + " = ?"; + } + + public static String buildDeleteV211PermanentLimitsVariantQuery() { + return "delete from " + V211_PERMANENT_LIMIT_TABLE + " where " + + NETWORK_UUID_COLUMN + " = ? and " + + VARIANT_NUM_COLUMN + " = ?"; + } + +} diff --git a/network-store-server/src/main/resources/db/changelog/changesets/changelog_20241121T110000Z.xml b/network-store-server/src/main/resources/db/changelog/changesets/changelog_20241121T110000Z.xml new file mode 100644 index 00000000..5e759136 --- /dev/null +++ b/network-store-server/src/main/resources/db/changelog/changesets/changelog_20241121T110000Z.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/network-store-server/src/main/resources/db/changelog/changesets/changelog_20241223T130000Z.xml b/network-store-server/src/main/resources/db/changelog/changesets/changelog_20241223T130000Z.xml new file mode 100644 index 00000000..688f1614 --- /dev/null +++ b/network-store-server/src/main/resources/db/changelog/changesets/changelog_20241223T130000Z.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file diff --git a/network-store-server/src/main/resources/db/changelog/changesets/changelog_20250114T140000Z.xml b/network-store-server/src/main/resources/db/changelog/changesets/changelog_20250114T140000Z.xml new file mode 100644 index 00000000..659b684f --- /dev/null +++ b/network-store-server/src/main/resources/db/changelog/changesets/changelog_20250114T140000Z.xml @@ -0,0 +1,12 @@ + + + + + + + + + \ No newline at end of file diff --git a/network-store-server/src/main/resources/db/changelog/db.changelog-master.yaml b/network-store-server/src/main/resources/db/changelog/db.changelog-master.yaml index 5d007952..2d4a434f 100644 --- a/network-store-server/src/main/resources/db/changelog/db.changelog-master.yaml +++ b/network-store-server/src/main/resources/db/changelog/db.changelog-master.yaml @@ -91,3 +91,17 @@ databaseChangeLog: - include: file: changesets/changelog_20241204T110000Z.xml relativeToChangelogFile: true + + - include: + file: changesets/changelog_20241121T110000Z.xml + relativeToChangelogFile: true + +# Migration of V2.11 limits. To be added before tag v2.13.0 is deployed +# - include: +# file: changesets/changelog_20241223T130000Z.xml +# relativeToChangelogFile: true + +# Deletion of V2.11 limits tables. To be added before tag v2.14.0 is deployed +# - include: +# file: changesets/changelog_20250114T140000Z.xml +# relativeToChangelogFile: true diff --git a/network-store-server/src/test/java/com/powsybl/network/store/server/V211LimitsMigrationTest.java b/network-store-server/src/test/java/com/powsybl/network/store/server/V211LimitsMigrationTest.java new file mode 100644 index 00000000..4d8e3376 --- /dev/null +++ b/network-store-server/src/test/java/com/powsybl/network/store/server/V211LimitsMigrationTest.java @@ -0,0 +1,404 @@ +/** + * Copyright (c) 2024, RTE (http://www.rte-france.com) + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package com.powsybl.network.store.server; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.common.collect.Lists; +import com.powsybl.iidm.network.LimitType; +import com.powsybl.iidm.network.VariantManagerConstants; +import com.powsybl.network.store.model.*; +import com.powsybl.network.store.server.dto.LimitsInfos; +import com.powsybl.network.store.server.dto.OwnerInfo; +import com.powsybl.network.store.server.dto.PermanentLimitAttributes; +import com.powsybl.network.store.server.exceptions.UncheckedSqlException; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.ZonedDateTime; +import java.util.*; +import java.util.stream.Collectors; + +import static com.powsybl.network.store.model.NetworkStoreApi.VERSION; +import static com.powsybl.network.store.server.NetworkStoreRepository.BATCH_SIZE; +import static com.powsybl.network.store.server.QueryCatalog.*; +import static com.powsybl.network.store.server.Utils.bindValues; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; + +/** + * @author Etienne Homer + */ +@SpringBootTest +@AutoConfigureMockMvc +@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) +class V211LimitsMigrationTest { + + private static final UUID NETWORK_UUID = UUID.fromString("7928181c-7977-4592-ba19-88027e4254e4"); + + @Autowired + private NetworkStoreRepository networkStoreRepository; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private MockMvc mvc; + + @Test + void migrateV211LimitsTest() throws Exception { + createNetwork(); + createLine(); + createDanglineLine(); + create2WTLine(); + create3WTLine(); + // To simulate the state of a non migrated network, we first clean the limits created with the new code. + truncateTable(TEMPORARY_LIMITS_TABLE); + truncateTable(PERMANENT_LIMITS_TABLE); + + // Then we add the limits with the V2.11 model + LimitsInfos limits1 = createLimitSet(); + LimitsInfos limits2 = createLimitSet2(); + insertV211Limits("l1", ResourceType.LINE, limits1, limits2); + insertV211Limits("dl1", ResourceType.DANGLING_LINE, limits1, limits2); + insertV211Limits("2wt", ResourceType.TWO_WINDINGS_TRANSFORMER, limits1, limits2); + insertV211Limits("3wt", ResourceType.THREE_WINDINGS_TRANSFORMER, limits1, limits2); + + // Finally we migrate the network + mvc.perform(MockMvcRequestBuilders.put("/" + VERSION + "/migration/v211limits/" + NETWORK_UUID + "/0") + .contentType(APPLICATION_JSON)) + .andExpect(status().isOk()); + + assertEquals(0, countRowsByEquipmentId("l1", V211_TEMPORARY_LIMIT_TABLE)); + assertEquals(0, countRowsByEquipmentId("dl1", V211_TEMPORARY_LIMIT_TABLE)); + assertEquals(0, countRowsByEquipmentId("2wt", V211_TEMPORARY_LIMIT_TABLE)); + assertEquals(0, countRowsByEquipmentId("3wt", V211_TEMPORARY_LIMIT_TABLE)); + + assertEquals(0, countRowsByEquipmentId("l1", V211_PERMANENT_LIMIT_TABLE)); + assertEquals(0, countRowsByEquipmentId("dl1", V211_PERMANENT_LIMIT_TABLE)); + assertEquals(0, countRowsByEquipmentId("2wt", V211_PERMANENT_LIMIT_TABLE)); + assertEquals(0, countRowsByEquipmentId("3wt", V211_PERMANENT_LIMIT_TABLE)); + + mvc.perform(get("/" + VERSION + "/networks/" + NETWORK_UUID + "/" + Resource.INITIAL_VARIANT_NUM + "/lines") + .contentType(APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(APPLICATION_JSON)) + .andExpect(jsonPath("data[0].id").value("l1")) + .andExpect(jsonPath("data[0].attributes.operationalLimitsGroups1[\"group1\"].currentLimits.permanentLimit").value(300.)) + .andExpect(jsonPath("data[0].attributes.operationalLimitsGroups2[\"group1\"].currentLimits.permanentLimit").value(300.)) + .andExpect(jsonPath("data[0].attributes.operationalLimitsGroups1[\"group1\"].currentLimits.temporaryLimits.[\"100\"].acceptableDuration").value(100.)) + .andExpect(jsonPath("data[0].attributes.operationalLimitsGroups2[\"group1\"].currentLimits.temporaryLimits.[\"200\"].acceptableDuration").value(200)) + .andExpect(jsonPath("data[0].attributes.operationalLimitsGroups1[\"group2\"].currentLimits.permanentLimit").value(400)) + .andExpect(jsonPath("data[0].attributes.operationalLimitsGroups2[\"group2\"].currentLimits.permanentLimit").value(400)) + .andExpect(jsonPath("data[0].attributes.operationalLimitsGroups1[\"group2\"].currentLimits.temporaryLimits.[\"300\"].acceptableDuration").value(300)) + .andExpect(jsonPath("data[0].attributes.operationalLimitsGroups2[\"group2\"].currentLimits.temporaryLimits.[\"500\"].acceptableDuration").value(500)); + + assertEquals(1, countRowsByEquipmentId("l1", TEMPORARY_LIMITS_TABLE)); + assertEquals(1, countRowsByEquipmentId("dl1", TEMPORARY_LIMITS_TABLE)); + assertEquals(1, countRowsByEquipmentId("2wt", TEMPORARY_LIMITS_TABLE)); + assertEquals(1, countRowsByEquipmentId("3wt", TEMPORARY_LIMITS_TABLE)); + + assertEquals(1, countRowsByEquipmentId("l1", PERMANENT_LIMITS_TABLE)); + assertEquals(1, countRowsByEquipmentId("dl1", PERMANENT_LIMITS_TABLE)); + assertEquals(1, countRowsByEquipmentId("2wt", PERMANENT_LIMITS_TABLE)); + assertEquals(1, countRowsByEquipmentId("3wt", PERMANENT_LIMITS_TABLE)); + } + + private void truncateTable(String tableName) { + try (Connection connection = networkStoreRepository.getDataSource().getConnection()) { + try (var preparedStmt = connection.prepareStatement("delete from " + tableName)) { + preparedStmt.executeUpdate(); + } + } catch (SQLException e) { + throw new UncheckedSqlException(e); + } + } + + private LimitsInfos createLimitSet() { + LimitsInfos limits = new LimitsInfos(); + List temporaryLimits = new ArrayList<>(); + + TemporaryLimitAttributes templimitAOkSide1a = TemporaryLimitAttributes.builder() + .side(1) + .acceptableDuration(100) + .limitType(LimitType.CURRENT) + .operationalLimitsGroupId("group1") + .build(); + + TemporaryLimitAttributes templimitAOkSide2a = TemporaryLimitAttributes.builder() + .side(2) + .acceptableDuration(100) + .limitType(LimitType.CURRENT) + .operationalLimitsGroupId("group1") + .build(); + + TemporaryLimitAttributes templimitAOkSide2b = TemporaryLimitAttributes.builder() + .side(2) + .acceptableDuration(200) + .limitType(LimitType.CURRENT) + .operationalLimitsGroupId("group1") + .build(); + temporaryLimits.add(templimitAOkSide1a); + temporaryLimits.add(templimitAOkSide2a); + temporaryLimits.add(templimitAOkSide2b); + limits.setTemporaryLimits(temporaryLimits); + + PermanentLimitAttributes permanentLimitSide1 = PermanentLimitAttributes.builder() + .side(1) + .limitType(LimitType.CURRENT) + .operationalLimitsGroupId("group1") + .value(300) + .build(); + PermanentLimitAttributes permanentLimitSide2 = PermanentLimitAttributes.builder() + .side(2) + .limitType(LimitType.CURRENT) + .operationalLimitsGroupId("group1") + .value(300) + .build(); + limits.setPermanentLimits(List.of(permanentLimitSide1, permanentLimitSide2)); + + return limits; + } + + private LimitsInfos createLimitSet2() { + LimitsInfos limits = new LimitsInfos(); + List temporaryLimits = new ArrayList<>(); + + TemporaryLimitAttributes templimitAOkSide1a = TemporaryLimitAttributes.builder() + .side(1) + .acceptableDuration(300) + .limitType(LimitType.CURRENT) + .operationalLimitsGroupId("group2") + .build(); + + TemporaryLimitAttributes templimitAOkSide2a = TemporaryLimitAttributes.builder() + .side(2) + .acceptableDuration(400) + .limitType(LimitType.CURRENT) + .operationalLimitsGroupId("group2") + .build(); + + TemporaryLimitAttributes templimitAOkSide2b = TemporaryLimitAttributes.builder() + .side(2) + .acceptableDuration(500) + .limitType(LimitType.CURRENT) + .operationalLimitsGroupId("group2") + .build(); + temporaryLimits.add(templimitAOkSide1a); + temporaryLimits.add(templimitAOkSide2a); + temporaryLimits.add(templimitAOkSide2b); + limits.setTemporaryLimits(temporaryLimits); + + PermanentLimitAttributes permanentLimitSide1 = PermanentLimitAttributes.builder() + .side(1) + .limitType(LimitType.CURRENT) + .operationalLimitsGroupId("group2") + .value(400) + .build(); + PermanentLimitAttributes permanentLimitSide2 = PermanentLimitAttributes.builder() + .side(2) + .limitType(LimitType.CURRENT) + .operationalLimitsGroupId("group2") + .value(400) + .build(); + limits.setPermanentLimits(List.of(permanentLimitSide1, permanentLimitSide2)); + + return limits; + } + + private void insertV211Limits(String equipmentId, ResourceType resourceType, LimitsInfos limits1, LimitsInfos limits2) { + OwnerInfo ownerInfo = new OwnerInfo(equipmentId, resourceType, NETWORK_UUID, 0); + + insertV211PermanentLimits(Map.of(ownerInfo, limits1)); + insertV211TemporaryLimits(Map.of(ownerInfo, limits1)); + + insertV211PermanentLimits(Map.of(ownerInfo, limits2)); + insertV211TemporaryLimits(Map.of(ownerInfo, limits2)); + } + + public void createNetwork() throws Exception { + Resource n1 = Resource.networkBuilder() + .id("n1") + .variantNum(0) + .attributes(NetworkAttributes.builder() + .uuid(NETWORK_UUID) + .variantId(VariantManagerConstants.INITIAL_VARIANT_ID) + .caseDate(ZonedDateTime.parse("2015-01-01T00:00:00.000Z")) + .build()) + .build(); + + mvc.perform(post("/" + VERSION + "/networks") + .contentType(APPLICATION_JSON) + .content(objectMapper.writeValueAsString(Collections.singleton(n1)))) + .andExpect(status().isCreated()); + } + + public void createLine() throws Exception { + Resource resLine = Resource.lineBuilder() + .id("l1") + .attributes(LineAttributes.builder().build()) + .build(); + + mvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/lines") + .contentType(APPLICATION_JSON) + .content(objectMapper.writeValueAsString(Collections.singleton(resLine)))) + .andExpect(status().isCreated()); + } + + public void createDanglineLine() throws Exception { + Resource danglingLine = Resource.danglingLineBuilder() + .id("dl1") + .attributes(DanglingLineAttributes.builder().build()) + .build(); + + mvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/dangling-lines") + .contentType(APPLICATION_JSON) + .content(objectMapper.writeValueAsString(Collections.singleton(danglingLine)))) + .andExpect(status().isCreated()); + } + + public void create2WTLine() throws Exception { + Resource twoWindingsTransformer = Resource.twoWindingsTransformerBuilder() + .id("2wt") + .attributes(TwoWindingsTransformerAttributes.builder().build()) + .build(); + + mvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/2-windings-transformers") + .contentType(APPLICATION_JSON) + .content(objectMapper.writeValueAsString(Collections.singleton(twoWindingsTransformer)))) + .andExpect(status().isCreated()); + } + + public void create3WTLine() throws Exception { + Resource twoWindingsTransformer = Resource.threeWindingsTransformerBuilder() + .id("3wt") + .attributes(ThreeWindingsTransformerAttributes.builder() + .leg1(new LegAttributes()) + .leg2(new LegAttributes()) + .leg3(new LegAttributes()) + .build()) + .build(); + + mvc.perform(post("/" + VERSION + "/networks/" + NETWORK_UUID + "/3-windings-transformers") + .contentType(APPLICATION_JSON) + .content(objectMapper.writeValueAsString(Collections.singleton(twoWindingsTransformer)))) + .andExpect(status().isCreated()); + } + + public static String buildInsertV211TemporaryLimitsQuery() { + return "insert into " + V211_TEMPORARY_LIMIT_TABLE + "(" + + EQUIPMENT_ID_COLUMN + ", " + EQUIPMENT_TYPE_COLUMN + ", " + + NETWORK_UUID_COLUMN + ", " + + VARIANT_NUM_COLUMN + ", " + + "operationallimitsgroupid, " + SIDE_COLUMN + ", limittype, " + + NAME_COLUMN + ", value_, acceptableDuration, fictitious)" + + " values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + } + + public static String buildInsertV211PermanentLimitsQuery() { + return "insert into " + V211_PERMANENT_LIMIT_TABLE + "(" + + EQUIPMENT_ID_COLUMN + ", " + EQUIPMENT_TYPE_COLUMN + ", " + + NETWORK_UUID_COLUMN + ", " + + VARIANT_NUM_COLUMN + ", " + + "operationallimitsgroupid, " + SIDE_COLUMN + ", limittype, value_)" + + " values (?, ?, ?, ?, ?, ?, ?, ?)"; + } + + public int countRowsByEquipmentId(String equipmentId, String tableName) { + try (var connection = networkStoreRepository.getDataSource().getConnection()) { + try (var preparedStmt = connection.prepareStatement("select count(*) from " + tableName + " where " + EQUIPMENT_ID_COLUMN + " = ?")) { + preparedStmt.setString(1, equipmentId); + try (ResultSet resultSet = preparedStmt.executeQuery()) { + resultSet.next(); + return resultSet.getInt(1); + } + } + } catch (SQLException e) { + throw new UncheckedSqlException(e); + } + } + + public void insertV211PermanentLimits(Map limitsInfos) { + Map> permanentLimits = limitsInfos.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getPermanentLimits())); + try (var connection = networkStoreRepository.getDataSource().getConnection()) { + try (var preparedStmt = connection.prepareStatement(buildInsertV211PermanentLimitsQuery())) { + List values = new ArrayList<>(8); + List>> list = new ArrayList<>(permanentLimits.entrySet()); + for (List>> subUnit : Lists.partition(list, BATCH_SIZE)) { + for (Map.Entry> entry : subUnit) { + for (PermanentLimitAttributes permanentLimit : entry.getValue()) { + values.clear(); + // In order, from the QueryCatalog.buildInsertTemporaryLimitsQuery SQL query : + // equipmentId, equipmentType, networkUuid, variantNum, operationalLimitsGroupId, side, limitType, value + values.add(entry.getKey().getEquipmentId()); + values.add(entry.getKey().getEquipmentType().toString()); + values.add(entry.getKey().getNetworkUuid()); + values.add(entry.getKey().getVariantNum()); + values.add(permanentLimit.getOperationalLimitsGroupId()); + values.add(permanentLimit.getSide()); + values.add(permanentLimit.getLimitType().toString()); + values.add(permanentLimit.getValue()); + bindValues(preparedStmt, values, objectMapper); + preparedStmt.addBatch(); + } + } + preparedStmt.executeBatch(); + } + } + } catch (SQLException e) { + throw new UncheckedSqlException(e); + } + } + + public void insertV211TemporaryLimits(Map limitsInfos) { + Map> temporaryLimits = limitsInfos.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getTemporaryLimits())); + try (var connection = networkStoreRepository.getDataSource().getConnection()) { + try (var preparedStmt = connection.prepareStatement(buildInsertV211TemporaryLimitsQuery())) { + List values = new ArrayList<>(11); + List>> list = new ArrayList<>(temporaryLimits.entrySet()); + for (List>> subUnit : Lists.partition(list, BATCH_SIZE)) { + for (Map.Entry> entry : subUnit) { + for (TemporaryLimitAttributes temporaryLimit : entry.getValue()) { + values.clear(); + // In order, from the QueryCatalog.buildInsertTemporaryLimitsQuery SQL query : + // equipmentId, equipmentType, networkUuid, variantNum, operationalLimitsGroupId, side, limitType, name, value, acceptableDuration, fictitious + values.add(entry.getKey().getEquipmentId()); + values.add(entry.getKey().getEquipmentType().toString()); + values.add(entry.getKey().getNetworkUuid()); + values.add(entry.getKey().getVariantNum()); + values.add(temporaryLimit.getOperationalLimitsGroupId()); + values.add(temporaryLimit.getSide()); + values.add(temporaryLimit.getLimitType().toString()); + values.add(temporaryLimit.getName()); + values.add(temporaryLimit.getValue()); + values.add(temporaryLimit.getAcceptableDuration()); + values.add(temporaryLimit.isFictitious()); + bindValues(preparedStmt, values, objectMapper); + preparedStmt.addBatch(); + } + } + preparedStmt.executeBatch(); + } + } + } catch (SQLException e) { + throw new UncheckedSqlException(e); + } + } +} diff --git a/pom.xml b/pom.xml index 67419a98..d1d8c3bf 100644 --- a/pom.xml +++ b/pom.xml @@ -62,6 +62,7 @@ ../../network-store-integration-test/target/site/jacoco-aggregate/jacoco.xml, ../../../network-store-integration-test/target/site/jacoco-aggregate/jacoco.xml + **/migration/**/*