Skip to content

Commit

Permalink
Add support for DISTINCT in STRING_AGG functions for PostgreSQL #1313
Browse files Browse the repository at this point in the history
  • Loading branch information
Tapac committed Aug 12, 2021
1 parent af78955 commit cbd43d5
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,21 @@ class QueryBuilder(
internalBuilder.append(postfix)
}

/** Appends all the elements separated using [separator] and using the given [prefix] and [postfix] if supplied. */
fun <T> Array<T>.appendTo(
separator: CharSequence = ", ",
prefix: CharSequence = "",
postfix: CharSequence = "",
transform: QueryBuilder.(T) -> Unit
) {
internalBuilder.append(prefix)
forEachIndexed { index, element ->
if (index > 0) internalBuilder.append(separator)
transform(element)
}
internalBuilder.append(postfix)
}

/** Appends the specified [value] to this [QueryBuilder]. */
fun append(value: Char): QueryBuilder = apply { internalBuilder.append(value) }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ open class CustomFunction<T>(
) : Function<T>(_columnType) {
override fun toQueryBuilder(queryBuilder: QueryBuilder): Unit = queryBuilder {
append(functionName, '(')
expr.toList().appendTo { +it }
expr.appendTo { +it }
append(')')
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class Union(

private fun prepareStatementSQL(builder: QueryBuilder) {
builder {
statements.toList().appendTo(separator = " $unionKeyword ") {
statements.appendTo(separator = " $unionKeyword ") {
when (it) {
is Query -> {
val isSubQuery = it.orderByExpressions.isNotEmpty() || it.limit != null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ abstract class FunctionProvider {
} else {
append("CONCAT_WS('", separator, "',")
}
expr.toList().appendTo { +it }
expr.appendTo { +it }
append(")")
}

Expand All @@ -181,7 +181,7 @@ abstract class FunctionProvider {
}
append(expr.expr)
if (expr.orderBy.isNotEmpty()) {
expr.orderBy.toList().appendTo(prefix = " ORDER BY ") {
expr.orderBy.appendTo(prefix = " ORDER BY ") {
append(it.first, " ", it.second.name)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ internal object OracleFunctionProvider : FunctionProvider() {
vararg expr: Expression<*>
): Unit = queryBuilder {
if (separator == "") {
expr.toList().appendTo(separator = " || ") { +it }
expr.appendTo(separator = " || ") { +it }
} else {
expr.toList().appendTo(separator = " || '$separator' || ") { +it }
expr.appendTo(separator = " || '$separator' || ") { +it }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,19 @@ internal object PostgreSQLFunctionProvider : FunctionProvider() {

override fun <T : String?> groupConcat(expr: GroupConcat<T>, queryBuilder: QueryBuilder) {
val tr = TransactionManager.current()
return when {
expr.orderBy.isNotEmpty() -> tr.throwUnsupportedException("PostgreSQL doesn't support ORDER BY in STRING_AGG function.")
expr.distinct -> tr.throwUnsupportedException("PostgreSQL doesn't support DISTINCT in STRING_AGG function.")
expr.separator == null -> tr.throwUnsupportedException("PostgreSQL requires explicit separator in STRING_AGG function.")
else -> queryBuilder { append("STRING_AGG(", expr.expr, ", '", expr.separator, "')") }
return when (expr.separator) {
null -> tr.throwUnsupportedException("PostgreSQL requires explicit separator in STRING_AGG function.")
else -> queryBuilder {
append("STRING_AGG(")
if (expr.distinct) append(" DISTINCT ")
append(expr.expr, ", '", expr.separator, "'")
if (expr.orderBy.isNotEmpty()) {
expr.orderBy.appendTo(prefix = " ORDER BY ") {
append(it.first, " ", it.second.name)
}
}
append(")")
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ class GroupByTests : DatabaseTestsBase() {
assertTrue(e.dialect::class in dialects, e.message!!)
}
}

users.name.groupConcat().checkExcept(PostgreSQLDialect::class, PostgreSQLNGDialect::class, SQLServerDialect::class, OracleDialect::class) {
assertEquals(3, it.size)
}
Expand All @@ -185,29 +186,26 @@ class GroupByTests : DatabaseTestsBase() {
assertNull(it["Prague"])
}

users.name.groupConcat(separator = " | ", distinct = true).checkExcept(PostgreSQLDialect::class, PostgreSQLNGDialect::class, OracleDialect::class) {
users.name.groupConcat(separator = " | ", distinct = true).checkExcept(OracleDialect::class) {
assertEquals(3, it.size)
assertEquals("Andrey", it["St. Petersburg"])
when (currentDialectTest) {
is MariaDBDialect -> assertEquals(true, it["Munich"] in listOf("Sergey | Eugene", "Eugene | Sergey"))
is MysqlDialect, is SQLServerDialect, is H2Dialect -> assertEquals("Eugene | Sergey", it["Munich"])
is MysqlDialect, is SQLServerDialect, is H2Dialect, is PostgreSQLDialect, is PostgreSQLNGDialect ->
assertEquals("Eugene | Sergey", it["Munich"])
else -> assertEquals("Sergey | Eugene", it["Munich"])
}
assertNull(it["Prague"])
}

users.name.groupConcat(separator = " | ", orderBy = users.name to SortOrder.ASC).checkExcept(
PostgreSQLDialect::class, PostgreSQLNGDialect::class
) {
users.name.groupConcat(separator = " | ", orderBy = users.name to SortOrder.ASC).checkExcept{
assertEquals(3, it.size)
assertEquals("Andrey", it["St. Petersburg"])
assertEquals("Eugene | Sergey", it["Munich"])
assertNull(it["Prague"])
}

users.name.groupConcat(separator = " | ", orderBy = users.name to SortOrder.DESC).checkExcept(
PostgreSQLDialect::class, PostgreSQLNGDialect::class
) {
users.name.groupConcat(separator = " | ", orderBy = users.name to SortOrder.DESC).checkExcept{
assertEquals(3, it.size)
assertEquals("Andrey", it["St. Petersburg"])
assertEquals("Sergey | Eugene", it["Munich"])
Expand Down

0 comments on commit cbd43d5

Please sign in to comment.