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