diff --git a/build.gradle.kts b/build.gradle.kts index fe87cfd..d0be00f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -10,6 +10,8 @@ repositories { } dependencies { + implementation("me.alllex.parsus:parsus-jvm:0.6.1") + testImplementation(kotlin("test")) } @@ -18,4 +20,4 @@ tasks.test { } kotlin { jvmToolchain(17) -} \ No newline at end of file +} diff --git a/src/main/kotlin/Main.kt b/src/main/kotlin/Main.kt index 1c1a641..4b0f7d0 100644 --- a/src/main/kotlin/Main.kt +++ b/src/main/kotlin/Main.kt @@ -1,5 +1,63 @@ package com.blzr -fun main() { - println("Hello World!") -} \ No newline at end of file +import me.alllex.parsus.parser.* +import java.io.File +import java.io.FileInputStream +import java.io.FileOutputStream +import kotlin.io.path.Path +import kotlin.io.path.createDirectories +import kotlin.io.path.createDirectory +import kotlin.io.path.createParentDirectories +import kotlin.io.path.deleteIfExists +import kotlin.io.path.listDirectoryEntries +import kotlin.system.exitProcess + +fun main(vararg args: String) { + val (input, output) = when (args.size) { + 1 -> args[0] to "${args[0].let { if (it.endsWith(".sql")) it.dropLast(4) else it }}/out/" + 2 -> args[0] to args[1] + else -> { + println("input.sql [out folder]") + exitProcess(0) + } + } + + val ins = FileInputStream(input) + + val text = ins.bufferedReader().readText() + + val nodes = text.split(Regex("^/$", RegexOption.MULTILINE)).filter { it.isNotBlank() } + println("We have ${nodes.size} nodes") + + val parser = OraDumpGrammar() + + val parsed: Map = nodes.associateWith { node -> + parser.parse(node).getOrElse { + println(node) + throw IllegalArgumentException(it.toString()) + } + } + + val outputPath = Path(output).createDirectories() + println("Create $outputPath") + + outputPath.listDirectoryEntries("*.sql").forEach { entry -> + println("Delete $entry") + entry.deleteIfExists() + } + + for ((text, type) in parsed) { + println("Writing ${type.fileName}") + val outputFile = File(output, type.fileName) + + if (outputFile.exists()) { + throw IllegalArgumentException("File already exists $outputFile") + } + + FileOutputStream(outputFile).bufferedWriter().use { + it.write(text.trim()) + } + } +} + + diff --git a/src/main/kotlin/Ora.kt b/src/main/kotlin/Ora.kt new file mode 100644 index 0000000..8ddd11e --- /dev/null +++ b/src/main/kotlin/Ora.kt @@ -0,0 +1,28 @@ +package com.blzr + +sealed class Ora { + data class CreateTable(override val name: String, override val fileName: String = "table ${name.lowercase()}.sql") : Ora() + data class AlterTable(override val name: String, override val fileName: String = "table ${name.lowercase()} alter.sql") : Ora() + data class AlterTableAddConstraint( + val table: String, + override val name: String, + override val fileName: String = "table ${table.lowercase()} constraint ${name.lowercase()}.sql" + ) : Ora() + + data class CreateView(override val name: String, override val fileName: String = "view ${name.lowercase()}.sql") : Ora() + data class CreateIndex(override val name: String, override val fileName: String = "index ${name.lowercase()}.sql") : Ora() + data class CreateSequence(override val name: String, override val fileName: String = "sequence ${name.lowercase()}.sql") : Ora() + data class CreateFunction(override val name: String, override val fileName: String = "function ${name.lowercase()}.sql") : Ora() + data class CreateProcedure(override val name: String, override val fileName: String = "procedure ${name.lowercase()}.sql") : Ora() + data class CreateTrigger(override val name: String, override val fileName: String = "trigger ${name.lowercase()}.sql") : Ora() + data class AlterTrigger(override val name: String, override val fileName: String = "trigger ${name.lowercase()} alter.sql") : + Ora() + + data class CreatePackage(override val name: String, override val fileName: String = "package ${name.lowercase()}.sql") : Ora() + data class CreatePackageBody(override val name: String, override val fileName: String = "package ${name.lowercase()} body.sql") : + Ora() + + + abstract val name: String + abstract val fileName: String +} diff --git a/src/main/kotlin/OraDumpGrammar.kt b/src/main/kotlin/OraDumpGrammar.kt new file mode 100644 index 0000000..ca20f86 --- /dev/null +++ b/src/main/kotlin/OraDumpGrammar.kt @@ -0,0 +1,107 @@ +package com.blzr + +import me.alllex.parsus.parser.Grammar +import me.alllex.parsus.parser.choose +import me.alllex.parsus.parser.map +import me.alllex.parsus.parser.maybe +import me.alllex.parsus.parser.parser +import me.alllex.parsus.parser.ref +import me.alllex.parsus.parser.times +import me.alllex.parsus.parser.unaryMinus +import me.alllex.parsus.token.literalToken +import me.alllex.parsus.token.regexToken + +class OraDumpGrammar() : Grammar(ignoreCase = true, debugMode = true) { + init { + regexToken("[\\s\\r\\n]+", ignored = true) + } + + val create = literalToken("create") + + val or = literalToken("or") + val replace = literalToken("replace") + val orReplace by or * replace + + val force by literalToken("force") + + val alter = literalToken("alter") + + val table = literalToken("table") + val view = literalToken("view") + + val add = literalToken("add") + val constraint = literalToken("constraint") + + val editionable = literalToken("editionable") + val function = literalToken("function") + val procedure = literalToken("procedure") + val trigger = literalToken("trigger") + val pkg = literalToken("package") + val body = literalToken("body") + + val unique = literalToken("unique") + val bitmap = literalToken("bitmap") + val index = literalToken("index") + + val sequence = literalToken("sequence") + + val quote = literalToken("\"") + val name = regexToken("\\w+") + val remaining = regexToken(Regex(".*", RegexOption.DOT_MATCHES_ALL)) + + val quoted by -quote * ref(::name) * -quote map { it.text } + + val createTable by -create * -maybe(orReplace) * -table * quoted * -remaining map { Ora.CreateTable(it) } + val alterTable by -alter * -table * quoted * -remaining map { Ora.AlterTable(it) } + val alterTableAddConstraint by -alter * -table * quoted * -add * -constraint * quoted * -remaining map { (a,b) -> + Ora.AlterTableAddConstraint(a, b) + } + + val createView by -create * -maybe(orReplace) * -maybe(force) * -maybe(editionable) * -view * quoted * -remaining map { + Ora.CreateView( + it + ) + } + + val createIndex by -create * -maybe(orReplace) * -maybe(unique) * -maybe(bitmap) * -index * quoted * -remaining map + { Ora.CreateIndex(it) } + val createSequence by -create * -maybe(orReplace) * -sequence * quoted * -remaining map + { Ora.CreateSequence(it) } + + val createFunction by -create * -maybe(orReplace) * -maybe(editionable) * -function * quoted * -remaining map + { Ora.CreateFunction(it) } + val createProcedure by -create * -maybe(orReplace) * -maybe(editionable) * -procedure * quoted * -remaining map + { Ora.CreateProcedure(it) } + val createTrigger by -create * -maybe(orReplace) * -maybe(editionable) * -trigger * quoted * -remaining map + { Ora.CreateTrigger(it) } + val alterTrigger by -alter * -trigger * quoted * -remaining map + { Ora.AlterTrigger(it) } + + val createPackage by -create * -maybe(orReplace) * -maybe(editionable) * -pkg * quoted * -remaining map { + Ora.CreatePackage( + it + ) + } + val createPackageBody by -create * -maybe(orReplace) * -maybe(editionable) * -pkg * -body * quoted * -remaining map { + Ora.CreatePackageBody( + it + ) + } + + override val root by parser { + choose( + createTable, + alterTableAddConstraint, + alterTable, + createView, + createIndex, + createSequence, + createFunction, + createProcedure, + createTrigger, + alterTrigger, + createPackage, + createPackageBody, + ) + } +}