com.zeoflow.depot.parser.expansion.ExpandableParsedQuery.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of depot-compiler Show documentation
Show all versions of depot-compiler Show documentation
The Depot persistence library provides an abstraction layer over SQLite to allow for more robust database access while using the full power of SQLite.
The newest version!
/*
* Copyright (C) 2021 ZeoFlow SRL
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zeoflow.depot.parser.expansion
import com.zeoflow.depot.parser.ParserErrors
import com.zeoflow.depot.parser.QueryType
import com.zeoflow.depot.parser.Table
import com.zeoflow.depot.verifier.QueryResultInfo
/**
* ExpandableSqlParser parses the query in more detail such that we know when an ALL projection is
* used (select *). This is information is only ever used if `depot.expandProjection` is set to
* true and that feature will be deprecated.
*
* This file stays for backwards compatibility purposes.
*/
sealed class ExpandableSection {
abstract val text: String
data class Text(val content: String) : ExpandableSection() {
override val text: String
get() = content
}
object Newline : ExpandableSection() {
override val text: String
get() = "\n"
}
data class BindVar(val symbol: String) : ExpandableSection() {
override val text: String
get() = symbol
}
sealed class Projection : ExpandableSection() {
object All : Projection() {
override val text: String
get() = "*"
}
data class Table(
val tableAlias: String,
override val text: String
) : Projection()
}
}
data class Position(val line: Int, val charInLine: Int) : Comparable {
override fun compareTo(other: Position): Int {
return if (line == other.line) {
charInLine - other.charInLine
} else {
line - other.line
}
}
}
data class SectionInfo(
val start: Position,
val end: Position,
val section: ExpandableSection
)
data class ExpandableParsedQuery(
// original query as written by user, code writers should use transformed query that has been
// processed and optimized.
val original: String,
val type: QueryType,
val inputs: List,
val projections: List,
val explicitColumns: List,
val tables: Set,
val syntaxErrors: List,
val runtimeQueryPlaceholder: Boolean
) {
companion object {
val STARTS_WITH_NUMBER = "^\\?[0-9]".toRegex()
val MISSING = ExpandableParsedQuery(
original = "missing query",
type = QueryType.UNKNOWN,
projections = emptyList(),
explicitColumns = emptyList(),
inputs = emptyList(),
tables = emptySet(),
syntaxErrors = emptyList(),
runtimeQueryPlaceholder = false
)
}
/**
* Optional data that might be assigned when the query is parsed inside an annotation processor.
* User may turn this off or it might be disabled for any reason so generated code should
* always handle not having it.
*/
var resultInfo: QueryResultInfo? = null
/**
* The transformed query when it is interpreted and rewritten by QueryInterpreter.
*/
var transformed: String = original
private set
val sections by lazy {
val specialSections: List = (inputs + projections).sortedBy { it.start }
val lines = original.lines()
val sections = arrayListOf()
var index = 0
var charInLine = 0
while (index < lines.size) {
val line = lines[index]
var multipleLineSection = false
specialSections
.filter { it.start.line == index }
.forEach { (start, end, section) ->
if (charInLine < start.charInLine) {
sections.add(
ExpandableSection.Text(
line.substring(
charInLine,
start.charInLine
)
)
)
}
sections.add(section)
charInLine = end.charInLine
if (index < end.line) {
index = end.line
multipleLineSection = true
}
}
if (!multipleLineSection) {
if (charInLine < line.length) {
sections.add(ExpandableSection.Text(line.substring(charInLine)))
}
if (index + 1 < lines.size) {
sections.add(ExpandableSection.Newline)
}
index++
charInLine = 0
}
}
sections
}
val bindSections by lazy { sections.filterIsInstance() }
private fun unnamedVariableErrors(): List {
val anonymousBindError = if (inputs.any { it.section.text == "?" }) {
arrayListOf(ParserErrors.ANONYMOUS_BIND_ARGUMENT)
} else {
emptyList()
}
return anonymousBindError + inputs.filter {
it.section.text.matches(STARTS_WITH_NUMBER)
}.map {
ParserErrors.cannotUseVariableIndices(it.section.text, it.start.charInLine)
}
}
private fun unknownQueryTypeErrors(): List {
return if (QueryType.SUPPORTED.contains(type)) {
emptyList()
} else {
listOf(ParserErrors.invalidQueryType(type))
}
}
val errors by lazy {
if (syntaxErrors.isNotEmpty()) {
// if there is a syntax error, don't report others since they might be misleading.
syntaxErrors
} else {
unnamedVariableErrors() + unknownQueryTypeErrors()
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy