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