Skip to content

Commit

Permalink
Create service which encodes the sql-command for creating a foreign-key
Browse files Browse the repository at this point in the history
  • Loading branch information
sdbuehlmann committed Feb 19, 2024
1 parent 8888c26 commit 1def889
Show file tree
Hide file tree
Showing 8 changed files with 341 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package ch.admin.bar.siard2.cmd.usecases.schemamapping;

import ch.admin.bar.siard2.cmd.SiardFromDb;
import ch.admin.bar.siard2.cmd.SiardToDb;
import ch.admin.bar.siard2.cmd.utils.SiardProjectExamples;
import ch.admin.bar.siard2.cmd.utils.siard.SiardArchivesHandler;
import ch.admin.bar.siard2.cmd.utils.siard.assertions.SiardArchiveAssertions;
import lombok.val;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.utility.DockerImageName;

import java.io.IOException;
import java.sql.SQLException;

public class PostgresDownloadMultiSchemaSiardProjectIT {

public final static String CREATE_MULTIPLE_SCHEMAS_SCRIPT = "usecases/schemamapping/create-multiple-schemas-with-multiple-tables_postgres.sql";

@Rule
public SiardArchivesHandler siardArchivesHandler = new SiardArchivesHandler();

@Rule
public PostgreSQLContainer<?> db = new PostgreSQLContainer<>(DockerImageName.parse("postgres:13"))
.withInitScript(CREATE_MULTIPLE_SCHEMAS_SCRIPT);

@Test
public void uploadAndDownload_expectNoExceptions() throws IOException, SQLException, ClassNotFoundException {
// given
val actualArchive = siardArchivesHandler.prepareEmpty();

// when
SiardFromDb siardFromDb = new SiardFromDb(new String[]{
"-o",
"-j:" + db.getJdbcUrl(),
"-u:" + db.getUsername(),
"-p:" + db.getPassword(),
"-s:" + actualArchive.getPathToArchiveFile()
});

// then
actualArchive.preserveArchive();
Assert.assertEquals(SiardFromDb.iRETURN_OK, siardFromDb.getReturn());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package ch.admin.bar.siard2.cmd.usecases.schemamapping;

import ch.admin.bar.siard2.cmd.SiardFromDb;
import ch.admin.bar.siard2.cmd.SiardToDb;
import ch.admin.bar.siard2.cmd.utils.siard.SiardArchivesHandler;
import ch.admin.bar.siard2.cmd.utils.siard.assertions.SiardArchiveAssertions;
import ch.admin.bar.siard2.cmd.utils.siard.model.utils.Id;
import ch.admin.bar.siard2.cmd.utils.siard.model.utils.QualifiedTableId;
import lombok.val;
import org.assertj.core.api.Assertions;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.utility.DockerImageName;

import java.io.IOException;
import java.sql.SQLException;

public class PostgresRenameSchemasUploadDownloadIT {

/**
* The file contains two schemes (Schema1 and Schema2) and each of it contains two tables
* (Schema1.Table1, Schema1.Table2, Schema2.Table3 and Schema2.Table4).
* </p>
* See usecases/schemamapping/create-multiple-schemas-with-multiple-tables_postgres.sql (Script which is used in
* {@link PostgresDownloadMultiSchemaSiardProjectIT} for generating the SIARD archive)
*/
public final static String SIARD_FILE = "usecases/schemamapping/multiple-schemas-with-multiple-tables.siard";

@Rule
public SiardArchivesHandler siardArchivesHandler = new SiardArchivesHandler();

@Rule
public PostgreSQLContainer<?> db = new PostgreSQLContainer<>(DockerImageName.parse("postgres:13"));

@Test
public void uploadAndDownload_expectNoExceptions() throws IOException, SQLException, ClassNotFoundException {
// given
val expectedArchive = siardArchivesHandler.prepareResource(SIARD_FILE);
val actualArchive = siardArchivesHandler.prepareEmpty();

// when
SiardToDb siardToDb = new SiardToDb(new String[]{
"-o",
"-j:" + db.getJdbcUrl(),
"-u:" + db.getUsername(),
"-p:" + db.getPassword(),
"-s:" + expectedArchive.getPathToArchiveFile(),
"schema1", "editedSchema1",
"schema2", "editedSchema2"
});
SiardFromDb siardFromDb = new SiardFromDb(new String[]{
"-o",
"-j:" + db.getJdbcUrl(),
"-u:" + db.getUsername(),
"-p:" + db.getPassword(),
"-s:" + actualArchive.getPathToArchiveFile()
});

// then
Assert.assertEquals(SiardToDb.iRETURN_OK, siardToDb.getReturn());
Assert.assertEquals(SiardFromDb.iRETURN_OK, siardFromDb.getReturn());

val metadataExplorer = actualArchive.exploreMetadata();

Assertions.assertThat(metadataExplorer.tryFindByTableId(QualifiedTableId.builder()
.schemaId(Id.of("editedSchema1"))
.tableId(Id.of("table1"))
.build()))
.isPresent();

Assertions.assertThat(metadataExplorer.tryFindByTableId(QualifiedTableId.builder()
.schemaId(Id.of("editedSchema1"))
.tableId(Id.of("table2"))
.build()))
.isPresent();

Assertions.assertThat(metadataExplorer.tryFindByTableId(QualifiedTableId.builder()
.schemaId(Id.of("editedSchema2"))
.tableId(Id.of("table3"))
.build()))
.isPresent();

Assertions.assertThat(metadataExplorer.tryFindByTableId(QualifiedTableId.builder()
.schemaId(Id.of("editedSchema2"))
.tableId(Id.of("table4"))
.build()))
.isPresent();

// TODO: Extend for types...
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
-- Create Schema1
CREATE SCHEMA Schema1;

-- Create tables in Schema1
CREATE TABLE Schema1.Table1
(
ID SERIAL PRIMARY KEY,
Name VARCHAR(50)
);

CREATE TABLE Schema1.Table2
(
ID SERIAL PRIMARY KEY,
Description TEXT
);

-- Insert data into Schema1.Table1
INSERT INTO Schema1.Table1 (Name)
VALUES ('John Doe'),
('Jane Smith'),
('Bob Johnson');

-- Insert data into Schema1.Table2
INSERT INTO Schema1.Table2 (Description)
VALUES ('Sample description 1'),
('Sample description 2'),
('Sample description 3');


-- Create Schema2
CREATE SCHEMA Schema2;

-- Create tables in Schema2
CREATE TABLE Schema2.Table3
(
ID SERIAL PRIMARY KEY,
Category VARCHAR(50)
);

CREATE TABLE Schema2.Table4
(
ID SERIAL PRIMARY KEY,
Quantity INT
);

-- Insert data into Schema2.Table3
INSERT INTO Schema2.Table3 (Category)
VALUES ('Category A'),
('Category B'),
('Category C');

-- Insert data into Schema2.Table4
INSERT INTO Schema2.Table4 (Quantity)
VALUES (10),
(20),
(30);

-- Additional comments
COMMENT ON TABLE Schema1.Table1 IS 'This is Table1 in Schema1.';
COMMENT ON COLUMN Schema1.Table1.ID IS 'Primary key for Table1.';

COMMENT ON TABLE Schema2.Table3 IS 'This is Table3 in Schema2.';
COMMENT ON COLUMN Schema2.Table3.ID IS 'Primary key for Table3.';
Binary file not shown.
4 changes: 2 additions & 2 deletions src/main/java/ch/admin/bar/siard2/cmd/ArchiveMapping.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
public class ArchiveMapping
{
private Map<String,SchemaMapping> _mapSchemas = new HashMap<String,SchemaMapping>();
SchemaMapping getSchemaMapping(String sSchemaName) { return _mapSchemas.get(sSchemaName); }
String getMappedSchemaName(String sSchemaName) { return getSchemaMapping(sSchemaName).getMappedSchemaName(); }
public SchemaMapping getSchemaMapping(String sSchemaName) { return _mapSchemas.get(sSchemaName); }
public String getMappedSchemaName(String sSchemaName) { return getSchemaMapping(sSchemaName).getMappedSchemaName(); }

private ArchiveMapping(boolean bSupportsArrays, boolean bSupportsUdts,
Map<String,String> mapSchemas, MetaData md,
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/ch/admin/bar/siard2/cmd/SchemaMapping.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
public class SchemaMapping extends Mapping
{
private String _sMappedSchemaName = null;
String getMappedSchemaName() { return _sMappedSchemaName; }
public String getMappedSchemaName() { return _sMappedSchemaName; }
public void setMappedSchemaName(String sMappedSchemaName) { _sMappedSchemaName = sMappedSchemaName; }
private Map<String,TableMapping> _mapTables = new HashMap<String, TableMapping>();
TableMapping getTableMapping(String sTableName) { return _mapTables.get(sTableName); }
public TableMapping getTableMapping(String sTableName) { return _mapTables.get(sTableName); }
String getMappedTableName(String sTableName) { return getTableMapping(sTableName).getMappedTableName(); }
private Map<String,TypeMapping> _mapTypes = new HashMap<String, TypeMapping>();
TypeMapping getTypeMapping(String sTypeName) { return _mapTypes.get(sTypeName); }
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/ch/admin/bar/siard2/cmd/TableMapping.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
public class TableMapping extends Mapping
{
private String _sMappedTableName = null;
String getMappedTableName() { return _sMappedTableName; }
public String getMappedTableName() { return _sMappedTableName; }
public void setMappedTableName(String sMappedTableName) { _sMappedTableName = sMappedTableName; }
private Map<String,String> _mapColumns = new HashMap<String,String>();
String getMappedColumnName(String sColumnName)
public String getMappedColumnName(String sColumnName)
{ return _mapColumns.get(sColumnName); }
private Map<String,String> _mapExtendedColumns = new HashMap<String,String>();
String getMappedExtendedColumnName(String sExtendedColumnName)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package ch.admin.bar.siard2.cmd.sql;

import ch.admin.bar.siard2.api.MetaForeignKey;
import ch.admin.bar.siard2.cmd.ArchiveMapping;
import ch.admin.bar.siard2.cmd.SchemaMapping;
import ch.admin.bar.siard2.cmd.TableMapping;
import ch.admin.bar.siard2.cmd.utils.ListAssembler;
import lombok.Builder;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import lombok.val;

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

@Slf4j
@RequiredArgsConstructor
public class TableDependantStatementGenerator {

private final String schemaName;
private final String tableName;

private final ArchiveMapping archiveMapping;

// String createForeignKeyQuery = "ALTER TABLE deine_tabelle "
// + "ADD CONSTRAINT fk_foreign_key_name "
// + "FOREIGN KEY (spalte_in_deiner_tabelle) "
// + "REFERENCES referenzierte_tabelle (referenzierte_spalte) "
// + "ON DELETE CASCADE "
// + "ON UPDATE CASCADE";
private String createConstraintStatement(final MetaForeignKey foreignKeyMetaData) {
if (foreignKeyMetaData.getReferences() == 0) {
return "";
}

val references = resolveReferences(foreignKeyMetaData);

val referencedTable = references.stream()
.map(foreignKeyReference -> String.format(
"%s.%s",
foreignKeyReference.getReferenced().getSchema(),
foreignKeyReference.getReferenced().getTable()))
.findFirst()
.orElseThrow(() -> new IllegalStateException("No references found"));

val sb = new StringBuilder("ADD CONSTRAINT ").append(foreignKeyMetaData.getName())
.append(" FOREIGN KEY(")
.append(references.stream()
.map(foreignKeyReference -> foreignKeyReference.getColumn().getColumn())
.collect(Collectors.joining(", ")))
.append(")")
.append("REFERENCES ")
.append(referencedTable)
.append(" (")
.append(references.stream()
.map(foreignKeyReference -> foreignKeyReference.getReferenced().getColumn())
.collect(Collectors.joining(", ")))
.append(")");

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

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

return sb.toString();
}

private List<ForeignKeyReference> resolveReferences(final MetaForeignKey foreignKeyMetaData) {
val referencedColumns = ListAssembler.assemble(
foreignKeyMetaData.getReferences(),
index -> {
val referenced = QualifiedColumnId.builder()
.schema(foreignKeyMetaData.getReferencedSchema())
.table(foreignKeyMetaData.getReferencedTable())
.column(foreignKeyMetaData.getReferenced(index))
.build();

val column = QualifiedColumnId.builder()
.schema(schemaName)
.table(tableName)
.column(foreignKeyMetaData.getColumn(index))
.build();

return ForeignKeyReference.builder()
.referenced(referenced)
.column(column)
.build();
});

return referencedColumns.stream()
.map(this::resolveMappings)
.collect(Collectors.toList());
}

private ForeignKeyReference resolveMappings(ForeignKeyReference origForeignKeyReference) {
return origForeignKeyReference.toBuilder()
.column(resolveMappings(origForeignKeyReference.getColumn()))
.referenced(resolveMappings(origForeignKeyReference.getReferenced()))
.build();
}

private QualifiedColumnId resolveMappings(QualifiedColumnId origQualifiedColumnId) {
final SchemaMapping sm = archiveMapping.getSchemaMapping(origQualifiedColumnId.getSchema()); // TODO FIXME can return null
final TableMapping tm = sm.getTableMapping(origQualifiedColumnId.getTable()); // TODO FIXME can return null

return origQualifiedColumnId.toBuilder()
.schema(sm.getMappedSchemaName())
.table(tm.getMappedTableName())
.column(tm.getMappedColumnName(origQualifiedColumnId.getColumn())) // TODO FIXME can return null
.build();
}

@Value
@Builder(toBuilder = true)
private static class ForeignKeyReference {
@NonNull QualifiedColumnId column;
@NonNull QualifiedColumnId referenced;
}

@Value
@Builder(toBuilder = true)
private static class QualifiedColumnId {
@NonNull String schema;
@NonNull String table;
@NonNull String column;
}
}

0 comments on commit 1def889

Please sign in to comment.