+
Skip to content

[Feature] Add support for window functions #460

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions buildSrc/src/main/kotlin/ktorm.maven-publish.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,11 @@ publishing {
name.set("夜里的向日葵")
email.set("641571835@qq.com")
}
developer {
id.set("michaelfyc")
name.set("michaelfyc")
email.set("michael.fyc@outlook.com")
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.ktorm.expression

import org.ktorm.database.DialectFeatureNotSupportedException

/**
* Base class designed to visit or modify SQL expression trees using visitor pattern.
*
Expand Down Expand Up @@ -68,6 +70,7 @@ public open class SqlExpressionVisitor {
is BetweenExpression<*> -> visitBetween(expr)
is ArgumentExpression -> visitArgument(expr)
is FunctionExpression -> visitFunction(expr)
is WindowFunctionExpression -> visitWindowFunction(expr)
is CaseWhenExpression -> visitCaseWhen(expr)
else -> visitUnknown(expr)
}
Expand Down Expand Up @@ -304,6 +307,25 @@ public open class SqlExpressionVisitor {
}
}

protected open fun visitWindow(expr: WindowExpression): WindowExpression {
return expr
}

protected open fun <T : Any> visitWindowFunction(expr: WindowFunctionExpression<T>): WindowFunctionExpression<T> {
val arguments = visitExpressionList(expr.arguments)
check(expr.window != null) {
throw DialectFeatureNotSupportedException("no anonymous or named windows found in window function expression `${expr.functionName}`.")
}
val window = visitWindow(expr.window)
if (arguments === expr.arguments && expr.window === window) {
return expr
}
return expr.copy(
arguments = arguments,
window = window
)
}

protected open fun <T : Any> visitCaseWhen(expr: CaseWhenExpression<T>): CaseWhenExpression<T> {
val operand = expr.operand?.let { visitScalar(it) }
val whenClauses = visitWhenClauses(expr.whenClauses)
Expand Down
180 changes: 180 additions & 0 deletions ktorm-core/src/main/kotlin/org/ktorm/expression/SqlExpressions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,186 @@ public data class FunctionExpression<T : Any>(
override val extraProperties: Map<String, Any> = emptyMap()
) : ScalarExpression<T>()

/**
* The enum of window function type in a [WindowExpression].
*/
public enum class WindowFunctionType(private val type: String) {
// aggregate
/**
* The min function, translated to `min(column)` in SQL.
*/
MIN("min"),

/**
* The max function, translated to `max(column)` in SQL.
*/
MAX("max"),

/**
* The avg function, translated to `avg(column)` in SQL.
*/
AVG("avg"),

/**
* The sum function, translated to `sum(column)` in SQL.
*/
SUM("sum"),

/**
* The count function, translated to `count(column)` in SQL.
*/
COUNT("count"),

// non-aggregate

/**
* The cume_dist function, translated to `cume_dist()` in SQL.
*/
CUME_DIST("cume_dist"),

/**
* The dense_rank function, translated to `dense_rank()` in SQL.
*/
DENSE_RANK("dense_rank"),

/**
* The first_value function, translated to `first_value(column)` in SQL.
*/
FIRST_VALUE("first_value"),

/**
* The lag function, translated to `lag(column, offset, default_value)` in SQL.
*/
LAG("lag"),

/**
* The last_value function, translated to `last_value(column)` in SQL.
*/
LAST_VALUE("last_value"),

/**
* The lead function, translated to `lead(column, offset, default_value)` in SQL.
*/
LEAD("lead"),

/**
* The nth_value function, translated to `nth_value(column, n)` in SQL.
*/
NTH_VALUE("nth_value"),

/**
* The ntile function, translated to `ntile(n)` in SQL.
*/
NTILE("ntile"),

/**
* The percent_rank function, translated to `percent_rank()` in SQL.
*/
PERCENT_RANK("percent_rank"),

/**
* The rank function, translated to `rank()` in SQL.
*/
RANK("rank"),

/**
* The row_number function, translated to `row_number()` in SQL.
*/
ROW_NUMBER("row_number");

override fun toString(): String {
return type
}
}


/**
* Window function expression, represents a SQL window function call.
*
* @property functionName the name of the window function.
* @property arguments the argument passed to the window function.
* @property window window specification of the window function.
* @since 3.6
*/
public data class WindowFunctionExpression<T : Any>(
val functionName: WindowFunctionType,
val arguments: List<ScalarExpression<*>>,
val window: WindowExpression?,
override val sqlType: SqlType<T>,
override val isLeafNode: Boolean = false,
override val extraProperties: Map<String, Any> = emptyMap()
) : ScalarExpression<T>()

/**
* Window expression, represents either an anonymous or named window.
*
* @property partitionArguments column expression passed to the partition clause.
* @property orderByExpressions column expression passed to the orderBy clause.
* @property frameUnit frame unit of a window frame clause
* @property frameExpression frame clause of the window function.
*
* @since 3.6
*/
public data class WindowExpression(
val partitionArguments: List<ScalarExpression<*>>,
val orderByExpressions: List<OrderByExpression>,
val frameUnit:FrameUnitType?,
val frameExpression: Pair<FrameExpression<*>,FrameExpression<*>?>?,
override val isLeafNode: Boolean = false,
override val extraProperties: Map<String, Any> = emptyMap()
): SqlExpression()

/**
* The enum type of frame unit in [WindowExpression].
*
* @since 3.6
*/
public enum class FrameUnitType(private val type: String){
ROWS("rows"),
RANGE("range"),
GROUPS("groups"),
ROWS_BETWEEN("rows between"),
RANGE_BETWEEN("range between"),
GROUPS_BETWEEN("groups between");

override fun toString(): String {
return type
}
}

/**
* The enum type of frame extent type in [FrameExpression].
*
* @since 3.6
*/
public enum class FrameExtentType(private val type: String) {
CURRENT_ROW("current row"),
UNBOUNDED_PRECEDING("unbounded preceding"),
UNBOUNDED_FOLLOWING("unbounded following"),
PRECEDING("preceding"),
FOLLOWING("following");

override fun toString(): String {
return type
}
}

/**
* Frame expression, represents a SQL window function frame clause.
*
* @property frameExtentType frame extent type of a frame clause.
* @property argument frame argument passed to frame clause.
*
* @since 3.6
*/
public data class FrameExpression<T : Any>(
val frameExtentType: FrameExtentType,
val argument: ScalarExpression<*>?,
override val sqlType: SqlType<T>,
override val isLeafNode: Boolean = false,
override val extraProperties: Map<String, Any> = emptyMap()
) : ScalarExpression<T>()

/**
* Case-when expression, represents a SQL case-when clause.
*
Expand Down
43 changes: 43 additions & 0 deletions ktorm-core/src/main/kotlin/org/ktorm/expression/SqlFormatter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,49 @@ public abstract class SqlFormatter(
return expr
}

override fun visitWindow(expr: WindowExpression): WindowExpression{
if (expr.partitionArguments.isNotEmpty()) {
writeKeyword("partition by ")
visitExpressionList(expr.partitionArguments)
}

if (expr.orderByExpressions.isNotEmpty()) {
writeKeyword("order by ")
visitOrderByList(expr.orderByExpressions)
}
if (expr.frameUnit != null) {
writeKeyword("${expr.frameUnit} ")
if (expr.frameExpression != null) {
val first = expr.frameExpression.first
val second = expr.frameExpression.second
first.argument?.let { visit(it) }
writeKeyword("${first.frameExtentType} ")
if(second!=null){
writeKeyword("and ")
second.argument?.let { visit(it) }
writeKeyword("${second.frameExtentType}")
}
}
}
removeLastBlank()
return expr
}

override fun <T : Any> visitWindowFunction(expr: WindowFunctionExpression<T>): WindowFunctionExpression<T> {
writeKeyword("${expr.functionName}(")
visitExpressionList(expr.arguments)
removeLastBlank()
writeKeyword(") over (")
check(expr.window != null) {
throw DialectFeatureNotSupportedException("no anonymous or named windows found in window function expression `${expr.functionName}`.")
}

visitWindow(expr.window)

write(")")
return expr
}

override fun <T : Any> visitCaseWhen(expr: CaseWhenExpression<T>): CaseWhenExpression<T> {
writeKeyword("case ")

Expand Down
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载