Skip to content

Commit

Permalink
Fix wrong SQL statement when creating foreign keys for MS SQL
Browse files Browse the repository at this point in the history
  • Loading branch information
sdbuehlmann committed Feb 22, 2024
1 parent 35f2e7f commit 991b62d
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public void uploadAndDownload_expectNoExceptions() throws IOException, SQLExcept
.expectedArchive(expectedArchive)
.actualArchive(actualArchive)
.assertionModifier(SiardArchiveAssertions.IGNORE_DBNAME) // FIXME ?
.assertionModifier(SiardArchiveAssertions.IGNORE_PRIMARY_KEY_NAME) // FIXME ?
.assertEqual();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public class ConnectorsRegistry {
public static final ConnectorId DB2 = ConnectorId.builder()
.name("DB/2")
.jdbcIdentifier("db2")
.connectorBuilder(DefaultConnector::new)
.connectorBuilder(Db2Connector::new)
.jdbcDriver(Db2Driver.class)
.build();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package ch.admin.bar.siard2.cmd.db.connector;

import ch.admin.bar.siard2.api.MetaTable;
import ch.admin.bar.siard2.api.generated.ReferentialActionType;
import ch.admin.bar.siard2.cmd.mapping.IdMapper;
import ch.admin.bar.siard2.cmd.model.QualifiedTableId;
import ch.admin.bar.siard2.cmd.sql.CreateForeignKeySqlGenerator;
import ch.admin.bar.siard2.cmd.sql.IdEncoder;
import ch.admin.bar.siard2.cmd.utils.ListAssembler;
import lombok.NonNull;
import lombok.val;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.time.Duration;

public class Db2Connector extends DefaultConnector {
public Db2Connector(ConnectorProperties properties, Connection connection) {
super(properties, connection);
}

@Override
public SqlExecutor createExecutor(IdMapper idMapper) {
return new Db2Executor(idMapper, connection, dbMetaData, properties.getQueryTimeout());
}

public static class Db2Executor extends DefaultSqlExecutor {

public Db2Executor(@NonNull IdMapper idMapper, @NonNull Connection connection, @NonNull DatabaseMetaData databaseMetaData, @NonNull Duration queryTimeout) {
super(idMapper, connection, databaseMetaData, queryTimeout);
}

@Override
public void addForeignKeys(MetaTable tableMetadata) throws SQLException {
if (tableMetadata.getMetaForeignKeys() > 0) {
val tableId = QualifiedTableId.builder()
.schema(tableMetadata.getParentMetaSchema().getName())
.table(tableMetadata.getName())
.build();

/*
[CONSTRAINT constraint_name]
FOREIGN KEY (fk1, fk2,...)
REFERENCES parent_table(c1,2,..)
ON UPDATE [ NO ACTION | RESTRICT]
ON DELETE [ NO ACTION | RESTRICT | CASCADE | SET NULL];
*/

val sqlGenerator = CreateForeignKeySqlGenerator.builder()
.tableId(QualifiedTableId.builder()
.schema(tableMetadata.getParentMetaSchema().getName())
.table(tableMetadata.getName())
.build())
.idEncoder(new IdEncoder())
.referentialActionsMapper(action -> {
// RESTRICT is unknown for MS SQL
// if (ReferentialActionType.fromValue(action).equals(ReferentialActionType.RESTRICT)) {
// return ReferentialActionType.NO_ACTION.value();
// }
return action;
})
.idMapper(idMapper)
.build();

val foreignKeysMetaData = ListAssembler.assemble(
tableMetadata.getMetaForeignKeys(),
tableMetadata::getMetaForeignKey);

for (val foreignKeyMetaData : foreignKeysMetaData) {
val sql = sqlGenerator.create(tableId, foreignKeyMetaData);

executeSql(sql);
}
}
}

private String getReferentialAction(int iReferentialAction) {
ReferentialActionType rat = null;
switch (iReferentialAction) {
case DatabaseMetaData.importedKeyCascade:
rat = ReferentialActionType.CASCADE;
break;
case DatabaseMetaData.importedKeySetNull:
rat = ReferentialActionType.SET_NULL;
break;
case DatabaseMetaData.importedKeySetDefault:
rat = ReferentialActionType.SET_DEFAULT;
break;
case DatabaseMetaData.importedKeyRestrict:
rat = ReferentialActionType.RESTRICT;
break;
case DatabaseMetaData.importedKeyNoAction:
rat = ReferentialActionType.NO_ACTION;
}
return rat.value();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@

public class DefaultConnector implements Connector {

private final ConnectorProperties properties;
protected final ConnectorProperties properties;

@Getter
private final Connection connection;
protected final Connection connection;

private final DatabaseMetaData dbMetaData;
protected final DatabaseMetaData dbMetaData;

@Getter
private final DbFeatures dbFeatures;
protected final DbFeatures dbFeatures;

@SneakyThrows // TODO
public DefaultConnector(final ConnectorProperties properties, final Connection connection) {
Expand Down Expand Up @@ -62,17 +62,17 @@ public void close() {
public static class DefaultSqlExecutor implements SqlExecutor {

@NonNull
private final IdMapper idMapper;
protected final IdMapper idMapper;

@NonNull
private final Connection connection;
protected final Connection connection;

@Getter
@NonNull
private final DatabaseMetaData databaseMetaData;
protected final DatabaseMetaData databaseMetaData;

@NonNull
private final Duration queryTimeout;
protected final Duration queryTimeout;

@Override
public void addForeignKeys(MetaTable tableMetadata) throws SQLException {
Expand All @@ -83,6 +83,7 @@ public void addForeignKeys(MetaTable tableMetadata) throws SQLException {
.table(tableMetadata.getName())
.build())
.idEncoder(new IdEncoder())
.referentialActionsMapper(action -> action)
.idMapper(idMapper)
.build();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
package ch.admin.bar.siard2.cmd.db.connector;

import ch.admin.bar.siard2.api.MetaTable;
import ch.admin.bar.siard2.api.generated.ReferentialActionType;
import ch.admin.bar.siard2.cmd.mapping.IdMapper;
import ch.admin.bar.siard2.cmd.model.QualifiedTableId;
import ch.admin.bar.siard2.cmd.sql.CreateForeignKeySqlGenerator;
import ch.admin.bar.siard2.cmd.sql.IdEncoder;
import ch.admin.bar.siard2.cmd.utils.ListAssembler;
import lombok.Builder;
import lombok.NonNull;
import lombok.val;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
Expand All @@ -10,19 +18,77 @@


public class MsSqlConnector extends DefaultConnector {

public MsSqlConnector(ConnectorProperties properties, Connection connection) {
super(properties, connection);
}

@Override
public SqlExecutor createExecutor(IdMapper idMapper) {
return super.createExecutor(idMapper);
return new MsSqlExecutor(idMapper, connection, dbMetaData, properties.getQueryTimeout());
}

public static class MsSqlExecutor extends DefaultSqlExecutor {

public MsSqlExecutor(@NonNull IdMapper idMapper, @NonNull Connection connection, @NonNull DatabaseMetaData dbMetaData, @NonNull Duration queryTimeout) {
super(idMapper, connection, dbMetaData, queryTimeout);
public MsSqlExecutor(@NonNull IdMapper idMapper, @NonNull Connection connection, @NonNull DatabaseMetaData databaseMetaData, @NonNull Duration queryTimeout) {
super(idMapper, connection, databaseMetaData, queryTimeout);
}

@Override
public void addForeignKeys(MetaTable tableMetadata) throws SQLException {
if (tableMetadata.getMetaForeignKeys() > 0) {
val tableId = QualifiedTableId.builder()
.schema(tableMetadata.getParentMetaSchema().getName())
.table(tableMetadata.getName())
.build();

val sqlGenerator = CreateForeignKeySqlGenerator.builder()
.tableId(QualifiedTableId.builder()
.schema(tableMetadata.getParentMetaSchema().getName())
.table(tableMetadata.getName())
.build())
.idEncoder(new IdEncoder())
.referentialActionsMapper(action -> {
// RESTRICT is unknown for MS SQL
// if (ReferentialActionType.fromValue(action).equals(ReferentialActionType.RESTRICT)) {
// return ReferentialActionType.NO_ACTION.value();
// }
return action;
})
.idMapper(idMapper)
.build();

val foreignKeysMetaData = ListAssembler.assemble(
tableMetadata.getMetaForeignKeys(),
tableMetadata::getMetaForeignKey);

for (val foreignKeyMetaData : foreignKeysMetaData) {
val sql = sqlGenerator.create(tableId, foreignKeyMetaData);

executeSql(sql);
}
}
}

private String getReferentialAction(int iReferentialAction) {
ReferentialActionType rat = null;
switch (iReferentialAction) {
case DatabaseMetaData.importedKeyCascade:
rat = ReferentialActionType.CASCADE;
break;
case DatabaseMetaData.importedKeySetNull:
rat = ReferentialActionType.SET_NULL;
break;
case DatabaseMetaData.importedKeySetDefault:
rat = ReferentialActionType.SET_DEFAULT;
break;
case DatabaseMetaData.importedKeyRestrict:
rat = ReferentialActionType.RESTRICT;
break;
case DatabaseMetaData.importedKeyNoAction:
rat = ReferentialActionType.NO_ACTION;
}
return rat.value();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
Expand Down Expand Up @@ -46,6 +47,9 @@ public class CreateForeignKeySqlGenerator {
@NonNull
private final IdMapper idMapper;

@NonNull
private final Function<String, String> referentialActionsMapper;

/**
* The identifier encoder for encoding keys.
*/
Expand Down Expand Up @@ -75,20 +79,34 @@ public String create(final List<MetaForeignKey> foreignKeyMetaData) {

val mappedTableId = idMapper.map(tableId);

val sb = new StringBuilder()
val stringBuilder = new StringBuilder()
.append("ALTER TABLE ")
.append(idEncoder.encodeKeySensitive(mappedTableId));

val addConstraintStatements = foreignKeyMetaData.stream()
.map(this::addConstraintStatement)
.collect(Collectors.joining(", "));

sb.append(" ")
stringBuilder.append(" ")
.append(addConstraintStatements);

log.info("SQL statement for creating foreign-keys: {}", sb);
log.info("SQL statement for creating foreign-keys: {}", stringBuilder);

return stringBuilder.toString();
}

public String create(final QualifiedTableId tableId, final MetaForeignKey foreignKeyMetaData) {
val mappedTableId = idMapper.map(tableId);

val stringBuilder = new StringBuilder()
.append("ALTER TABLE ")
.append(idEncoder.encodeKeySensitive(mappedTableId))
.append(" ")
.append(addConstraintStatement(foreignKeyMetaData));

log.info("SQL statement for creating foreign-keys: {}", stringBuilder);

return sb.toString();
return stringBuilder.toString();
}

private String addConstraintStatement(final MetaForeignKey foreignKeyMetaData) {
Expand All @@ -104,7 +122,7 @@ private String addConstraintStatement(final MetaForeignKey foreignKeyMetaData) {
.findFirst()
.orElseThrow(() -> new IllegalStateException("No references found"));

val sb = new StringBuilder()
val stringBuilder = new StringBuilder()
.append("ADD CONSTRAINT ")
.append(foreignKeyMetaData.getName())
.append(" FOREIGN KEY (")
Expand All @@ -122,12 +140,14 @@ private String addConstraintStatement(final MetaForeignKey foreignKeyMetaData) {

// actions
Optional.ofNullable(foreignKeyMetaData.getDeleteAction())
.ifPresent(action -> sb.append(" ON DELETE ").append(action));
.ifPresent(action -> stringBuilder.append(" ON DELETE ")
.append(referentialActionsMapper.apply(action)));

Optional.ofNullable(foreignKeyMetaData.getUpdateAction())
.ifPresent(action -> sb.append(" ON UPDATE ").append(action));
.ifPresent(action -> stringBuilder.append(" ON UPDATE ")
.append(referentialActionsMapper.apply(action)));

return sb.toString();
return stringBuilder.toString();
}

private List<ForeignKeyReference> resolveReferences(final MetaForeignKey foreignKeyMetaData) {
Expand Down

0 comments on commit 991b62d

Please sign in to comment.