From cbbde2e71a2114afe298e9bdc30d6ea565403693 Mon Sep 17 00:00:00 2001 From: npedot <nicola.pedot@gmail.com> Date: Mon, 20 Jan 2020 13:20:44 +0100 Subject: [PATCH] adds changeset vsplit decomposition --- doc/vertical_specication.md | 44 ++++++++ .../domain/ChangeSet.kt | 2 + .../domain/CreateView.kt | 2 +- .../unibz.cs.semint.kprime/domain/Database.kt | 2 + .../unibz.cs.semint.kprime/domain/Schema.kt | 51 +++++++++ .../scenario/PersonHSplitScenario.kt | 3 +- .../scenario/PersonVSplitScenario.kt | 104 ++++++++++++++++++ .../usecase/ApplyChangeSetUseCase.kt | 11 ++ .../usecase/HSplitDetectUseCase.kt | 9 +- .../usecase/TransformerUseCase.kt | 8 ++ .../domain/ChangeSetTest.kt | 2 +- .../scenario/PersonVSplitScenarioTI.kt | 19 ++++ 12 files changed, 253 insertions(+), 4 deletions(-) create mode 100644 doc/vertical_specication.md create mode 100644 src/main/kotlin/unibz.cs.semint.kprime/usecase/ApplyChangeSetUseCase.kt create mode 100644 src/main/kotlin/unibz.cs.semint.kprime/usecase/TransformerUseCase.kt create mode 100644 src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenarioTI.kt diff --git a/doc/vertical_specication.md b/doc/vertical_specication.md new file mode 100644 index 0000000..b2df83a --- /dev/null +++ b/doc/vertical_specication.md @@ -0,0 +1,44 @@ +# vertical specification: + + <decompose> + <table tableName> + <cols> + <constraint fun dep> + <source><cols> + <target><cols> + <changeSet> + <xpath name="K">tableName key cols</xpath> + <xpath name="LHS">tableName source fun dep cols</xpath> + <xpath name="RHS">tableName target fun dep cols</xpath> + <xpath name="Rest">tableName other cols=*-K-LHS-RHS</xpath> + <createView tableName1>select K,LHS,Rest from tableName</createView> + <createView tableName2>select LHS,RHS from tableName</createView> + <constraint inclusion> + <source tableName1><col> + <target tableName2><col> + <constraint inclusion> + <source tableName2><col> + <target tableName1><col> + <changeSet> + + <compose> + <table tableName1> + <cols> + <table tableName2> + <cols> + <constraint inclusion> + <source tableName1><col> + <target tableName2><col> + <constraint inclusion> + <source tableName2><col> + <target tableName1><col> + <xpath name="K">key cols</xpath> + <xpath name="LHS">source fun dep cols</xpath> + <xpath name="RHS">target fun dep cols</xpath> + <xpath name="Rest">other cols</xpath> + <changeSet> + <createView tableName>select * from tableName1 join tableName2 on inclusion col </createView> + <constraint fun dep> + <source><cols> + <target><cols> + <changeSet> diff --git a/src/main/kotlin/unibz.cs.semint.kprime/domain/ChangeSet.kt b/src/main/kotlin/unibz.cs.semint.kprime/domain/ChangeSet.kt index efb86f5..64f69b8 100644 --- a/src/main/kotlin/unibz.cs.semint.kprime/domain/ChangeSet.kt +++ b/src/main/kotlin/unibz.cs.semint.kprime/domain/ChangeSet.kt @@ -11,4 +11,6 @@ class ChangeSet() { @JacksonXmlElementWrapper(useWrapping=false) var createView= ArrayList<CreateView>() + @JacksonXmlElementWrapper(useWrapping=false) + var createConstraint= ArrayList<Constraint>() } \ No newline at end of file diff --git a/src/main/kotlin/unibz.cs.semint.kprime/domain/CreateView.kt b/src/main/kotlin/unibz.cs.semint.kprime/domain/CreateView.kt index 16a0f0c..382339d 100644 --- a/src/main/kotlin/unibz.cs.semint.kprime/domain/CreateView.kt +++ b/src/main/kotlin/unibz.cs.semint.kprime/domain/CreateView.kt @@ -12,5 +12,5 @@ class CreateView() { @JacksonXmlProperty(isAttribute = true) var viewName: String = "" @JacksonXmlText - var value:String = "" + var text:String = "" } \ No newline at end of file diff --git a/src/main/kotlin/unibz.cs.semint.kprime/domain/Database.kt b/src/main/kotlin/unibz.cs.semint.kprime/domain/Database.kt index 5a802ca..e734387 100644 --- a/src/main/kotlin/unibz.cs.semint.kprime/domain/Database.kt +++ b/src/main/kotlin/unibz.cs.semint.kprime/domain/Database.kt @@ -6,6 +6,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement @JacksonXmlRootElement(localName = "database") open class Database () { + @JacksonXmlProperty(isAttribute = true) var name: String ="" @JacksonXmlProperty(isAttribute = true) @@ -19,4 +20,5 @@ open class Database () { // this.id=id // this.schema=schema // } + } diff --git a/src/main/kotlin/unibz.cs.semint.kprime/domain/Schema.kt b/src/main/kotlin/unibz.cs.semint.kprime/domain/Schema.kt index 5c9e213..cd47a55 100644 --- a/src/main/kotlin/unibz.cs.semint.kprime/domain/Schema.kt +++ b/src/main/kotlin/unibz.cs.semint.kprime/domain/Schema.kt @@ -16,4 +16,55 @@ class Schema () { fun table(name: String):Table { return tables.filter { t -> t.name==name }.first() } + + fun key(tableName: String): Set<Column> { + var resultCols = mutableSetOf<Column>() + val first = constraints.filter { c -> + c.type == Constraint.TYPE.PRIMARY_KEY.name && + c.name == "primaryKey.${tableName}" + }.toList() + if (first.isEmpty()) return mutableSetOf() + return first[0].source.columns.toSet() + } + + fun functionalLHS(tableName: String): Set<Column> { + var resultCols = mutableSetOf<Column>() + val first = constraints.filter { c -> + c.type == Constraint.TYPE.FUNCTIONAL.name && + c.name == "functional.${tableName}" + }.toList() + if (first.isEmpty()) return mutableSetOf() + return first[0].source.columns.toSet() + } + + fun functionalRHS(tableName: String): Set<Column> { + var resultCols = mutableSetOf<Column>() + val first = constraints.filter { c -> + c.type == Constraint.TYPE.FUNCTIONAL.name && + c.name == "functional.${tableName}" + }.toList() + if (first.isEmpty()) return mutableSetOf() + return first[0].target.columns.toSet() + } + + fun key(tableName:String,k:Set<Column>) { + val primaryConstraint = Constraint() + primaryConstraint.name="primaryKey.$tableName" + primaryConstraint.source.table="$tableName" + primaryConstraint.source.columns.addAll(k) + primaryConstraint.type=Constraint.TYPE.PRIMARY_KEY.name + constraints.add(primaryConstraint) + } + + fun functional(tableName:String, lhs:Set<Column>, rhs:Set<Column>){ + val functionalConstraint = Constraint() + functionalConstraint.name="functional.$tableName" + functionalConstraint.source.table="$tableName" + functionalConstraint.source.columns.addAll(lhs) + functionalConstraint.target.table="$tableName" + functionalConstraint.target.columns.addAll(rhs) + functionalConstraint.type= Constraint.TYPE.FUNCTIONAL.name + constraints.add(functionalConstraint) + + } } diff --git a/src/main/kotlin/unibz.cs.semint.kprime/scenario/PersonHSplitScenario.kt b/src/main/kotlin/unibz.cs.semint.kprime/scenario/PersonHSplitScenario.kt index 0592068..06e1687 100644 --- a/src/main/kotlin/unibz.cs.semint.kprime/scenario/PersonHSplitScenario.kt +++ b/src/main/kotlin/unibz.cs.semint.kprime/scenario/PersonHSplitScenario.kt @@ -10,7 +10,6 @@ class PersonHSplitScenario { fun run() { val personMetadata = buildPersonMetadata() hsplitPersonMetadata(personMetadata) - } private fun buildPersonMetadata(): Database { @@ -27,6 +26,7 @@ class PersonHSplitScenario { colS.nullable=true personTable.columns.add(colS) db.schema.tables.add(personTable) + val primaryConstraint = Constraint() primaryConstraint.name="primaryKey.person" primaryConstraint.source.table="person" @@ -34,6 +34,7 @@ class PersonHSplitScenario { primaryConstraint.source.columns.add(colT) primaryConstraint.type=Constraint.TYPE.PRIMARY_KEY.name db.schema.constraints.add(primaryConstraint) + return db } diff --git a/src/main/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenario.kt b/src/main/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenario.kt index 217d9bd..ad24c57 100644 --- a/src/main/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenario.kt +++ b/src/main/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenario.kt @@ -1,4 +1,108 @@ package unibz.cs.semint.kprime.scenario +import unibz.cs.semint.kprime.adapter.service.XMLSerializerJacksonAdapter +import unibz.cs.semint.kprime.domain.* +import unibz.cs.semint.kprime.usecase.XMLSerializeUseCase + class PersonVSplitScenario { + + fun run(): ChangeSet { + val personMetadata = buildPersonMetadata() + println(XMLSerializeUseCase(XMLSerializerJacksonAdapter()).prettyDatabase(personMetadata)) + return vsplitPersonMetadata(personMetadata) + } + + private fun buildPersonMetadata(): Database { + val db = Database() + val personTable = Table() + personTable.name= "person" + val colSSN = Column("SSN", "id.SSN", "dbname.SSN") + personTable.columns.add(colSSN) + colSSN.nullable=false + + val colT = Column("T", "id.T", "dbname.T") + personTable.columns.add(colT) + colT.nullable=false + + val colS = Column("S", "id.S", "dbname.S") + colS.nullable=true + personTable.columns.add(colS) + + val colX = Column("X", "id.X", "dbname.X") + colX.nullable=true + personTable.columns.add(colX) + + db.schema.key("person", mutableSetOf(colSSN)) + db.schema.functional("person", mutableSetOf(colT), mutableSetOf(colS)) + + db.schema.tables.add(personTable) + + return db + } + + private fun vsplitPersonMetadata(personMetadata: Database): ChangeSet { + // check for functional dep + // create changeset + var changeSet = ChangeSet() + + // compute K + val keyCols = personMetadata.schema.key("person") + var key = keyCols.map { x -> x.name }.toSet() + println("key $key") + // compute LHS + var lhsCols = personMetadata.schema.functionalLHS("person") + var lhs= lhsCols.map { x -> x.name }.toSet() + println("lhs $lhs") + if (lhs.isEmpty()) return changeSet + // compute RHS + val rhsCols = personMetadata.schema.functionalRHS("person") + var rhs = rhsCols.map { x -> x.name }.toSet() + println("rhs $rhs") + // compute Rest + val allCols = personMetadata.schema.table("person").columns.toSet() + val all = allCols.map { x -> x.name }.toSet() + var rest = all.minus(key).minus(lhs).minus(rhs) + val allNotKey = all.minus(key) + val allNotKeyCols = allCols.minus(keyCols) + + println("rest $rest") + + // create view1 = select K,LHS,Rest + var view1cols = "select "+key.plus(lhs).plus(rest).joinToString()+" from person" + val view1 = CreateView() + view1.viewName="tableName1" + view1.text=view1cols + changeSet.createView.add(view1) + // create view2 = select LHS,RHS + var view2cols = "select "+lhs.plus(rhs).joinToString()+" from person" + val view2 = CreateView() + view2.viewName="tableName2" + view2.text=view2cols + changeSet.createView.add(view2) + // create inclusion constraint tab1 tab2 + val inclusionTab1Tab2 = Constraint() + inclusionTab1Tab2.type=Constraint.TYPE.INCLUSION.name + inclusionTab1Tab2.source.table="tableName1" + inclusionTab1Tab2.source.columns.addAll(lhsCols) + inclusionTab1Tab2.target.table="tableName2" + inclusionTab1Tab2.target.columns.addAll(lhsCols) + changeSet.createConstraint.add(inclusionTab1Tab2) + // create inclusion constraint tab2 tab1 + val inclusionTab2Tab1 = Constraint() + inclusionTab2Tab1.type=Constraint.TYPE.INCLUSION.name + inclusionTab2Tab1.source.table="tableName2" + inclusionTab2Tab1.source.columns.addAll(lhsCols) + inclusionTab2Tab1.target.table="tableName1" + inclusionTab2Tab1.target.columns.addAll(lhsCols) + changeSet.createConstraint.add(inclusionTab2Tab1) + // create primary key on tab2 + val primaryTab2 = Constraint() + primaryTab2.type=Constraint.TYPE.PRIMARY_KEY.name + primaryTab2.source.table="tableName2" + primaryTab2.source.columns.addAll(lhsCols) + primaryTab2.target.table="tableName2" + primaryTab2.target.columns.addAll(rhsCols) + changeSet.createConstraint.add(primaryTab2) + return changeSet + } } \ No newline at end of file diff --git a/src/main/kotlin/unibz.cs.semint.kprime/usecase/ApplyChangeSetUseCase.kt b/src/main/kotlin/unibz.cs.semint.kprime/usecase/ApplyChangeSetUseCase.kt new file mode 100644 index 0000000..33d2f11 --- /dev/null +++ b/src/main/kotlin/unibz.cs.semint.kprime/usecase/ApplyChangeSetUseCase.kt @@ -0,0 +1,11 @@ +package unibz.cs.semint.kprime.usecase + +import unibz.cs.semint.kprime.domain.ChangeSet +import unibz.cs.semint.kprime.domain.Database + +class ApplyChangeSetUseCase { + fun apply(db: Database, changeset:ChangeSet):Database { + // TODO for every create* and remove* will do it on a cloned db + return Database() + } +} \ No newline at end of file diff --git a/src/main/kotlin/unibz.cs.semint.kprime/usecase/HSplitDetectUseCase.kt b/src/main/kotlin/unibz.cs.semint.kprime/usecase/HSplitDetectUseCase.kt index 31e93a6..eea22ef 100644 --- a/src/main/kotlin/unibz.cs.semint.kprime/usecase/HSplitDetectUseCase.kt +++ b/src/main/kotlin/unibz.cs.semint.kprime/usecase/HSplitDetectUseCase.kt @@ -1,3 +1,10 @@ package unibz.cs.semint.kprime.usecase -class HSplitDetectUseCase {} \ No newline at end of file +import unibz.cs.semint.kprime.domain.ChangeSet +import unibz.cs.semint.kprime.domain.Database + +class HSplitDetectUseCase: TransformerUseCase { + override fun compute(db: Database): ChangeSet { + TODO("not implemented") + } +} \ No newline at end of file diff --git a/src/main/kotlin/unibz.cs.semint.kprime/usecase/TransformerUseCase.kt b/src/main/kotlin/unibz.cs.semint.kprime/usecase/TransformerUseCase.kt new file mode 100644 index 0000000..b498358 --- /dev/null +++ b/src/main/kotlin/unibz.cs.semint.kprime/usecase/TransformerUseCase.kt @@ -0,0 +1,8 @@ +package unibz.cs.semint.kprime.usecase + +import unibz.cs.semint.kprime.domain.ChangeSet +import unibz.cs.semint.kprime.domain.Database + +interface TransformerUseCase { + fun compute(db: Database):ChangeSet +} \ No newline at end of file diff --git a/src/test/kotlin/unibz.cs.semint.kprime/domain/ChangeSetTest.kt b/src/test/kotlin/unibz.cs.semint.kprime/domain/ChangeSetTest.kt index 4b2206a..b0159e7 100644 --- a/src/test/kotlin/unibz.cs.semint.kprime/domain/ChangeSetTest.kt +++ b/src/test/kotlin/unibz.cs.semint.kprime/domain/ChangeSetTest.kt @@ -12,7 +12,7 @@ class ChangeSetTest { // given val changeSet = ChangeSet() val view = CreateView() - view.value="select * from table" + view.text="select * from table" changeSet.createView.add(view) changeSet.createView.add(view) // when diff --git a/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenarioTI.kt b/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenarioTI.kt new file mode 100644 index 0000000..27e3ea7 --- /dev/null +++ b/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenarioTI.kt @@ -0,0 +1,19 @@ +package unibz.cs.semint.kprime.scenario + +import org.junit.Test +import unibz.cs.semint.kprime.adapter.service.XMLSerializerJacksonAdapter +import unibz.cs.semint.kprime.usecase.XMLSerializeUseCase + +class PersonVSplitScenarioTI { + + @Test + fun test_person_vsplit_scenario() { + // given + val personVSplitScenario = PersonVSplitScenario() + // when + val changeSet = personVSplitScenario.run() + // then + // prints changeset + println(XMLSerializeUseCase(XMLSerializerJacksonAdapter()).prettyChangeSet(changeSet)) + } +} \ No newline at end of file -- GitLab