diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Expression.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Expression.kt index ba715c178f..51d344538c 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Expression.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Expression.kt @@ -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 Array.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) } diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Function.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Function.kt index 62366f046c..fadc505c97 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Function.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Function.kt @@ -20,7 +20,7 @@ open class CustomFunction( ) : Function(_columnType) { override fun toQueryBuilder(queryBuilder: QueryBuilder): Unit = queryBuilder { append(functionName, '(') - expr.toList().appendTo { +it } + expr.appendTo { +it } append(')') } } diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Union.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Union.kt index ad3b10cf27..036e9e21a9 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Union.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/Union.kt @@ -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 diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt index 09f4c6baaf..598a044b1e 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Default.kt @@ -164,7 +164,7 @@ abstract class FunctionProvider { } else { append("CONCAT_WS('", separator, "',") } - expr.toList().appendTo { +it } + expr.appendTo { +it } append(")") } @@ -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) } } diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/OracleDialect.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/OracleDialect.kt index 2d6aa7417e..b3765e4372 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/OracleDialect.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/OracleDialect.kt @@ -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 } } } diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/PostgreSQL.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/PostgreSQL.kt index 2ba5cbf27e..c0e454f797 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/PostgreSQL.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/PostgreSQL.kt @@ -31,11 +31,19 @@ internal object PostgreSQLFunctionProvider : FunctionProvider() { override fun groupConcat(expr: GroupConcat, 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(")") + } } } diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/GroupByTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/GroupByTests.kt index 346ad0ceea..66cd1dddbb 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/GroupByTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/dml/GroupByTests.kt @@ -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) } @@ -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"])