feat(engine): add substituteSelect and substituteGroupingSpec
This commit is contained in:
1 parent
020a3c09ed
commit
d6b9462677
1 file changed
+213
@@ -156,3 +156,216 @@ internal object ParameterSubstitution {
|
||||
)
|
||||
}
|
||||
|
||||
private fun substituteSelect(select: SelectStatement, boundValues: List<Value>): SelectStatement = select.copy(
|
||||
from = substituteFromItem(select.from, boundValues),
|
||||
joins = select.joins.map { join ->
|
||||
join.copy(
|
||||
from = substituteFromItem(join.from, boundValues),
|
||||
on = join.on?.let { substituteExpression(it, boundValues) }
|
||||
)
|
||||
},
|
||||
where = select.where?.let { substituteExpression(it, boundValues) },
|
||||
groupBy = select.groupBy.map { substituteExpression(it, boundValues) },
|
||||
groupingSpec = select.groupingSpec?.let { substituteGroupingSpec(it, boundValues) },
|
||||
having = select.having?.let { substituteExpression(it, boundValues) },
|
||||
projection = substituteProjection(select.projection, boundValues),
|
||||
orderBy = select.orderBy?.let { substituteOrderBy(it, boundValues) },
|
||||
distinctOn = select.distinctOn.map { substituteExpression(it, boundValues) }
|
||||
)
|
||||
|
||||
private fun substituteGroupingSpec(spec: GroupingSpec, boundValues: List<Value>): GroupingSpec = when (spec) {
|
||||
is RollupSpec -> spec.copy(expressions = spec.expressions.map { substituteExpression(it, boundValues) })
|
||||
is CubeSpec -> spec.copy(expressions = spec.expressions.map { substituteExpression(it, boundValues) })
|
||||
is GroupingSetsSpec -> spec.copy(sets = spec.sets.map { set -> set.map { substituteExpression(it, boundValues) } })
|
||||
}
|
||||
|
||||
private fun substituteFromItem(item: FromItem, boundValues: List<Value>): FromItem = when (item) {
|
||||
is TableSource -> item
|
||||
is SubquerySource -> item.copy(select = substituteQuery(item.select, boundValues))
|
||||
}
|
||||
|
||||
private fun substituteProjection(projection: Projection, boundValues: List<Value>): Projection = when (projection) {
|
||||
StarProjection -> projection
|
||||
is ExpressionProjection ->
|
||||
projection.copy(items = projection.items.map { it.copy(expression = substituteExpression(it.expression, boundValues)) })
|
||||
}
|
||||
|
||||
private fun substituteOrderBy(orderBy: OrderBy, boundValues: List<Value>): OrderBy =
|
||||
orderBy.copy(keys = orderBy.keys.map { it.copy(expression = substituteExpression(it.expression, boundValues)) })
|
||||
|
||||
private fun substituteExpression(expression: Expression, boundValues: List<Value>): Expression = when (expression) {
|
||||
is ParameterExpression -> LiteralExpression(boundValueAt(expression.index, boundValues))
|
||||
is ScalarSubqueryExpression -> expression.copy(select = substituteSelect(expression.select, boundValues))
|
||||
is ExistsExpression -> expression.copy(select = substituteSelect(expression.select, boundValues))
|
||||
is InSubqueryExpression -> expression.copy(
|
||||
operand = substituteExpression(expression.operand, boundValues),
|
||||
select = substituteSelect(expression.select, boundValues)
|
||||
)
|
||||
is BinaryExpression -> expression.copy(
|
||||
left = substituteExpression(expression.left, boundValues),
|
||||
right = substituteExpression(expression.right, boundValues)
|
||||
)
|
||||
is UnaryExpression -> expression.copy(operand = substituteExpression(expression.operand, boundValues))
|
||||
is CallExpression -> expression.copy(arguments = expression.arguments.map { substituteExpression(it, boundValues) })
|
||||
is IsNullExpression -> expression.copy(operand = substituteExpression(expression.operand, boundValues))
|
||||
is InExpression -> expression.copy(
|
||||
operand = substituteExpression(expression.operand, boundValues),
|
||||
values = expression.values.map { substituteExpression(it, boundValues) }
|
||||
)
|
||||
is BetweenExpression -> expression.copy(
|
||||
operand = substituteExpression(expression.operand, boundValues),
|
||||
low = substituteExpression(expression.low, boundValues),
|
||||
high = substituteExpression(expression.high, boundValues)
|
||||
)
|
||||
is LikeExpression -> expression.copy(
|
||||
operand = substituteExpression(expression.operand, boundValues),
|
||||
pattern = substituteExpression(expression.pattern, boundValues)
|
||||
)
|
||||
is CaseExpression -> expression.copy(
|
||||
operand = expression.operand?.let { substituteExpression(it, boundValues) },
|
||||
branches = expression.branches.map {
|
||||
it.copy(condition = substituteExpression(it.condition, boundValues), result = substituteExpression(it.result, boundValues))
|
||||
},
|
||||
elseExpression = expression.elseExpression?.let { substituteExpression(it, boundValues) }
|
||||
)
|
||||
is CastExpression -> expression.copy(operand = substituteExpression(expression.operand, boundValues))
|
||||
is CollateExpression -> expression.copy(operand = substituteExpression(expression.operand, boundValues))
|
||||
is FunctionExpression -> expression.copy(
|
||||
argument = expression.argument?.let { substituteExpression(it, boundValues) },
|
||||
separator = expression.separator?.let { substituteExpression(it, boundValues) },
|
||||
orderBy = expression.orderBy.map { it.copy(expression = substituteExpression(it.expression, boundValues)) },
|
||||
filter = expression.filter?.let { substituteExpression(it, boundValues) }
|
||||
)
|
||||
is WindowExpression -> expression.copy(
|
||||
argument = expression.argument?.let { substituteExpression(it, boundValues) },
|
||||
partitionBy = expression.partitionBy.map { substituteExpression(it, boundValues) },
|
||||
orderBy = expression.orderBy.map { it.copy(expression = substituteExpression(it.expression, boundValues)) },
|
||||
offset = expression.offset?.let { substituteExpression(it, boundValues) },
|
||||
defaultValue = expression.defaultValue?.let { substituteExpression(it, boundValues) },
|
||||
frame = expression.frame?.let { frame ->
|
||||
frame.copy(
|
||||
start = substituteFrameBound(frame.start, boundValues),
|
||||
end = substituteFrameBound(frame.end, boundValues)
|
||||
)
|
||||
}
|
||||
)
|
||||
is ColumnExpression, is LiteralExpression, is NextValueExpression, is CurrentValueExpression,
|
||||
is MergeActionExpression -> expression
|
||||
}
|
||||
|
||||
private fun substituteFrameBound(bound: FrameBound, boundValues: List<Value>): FrameBound =
|
||||
bound.copy(offset = bound.offset?.let { substituteExpression(it, boundValues) })
|
||||
|
||||
private fun boundValueAt(index: Int, boundValues: List<Value>): Value =
|
||||
boundValues.getOrNull(index - 1) ?: throw KestrelException("no value supplied for parameter \$$index")
|
||||
|
||||
// --- parameterCount's twin walk: same shape as substitute above, but only tracks the max index seen. ---
|
||||
|
||||
private fun maxIndexInAssignments(assignments: List<Assignment>): Int =
|
||||
assignments.maxOfOrNull { it.value?.let(::maxIndexInExpression) ?: 0 } ?: 0
|
||||
|
||||
private fun maxIndexInOnConflict(clause: OnConflictClause): Int = when (val action = clause.action) {
|
||||
ConflictDoNothing -> 0
|
||||
is ConflictDoUpdate -> maxOf(maxIndexInAssignments(action.assignments), action.where?.let(::maxIndexInExpression) ?: 0)
|
||||
}
|
||||
|
||||
private fun maxIndexInWhenClause(clause: MergeWhenClause): Int = when (clause) {
|
||||
is MergeMatchedUpdate -> maxOf(
|
||||
clause.condition?.let(::maxIndexInExpression) ?: 0,
|
||||
maxIndexInAssignments(clause.assignments)
|
||||
)
|
||||
is MergeMatchedDelete -> clause.condition?.let(::maxIndexInExpression) ?: 0
|
||||
is MergeNotMatchedInsert -> maxOf(
|
||||
clause.condition?.let(::maxIndexInExpression) ?: 0,
|
||||
clause.values.maxOfOrNull { it?.let(::maxIndexInExpression) ?: 0 } ?: 0
|
||||
)
|
||||
}
|
||||
|
||||
private fun maxIndexInQuery(query: QueryStatement): Int = when (query) {
|
||||
is SelectStatement -> maxIndexInSelect(query)
|
||||
is SetOperationStatement -> maxOf(
|
||||
maxIndexInQuery(query.left),
|
||||
maxIndexInQuery(query.right),
|
||||
query.orderBy?.let(::maxIndexInOrderBy) ?: 0
|
||||
)
|
||||
is WithQuery -> maxOf(
|
||||
query.ctes.maxOfOrNull { maxIndexInQuery(it.query) } ?: 0,
|
||||
maxIndexInQuery(query.body)
|
||||
)
|
||||
}
|
||||
|
||||
private fun maxIndexInSelect(select: SelectStatement): Int = maxOf(
|
||||
maxIndexInFromItem(select.from),
|
||||
select.joins.maxOfOrNull { join -> maxOf(maxIndexInFromItem(join.from), join.on?.let(::maxIndexInExpression) ?: 0) } ?: 0,
|
||||
select.where?.let(::maxIndexInExpression) ?: 0,
|
||||
select.groupBy.maxOfOrNull(::maxIndexInExpression) ?: 0,
|
||||
select.groupingSpec?.let(::maxIndexInGroupingSpec) ?: 0,
|
||||
select.having?.let(::maxIndexInExpression) ?: 0,
|
||||
maxIndexInProjection(select.projection),
|
||||
select.orderBy?.let(::maxIndexInOrderBy) ?: 0,
|
||||
select.distinctOn.maxOfOrNull(::maxIndexInExpression) ?: 0
|
||||
)
|
||||
|
||||
private fun maxIndexInGroupingSpec(spec: GroupingSpec): Int = when (spec) {
|
||||
is RollupSpec -> spec.expressions.maxOfOrNull(::maxIndexInExpression) ?: 0
|
||||
is CubeSpec -> spec.expressions.maxOfOrNull(::maxIndexInExpression) ?: 0
|
||||
is GroupingSetsSpec -> spec.sets.maxOfOrNull { set -> set.maxOfOrNull(::maxIndexInExpression) ?: 0 } ?: 0
|
||||
}
|
||||
|
||||
private fun maxIndexInFromItem(item: FromItem): Int = when (item) {
|
||||
is TableSource -> 0
|
||||
is SubquerySource -> maxIndexInQuery(item.select)
|
||||
}
|
||||
|
||||
private fun maxIndexInProjection(projection: Projection): Int = when (projection) {
|
||||
StarProjection -> 0
|
||||
is ExpressionProjection -> projection.items.maxOfOrNull { maxIndexInExpression(it.expression) } ?: 0
|
||||
}
|
||||
|
||||
private fun maxIndexInOrderBy(orderBy: OrderBy): Int = orderBy.keys.maxOfOrNull { maxIndexInExpression(it.expression) } ?: 0
|
||||
|
||||
private fun maxIndexInExpression(expression: Expression): Int = when (expression) {
|
||||
is ParameterExpression -> expression.index
|
||||
is ScalarSubqueryExpression -> maxIndexInSelect(expression.select)
|
||||
is ExistsExpression -> maxIndexInSelect(expression.select)
|
||||
is InSubqueryExpression -> maxOf(maxIndexInExpression(expression.operand), maxIndexInSelect(expression.select))
|
||||
is BinaryExpression -> maxOf(maxIndexInExpression(expression.left), maxIndexInExpression(expression.right))
|
||||
is UnaryExpression -> maxIndexInExpression(expression.operand)
|
||||
is CallExpression -> expression.arguments.maxOfOrNull(::maxIndexInExpression) ?: 0
|
||||
is IsNullExpression -> maxIndexInExpression(expression.operand)
|
||||
is InExpression -> maxOf(
|
||||
maxIndexInExpression(expression.operand),
|
||||
expression.values.maxOfOrNull(::maxIndexInExpression) ?: 0
|
||||
)
|
||||
is BetweenExpression -> maxOf(
|
||||
maxIndexInExpression(expression.operand),
|
||||
maxIndexInExpression(expression.low),
|
||||
maxIndexInExpression(expression.high)
|
||||
)
|
||||
is LikeExpression -> maxOf(maxIndexInExpression(expression.operand), maxIndexInExpression(expression.pattern))
|
||||
is CaseExpression -> maxOf(
|
||||
expression.operand?.let(::maxIndexInExpression) ?: 0,
|
||||
expression.branches.maxOfOrNull { maxOf(maxIndexInExpression(it.condition), maxIndexInExpression(it.result)) } ?: 0,
|
||||
expression.elseExpression?.let(::maxIndexInExpression) ?: 0
|
||||
)
|
||||
is CastExpression -> maxIndexInExpression(expression.operand)
|
||||
is CollateExpression -> maxIndexInExpression(expression.operand)
|
||||
is FunctionExpression -> maxOf(
|
||||
expression.argument?.let(::maxIndexInExpression) ?: 0,
|
||||
expression.separator?.let(::maxIndexInExpression) ?: 0,
|
||||
expression.orderBy.maxOfOrNull { maxIndexInExpression(it.expression) } ?: 0,
|
||||
expression.filter?.let(::maxIndexInExpression) ?: 0
|
||||
)
|
||||
is WindowExpression -> maxOf(
|
||||
expression.argument?.let(::maxIndexInExpression) ?: 0,
|
||||
expression.partitionBy.maxOfOrNull(::maxIndexInExpression) ?: 0,
|
||||
expression.orderBy.maxOfOrNull { maxIndexInExpression(it.expression) } ?: 0,
|
||||
expression.offset?.let(::maxIndexInExpression) ?: 0,
|
||||
expression.defaultValue?.let(::maxIndexInExpression) ?: 0,
|
||||
expression.frame?.start?.offset?.let(::maxIndexInExpression) ?: 0,
|
||||
expression.frame?.end?.offset?.let(::maxIndexInExpression) ?: 0
|
||||
)
|
||||
is ColumnExpression, is LiteralExpression, is NextValueExpression, is CurrentValueExpression,
|
||||
is MergeActionExpression -> 0
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user