diff --git a/README.md b/README.md index 5f1868077a3db32059cb1c69422458ce5a314acd..b5ea283c3de2035029d5c34356ac18f8bf7db4d2 100644 --- a/README.md +++ b/README.md @@ -73,4 +73,11 @@ technology depenent packages * schema clone, builder, immutable * schema pattern matcher * schema variable extrator -* sql view generator \ No newline at end of file +* sql view generator + +## references + +https://www.baeldung.com/java-xpath +https://freemarker.apache.org/ +https://github.com/ostap/relations-java +https://github.com/JSQLParser/JSqlParser/wiki/Examples-of-SQL-parsing \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0221a528777743ff70d5aa1328aedca55e31ca2e..f4ea53e121fba194380d142b90b700edf937d96f 100644 --- a/pom.xml +++ b/pom.xml @@ -67,6 +67,12 @@ <artifactId>postgresql</artifactId> <version>42.2.8</version> </dependency> + <dependency> + <groupId>org.freemarker</groupId> + <artifactId>freemarker</artifactId> + <version>2.3.29</version> + </dependency> + <!-- test --> diff --git a/src/main/kotlin/unibz.cs.semint.kprime/domain/Constraint.kt b/src/main/kotlin/unibz.cs.semint.kprime/domain/Constraint.kt index 02efb4e9faadc11e08940ebaf29ed805093fa1dc..c136be458f83858ec9166c127a79d6dd1296b981 100644 --- a/src/main/kotlin/unibz.cs.semint.kprime/domain/Constraint.kt +++ b/src/main/kotlin/unibz.cs.semint.kprime/domain/Constraint.kt @@ -5,6 +5,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement @JacksonXmlRootElement(localName = "constraint") class Constraint () { + enum class TYPE { FOREIGN_KEY,PRIMARY_KEY,FUNCTIONAL,DOUBLE_INCLUSION,INCLUSION,DISJUNCTION,COVER } diff --git a/src/main/kotlin/unibz.cs.semint.kprime/usecase/HJoinUseCase.kt b/src/main/kotlin/unibz.cs.semint.kprime/usecase/HJoinUseCase.kt index 0cbb4201ed37893e7c339865a4595eedd0160de2..a17d9d34541e22f95e3f6767660ff7145cfd4a91 100644 --- a/src/main/kotlin/unibz.cs.semint.kprime/usecase/HJoinUseCase.kt +++ b/src/main/kotlin/unibz.cs.semint.kprime/usecase/HJoinUseCase.kt @@ -1,2 +1,5 @@ package unibz.cs.semint.kprime.usecase +class HJoinUseCase { + +} \ No newline at end of file diff --git a/src/main/kotlin/unibz.cs.semint.kprime/usecase/VSplitUseCase.kt b/src/main/kotlin/unibz.cs.semint.kprime/usecase/VSplitUseCase.kt index 9bdd18eebbe535f4cbe9d52e727f3ac28fd21230..cacf67c3396d2e7e5b0862135399aa91c2e33b2b 100644 --- a/src/main/kotlin/unibz.cs.semint.kprime/usecase/VSplitUseCase.kt +++ b/src/main/kotlin/unibz.cs.semint.kprime/usecase/VSplitUseCase.kt @@ -5,25 +5,33 @@ import unibz.cs.semint.kprime.domain.* class VSplitUseCase { fun compute(metadataDatabase: Database): ChangeSet { - // check for functional dep // create changeset var changeSet = ChangeSet() + // precondition: check for first table with functional dep, get the name + var tableWithFunctionalName= metadataDatabase.schema.constraints + .filter { c -> c.type==Constraint.TYPE.FUNCTIONAL.name } + .map { c -> c.source.table }.first() + if (tableWithFunctionalName.isEmpty()) return changeSet + // compute K - val keyCols = metadataDatabase.schema.key("person") + val keyCols = metadataDatabase.schema.key(tableWithFunctionalName) var key = keyCols.map { x -> x.name }.toSet() println("key $key") + // compute LHS - var lhsCols = metadataDatabase.schema.functionalLHS("person") + var lhsCols = metadataDatabase.schema.functionalLHS(tableWithFunctionalName) var lhs= lhsCols.map { x -> x.name }.toSet() println("lhs $lhs") if (lhs.isEmpty()) return changeSet + // compute RHS - val rhsCols = metadataDatabase.schema.functionalRHS("person") + val rhsCols = metadataDatabase.schema.functionalRHS(tableWithFunctionalName) var rhs = rhsCols.map { x -> x.name }.toSet() println("rhs $rhs") + // compute Rest - val allCols = metadataDatabase.schema.table("person").columns.toSet() + val allCols = metadataDatabase.schema.table(tableWithFunctionalName).columns.toSet() val all = allCols.map { x -> x.name }.toSet() var rest = all.minus(key).minus(lhs).minus(rhs) val allNotKey = all.minus(key) @@ -32,25 +40,26 @@ class VSplitUseCase { println("rest $rest") // create view1 = select K,LHS,Rest - var view1cols = "select "+key.plus(lhs).plus(rest).joinToString()+" from person" + var view1cols = "select "+key.plus(lhs).plus(rest).joinToString()+" from $tableWithFunctionalName" 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" + var view2cols = "select "+lhs.plus(rhs).joinToString()+" from $tableWithFunctionalName" 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 key constraint tab2 + val keyTab2 = Constraint() + keyTab2.type=Constraint.TYPE.PRIMARY_KEY.name + keyTab2.source.table="tableName2" + keyTab2.source.columns.addAll(lhsCols) + changeSet.createConstraint.add(keyTab2) + // create inclusion constraint tab2 tab1 val inclusionTab2Tab1 = Constraint() inclusionTab2Tab1.type=Constraint.TYPE.DOUBLE_INCLUSION.name diff --git a/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenarioTI.kt b/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenarioTI.kt index 86dd0e846f6b34d8285a97a465fe7e1816bc76a3..70911ea3409521497051d4997305ee9229b783a6 100644 --- a/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenarioTI.kt +++ b/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonVSplitScenarioTI.kt @@ -51,4 +51,10 @@ class PersonVSplitScenarioTI { // prints changeset println(XMLSerializeUseCase(XMLSerializerJacksonAdapter()).prettyChangeSet(changeSet)) } + + @Test + fun test_print_input_db() { + val db = buildPersonMetadata() + println(XMLSerializeUseCase(XMLSerializerJacksonAdapter()).prettyDatabase(db).ok) + } } \ No newline at end of file diff --git a/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonXPathScenarioTI.kt b/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonXPathScenarioTI.kt new file mode 100644 index 0000000000000000000000000000000000000000..ec92474965909217b550585ffd68ae5046cfe9c3 --- /dev/null +++ b/src/test/kotlin/unibz.cs.semint.kprime/scenario/PersonXPathScenarioTI.kt @@ -0,0 +1,39 @@ +package unibz.cs.semint.kprime.scenario + +import freemarker.cache.ClassTemplateLoader +import freemarker.template.Configuration +import freemarker.template.Template +import org.junit.Test +import java.io.OutputStreamWriter + +class PersonXPathScenarioTI { + + @Test + fun test_xpath_extraction_on_person_db() { + // given + // input person db + val personDbXml = PersonXPathScenarioTI::class.java.getResource("/db/person.xml").readText() + //println(personDbXml) + // input person out template + val personTemplate = PersonXPathScenarioTI::class.java.getResource("/db/person_out.template").readText() + //println(personTemplate) + // extract vars as value attributes via xpaths + val keys = listOf<String>("SSN") + val lhss = listOf<String>("T") + val rhss = listOf<String>("S") + val rests = listOf<String>("X") + // use vars in template + val templConfig = Configuration(Configuration.VERSION_2_3_29) + val classTemplLoader = ClassTemplateLoader(PersonXPathScenarioTI::javaClass.javaClass,"/") + templConfig.templateLoader= classTemplLoader + val templModel = mapOf<String,Any>( + "keys" to keys, + "lhss" to lhss, + "rhss" to rhss, + "rests" to rests) + val templ = //Template.getPlainTextTemplate("templ1",personTemplate,templConfig) + templConfig.getTemplate("db/person_out.template") + val out = OutputStreamWriter(System.out) + templ.process(templModel,out) + } +} \ No newline at end of file diff --git a/src/test/resources/db/person.xml b/src/test/resources/db/person.xml new file mode 100644 index 0000000000000000000000000000000000000000..40ddf5e55535d5ab076b5b3cea9bd3fae1c40c6a --- /dev/null +++ b/src/test/resources/db/person.xml @@ -0,0 +1,38 @@ +<database name="" id=""> + <schema name="" id=""> + <tables> + <tables name="person" id="" view="" condition=""> + <columns> + <columns name="SSN" id="id.SSN" dbname="dbname.SSN" nullable="false" dbtype=""/> + <columns name="T" id="id.T" dbname="dbname.T" nullable="false" dbtype=""/> + <columns name="S" id="id.S" dbname="dbname.S" nullable="true" dbtype=""/> + <columns name="X" id="id.X" dbname="dbname.X" nullable="true" dbtype=""/> + </columns> + </tables> + </tables> + <constraints> + <constraints name="primaryKey.person" id="" type="PRIMARY_KEY"> + <source name="" id="" table="person"> + <columns> + <columns name="SSN" id="id.SSN" dbname="dbname.SSN" nullable="false" dbtype=""/> + </columns> + </source> + <target name="" id="" table=""> + <columns/> + </target> + </constraints> + <constraints name="functional.person" id="" type="FUNCTIONAL"> + <source name="" id="" table="person"> + <columns> + <columns name="T" id="id.T" dbname="dbname.T" nullable="false" dbtype=""/> + </columns> + </source> + <target name="" id="" table="person"> + <columns> + <columns name="S" id="id.S" dbname="dbname.S" nullable="true" dbtype=""/> + </columns> + </target> + </constraints> + </constraints> + </schema> +</database> diff --git a/src/test/resources/db/person_out.template b/src/test/resources/db/person_out.template new file mode 100644 index 0000000000000000000000000000000000000000..7468553ab67e5816a7a66776831ef832c119014e --- /dev/null +++ b/src/test/resources/db/person_out.template @@ -0,0 +1,49 @@ +<database name="" id=""> + <schema name="" id=""> + <tables> + <tables name="person1" id="" view="" condition=""> + <columns> + <#list keys as key> + <columns name=${key} id="id.${key}" nullable="false" dbtype=""/> + </#list> + <#list lhss as lhs> + <columns name="${lhs}" id="id.${lhs}" nullable="false" dbtype=""/> + </#list> + <#list rests as rest> + <columns name="${rest}" id="id.${rest}" nullable="true" dbtype=""/> + </#list> + </columns> + </tables> + <tables name="person2" id="" view="" condition=""> + <columns> + <columns name="T" id="id.T" dbname="dbname.T" nullable="false" dbtype=""/> + <columns name="S" id="id.S" dbname="dbname.S" nullable="true" dbtype=""/> + </columns> + </tables> + </tables> + <constraints> + <constraints name="primaryKey.person" id="" type="PRIMARY_KEY"> + <source name="" id="" table="person"> + <columns> + <columns name="SSN" id="id.SSN" dbname="dbname.SSN" nullable="false" dbtype=""/> + </columns> + </source> + <target name="" id="" table=""> + <columns/> + </target> + </constraints> + <constraints name="functional.person" id="" type="FUNCTIONAL"> + <source name="" id="" table="person"> + <columns> + <columns name="T" id="id.T" dbname="dbname.T" nullable="false" dbtype=""/> + </columns> + </source> + <target name="" id="" table="person"> + <columns> + <columns name="S" id="id.S" dbname="dbname.S" nullable="true" dbtype=""/> + </columns> + </target> + </constraints> + </constraints> + </schema> +</database>