From 9135c4a6c779db9af6d72be8e511172c30082fb3 Mon Sep 17 00:00:00 2001 From: Eugene Kleshnin Date: Sun, 29 Jan 2023 19:26:00 +0000 Subject: [PATCH 1/5] Add CHARINDEX function for sqlserver --- .../org/jetbrains/exposed/sql/Function.kt | 8 +++++ .../exposed/sql/SQLExpressionBuilder.kt | 3 ++ .../jetbrains/exposed/sql/vendors/Default.kt | 18 ++++++++-- .../exposed/sql/vendors/SQLServerDialect.kt | 4 +++ .../tests/shared/functions/FunctionsTests.kt | 36 +++++++++++++++++++ 5 files changed, 67 insertions(+), 2 deletions(-) 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 2833f3673f..a818f9881b 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 @@ -137,6 +137,14 @@ class Trim( override fun toQueryBuilder(queryBuilder: QueryBuilder): Unit = queryBuilder { append("TRIM(", expr, ")") } } +/** + * Represents an SQL function that returns the index of the first occurrence of [char] in [expr] or 0 + */ +class CharIndex(val expr: Expression, val char: String) : Function(IntegerColumnType()) { + override fun toQueryBuilder(queryBuilder: QueryBuilder) = + currentDialect.functionProvider.charIndex(queryBuilder, expr, char) +} + // General-Purpose Aggregate Functions /** diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SQLExpressionBuilder.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SQLExpressionBuilder.kt index fad9a6bdbe..10e88e6d42 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SQLExpressionBuilder.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SQLExpressionBuilder.kt @@ -40,6 +40,9 @@ fun Expression.substring(start: Int, length: Int): Substring /** Removes the longest string containing only spaces from both ends of string expression. */ fun Expression.trim(): Trim = Trim(this) +/** Returns the index of the first occurrence of [char] in this string expression or 0 if it doesn't contain [char] */ +fun Expression.charIndex(char: String): CharIndex = CharIndex(this, char) + // General-Purpose Aggregate Functions /** Returns the minimum value of this expression across all non-null input values, or `null` if there are no non-null values. */ 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 974519735a..4104004c3e 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 @@ -1,13 +1,12 @@ package org.jetbrains.exposed.sql.vendors +import org.jetbrains.exposed.exceptions.UnsupportedByDialectException import org.jetbrains.exposed.exceptions.throwUnsupportedException import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.TransactionManager import java.nio.ByteBuffer import java.util.* import java.util.concurrent.ConcurrentHashMap -import kotlin.collections.HashMap -import kotlin.collections.LinkedHashSet /** * Provides definitions for all the supported SQL data types. @@ -208,6 +207,21 @@ abstract class FunctionProvider { append(")") } + /** + * SQL function that returns the index of the first occurrence of the given substring [char] + * in the string expression [expr] + * + * @param queryBuilder Query builder to append the SQL function to. + * @param expr String expression to find the substring in. + * @param char: Substring to find + * @return index of the first occurrence of [char] in [expr] starting from 1 or 0 if [expr] doesn't contain [char] + */ + open fun charIndex(queryBuilder: QueryBuilder, expr: Expression, char: String) { + throw UnsupportedByDialectException( + "There's no generic SQL for CHARINDEX. There must be vendor specific implementation.", currentDialect + ) + } + // Pattern matching /** diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt index 51f57ebb9c..a37306ce75 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt @@ -73,6 +73,10 @@ internal object SQLServerFunctionProvider : FunctionProvider() { } } + override fun charIndex(queryBuilder: QueryBuilder, expr: Expression, char: String) = queryBuilder { + append("CHARINDEX(\'", char, "\',", expr, ")") + } + override fun regexp( expr1: Expression, pattern: Expression, diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt index 3e7306a65d..077fd1135f 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt @@ -301,6 +301,42 @@ class FunctionsTests : DatabaseTestsBase() { } } + @Test + fun testCharIndex() { + withCitiesAndUsers { cities, _, _ -> + val charIndex = cities.name.charIndex("e") + val results = cities.slice(charIndex).selectAll().toList() + + assertEquals(6, results[0][charIndex]) // St. Petersburg + assertEquals(0, results[1][charIndex]) // Munich + assertEquals(6, results[2][charIndex]) // Prague + } + } + + @Test + fun testCharIndex02() { + withCitiesAndUsers { cities, _, _ -> + val charIndex = cities.name.charIndex("Peter") + val results = cities.slice(charIndex).selectAll().toList() + + assertEquals(5, results[0][charIndex]) // St. Petersburg + assertEquals(0, results[1][charIndex]) // Munich + assertEquals(0, results[2][charIndex]) // Prague + } + } + + @Test + fun testCharIndex03() { + withCitiesAndUsers { cities, _, _ -> + val charIndex = cities.name.charIndex("p") + val results = cities.slice(charIndex).selectAll().toList() + + assertEquals(5, results[0][charIndex]) // St. Petersburg + assertEquals(0, results[1][charIndex]) // Munich + assertEquals(1, results[2][charIndex]) // Prague + } + } + @Test fun testRandomFunction01() { val t = DMLTestsData.Cities From 3578fbb270f9f724632cf6e5de86af2322bb951c Mon Sep 17 00:00:00 2001 From: Eugene Kleshnin Date: Sun, 29 Jan 2023 20:21:51 +0000 Subject: [PATCH 2/5] Disable charIndex tests for everything except Sql server --- .../exposed/sql/tests/shared/functions/FunctionsTests.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt index 077fd1135f..6039f59074 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt @@ -303,7 +303,7 @@ class FunctionsTests : DatabaseTestsBase() { @Test fun testCharIndex() { - withCitiesAndUsers { cities, _, _ -> + withCitiesAndUsers(TestDB.values().asList().minus(TestDB.SQLSERVER)) { cities, _, _ -> val charIndex = cities.name.charIndex("e") val results = cities.slice(charIndex).selectAll().toList() @@ -315,7 +315,7 @@ class FunctionsTests : DatabaseTestsBase() { @Test fun testCharIndex02() { - withCitiesAndUsers { cities, _, _ -> + withCitiesAndUsers(TestDB.values().asList().minus(TestDB.SQLSERVER)) { cities, _, _ -> val charIndex = cities.name.charIndex("Peter") val results = cities.slice(charIndex).selectAll().toList() @@ -327,7 +327,7 @@ class FunctionsTests : DatabaseTestsBase() { @Test fun testCharIndex03() { - withCitiesAndUsers { cities, _, _ -> + withCitiesAndUsers(TestDB.values().asList().minus(TestDB.SQLSERVER)) { cities, _, _ -> val charIndex = cities.name.charIndex("p") val results = cities.slice(charIndex).selectAll().toList() From 1cc662d5c6d22dbeda4f93e3cf22e614f8b98d74 Mon Sep 17 00:00:00 2001 From: Eugene Kleshnin Date: Fri, 3 Feb 2023 19:51:05 +0000 Subject: [PATCH 3/5] Rename charIndex to locate --- .../org/jetbrains/exposed/sql/Function.kt | 6 ++-- .../exposed/sql/SQLExpressionBuilder.kt | 4 +-- .../jetbrains/exposed/sql/vendors/Default.kt | 11 +++--- .../exposed/sql/vendors/SQLServerDialect.kt | 4 +-- .../tests/shared/functions/FunctionsTests.kt | 36 +++++++++---------- 5 files changed, 31 insertions(+), 30 deletions(-) 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 a818f9881b..1898e58f38 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 @@ -138,11 +138,11 @@ class Trim( } /** - * Represents an SQL function that returns the index of the first occurrence of [char] in [expr] or 0 + * Represents an SQL function that returns the index of the first occurrence of [substring] in [expr] or 0 */ -class CharIndex(val expr: Expression, val char: String) : Function(IntegerColumnType()) { +class Locate(val expr: Expression, val substring: String) : Function(IntegerColumnType()) { override fun toQueryBuilder(queryBuilder: QueryBuilder) = - currentDialect.functionProvider.charIndex(queryBuilder, expr, char) + currentDialect.functionProvider.locate(queryBuilder, expr, substring) } // General-Purpose Aggregate Functions diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SQLExpressionBuilder.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SQLExpressionBuilder.kt index 10e88e6d42..9a0c75e327 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SQLExpressionBuilder.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/SQLExpressionBuilder.kt @@ -40,8 +40,8 @@ fun Expression.substring(start: Int, length: Int): Substring /** Removes the longest string containing only spaces from both ends of string expression. */ fun Expression.trim(): Trim = Trim(this) -/** Returns the index of the first occurrence of [char] in this string expression or 0 if it doesn't contain [char] */ -fun Expression.charIndex(char: String): CharIndex = CharIndex(this, char) +/** Returns the index of the first occurrence of [substring] in this string expression or 0 if it doesn't contain [substring] */ +fun Expression.locate(substring: String): Locate = Locate(this, substring) // General-Purpose Aggregate Functions 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 4104004c3e..eaf363ed73 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 @@ -208,17 +208,18 @@ abstract class FunctionProvider { } /** - * SQL function that returns the index of the first occurrence of the given substring [char] + * SQL function that returns the index of the first occurrence of the given substring [substring] * in the string expression [expr] * * @param queryBuilder Query builder to append the SQL function to. * @param expr String expression to find the substring in. - * @param char: Substring to find - * @return index of the first occurrence of [char] in [expr] starting from 1 or 0 if [expr] doesn't contain [char] + * @param substring: Substring to find + * @return index of the first occurrence of [substring] in [expr] starting from 1 + * or 0 if [expr] doesn't contain [substring] */ - open fun charIndex(queryBuilder: QueryBuilder, expr: Expression, char: String) { + open fun locate(queryBuilder: QueryBuilder, expr: Expression, substring: String) { throw UnsupportedByDialectException( - "There's no generic SQL for CHARINDEX. There must be vendor specific implementation.", currentDialect + "There's no generic SQL for LOCATE. There must be vendor specific implementation.", currentDialect ) } diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt index a37306ce75..eddb1dcd66 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt @@ -73,8 +73,8 @@ internal object SQLServerFunctionProvider : FunctionProvider() { } } - override fun charIndex(queryBuilder: QueryBuilder, expr: Expression, char: String) = queryBuilder { - append("CHARINDEX(\'", char, "\',", expr, ")") + override fun locate(queryBuilder: QueryBuilder, expr: Expression, substring: String) = queryBuilder { + append("CHARINDEX(\'", substring, "\',", expr, ")") } override fun regexp( diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt index 6039f59074..a6d415066c 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt @@ -302,38 +302,38 @@ class FunctionsTests : DatabaseTestsBase() { } @Test - fun testCharIndex() { + fun testLocate() { withCitiesAndUsers(TestDB.values().asList().minus(TestDB.SQLSERVER)) { cities, _, _ -> - val charIndex = cities.name.charIndex("e") - val results = cities.slice(charIndex).selectAll().toList() + val locate = cities.name.locate("e") + val results = cities.slice(locate).selectAll().toList() - assertEquals(6, results[0][charIndex]) // St. Petersburg - assertEquals(0, results[1][charIndex]) // Munich - assertEquals(6, results[2][charIndex]) // Prague + assertEquals(6, results[0][locate]) // St. Petersburg + assertEquals(0, results[1][locate]) // Munich + assertEquals(6, results[2][locate]) // Prague } } @Test - fun testCharIndex02() { + fun testLocate02() { withCitiesAndUsers(TestDB.values().asList().minus(TestDB.SQLSERVER)) { cities, _, _ -> - val charIndex = cities.name.charIndex("Peter") - val results = cities.slice(charIndex).selectAll().toList() + val locate = cities.name.locate("Peter") + val results = cities.slice(locate).selectAll().toList() - assertEquals(5, results[0][charIndex]) // St. Petersburg - assertEquals(0, results[1][charIndex]) // Munich - assertEquals(0, results[2][charIndex]) // Prague + assertEquals(5, results[0][locate]) // St. Petersburg + assertEquals(0, results[1][locate]) // Munich + assertEquals(0, results[2][locate]) // Prague } } @Test - fun testCharIndex03() { + fun testLocate03() { withCitiesAndUsers(TestDB.values().asList().minus(TestDB.SQLSERVER)) { cities, _, _ -> - val charIndex = cities.name.charIndex("p") - val results = cities.slice(charIndex).selectAll().toList() + val locate = cities.name.locate("p") + val results = cities.slice(locate).selectAll().toList() - assertEquals(5, results[0][charIndex]) // St. Petersburg - assertEquals(0, results[1][charIndex]) // Munich - assertEquals(1, results[2][charIndex]) // Prague + assertEquals(5, results[0][locate]) // St. Petersburg + assertEquals(0, results[1][locate]) // Munich + assertEquals(1, results[2][locate]) // Prague } } From 1ae164a0ca94fbaf6784d7ea5fbde3ca921ebc64 Mon Sep 17 00:00:00 2001 From: Eugene Kleshnin Date: Fri, 3 Feb 2023 21:06:15 +0000 Subject: [PATCH 4/5] add locate support for all db systems --- .../main/kotlin/org/jetbrains/exposed/sql/vendors/H2.kt | 8 ++++++++ .../org/jetbrains/exposed/sql/vendors/MariaDBDialect.kt | 8 ++++++++ .../kotlin/org/jetbrains/exposed/sql/vendors/Mysql.kt | 8 ++++++++ .../org/jetbrains/exposed/sql/vendors/OracleDialect.kt | 8 ++++++++ .../org/jetbrains/exposed/sql/vendors/PostgreSQL.kt | 8 ++++++++ .../org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt | 6 +++++- .../org/jetbrains/exposed/sql/vendors/SQLiteDialect.kt | 8 ++++++++ .../exposed/sql/tests/shared/functions/FunctionsTests.kt | 6 +++--- 8 files changed, 56 insertions(+), 4 deletions(-) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/H2.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/H2.kt index bb60c0d349..461475e1e6 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/H2.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/H2.kt @@ -108,6 +108,14 @@ internal object H2FunctionProvider : FunctionProvider() { return super.insert(false, table, columns, sql, transaction).replaceFirst("INSERT", "MERGE") } + + override fun locate( + queryBuilder: QueryBuilder, + expr: Expression, + substring: String + ) = queryBuilder { + append("LOCATE(\'", substring, "\',", expr, ")") + } } /** diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/MariaDBDialect.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/MariaDBDialect.kt index b593b033c1..9d65c28d7b 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/MariaDBDialect.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/MariaDBDialect.kt @@ -18,6 +18,14 @@ internal object MariaDBFunctionProvider : MysqlFunctionProvider() { ): Unit = queryBuilder { append(expr1, " REGEXP ", pattern) } + + override fun locate( + queryBuilder: QueryBuilder, + expr: Expression, + substring: String + ) = queryBuilder { + append("LOCATE(\'", substring, "\',", expr, ")") + } } /** diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Mysql.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Mysql.kt index 3dbc2ef098..84f765b282 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Mysql.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/Mysql.kt @@ -72,6 +72,14 @@ internal open class MysqlFunctionProvider : FunctionProvider() { override fun Expression.match(pattern: String, mode: MatchMode?): Op = MATCH(this, pattern, mode ?: MysqlMatchMode.STRICT) + override fun locate( + queryBuilder: QueryBuilder, + expr: Expression, + substring: String + ) = queryBuilder { + append("LOCATE(\'", substring, "\',", expr, ")") + } + override fun regexp( expr1: Expression, pattern: Expression, 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 f015f060c7..ec50b62225 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 @@ -92,6 +92,14 @@ internal object OracleFunctionProvider : FunctionProvider() { append(col, " ", order.name, ")") } + override fun locate( + queryBuilder: QueryBuilder, + expr: Expression, + substring: String + ) = queryBuilder { + append("INSTR(", expr, ",\'", substring, "\')") + } + override fun year(expr: Expression, queryBuilder: QueryBuilder): Unit = queryBuilder { append("Extract(YEAR FROM ") append(expr) 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 102b0d8c75..15208125ee 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 @@ -46,6 +46,14 @@ internal object PostgreSQLFunctionProvider : FunctionProvider() { } } + override fun locate( + queryBuilder: QueryBuilder, + expr: Expression, + substring: String + ) = queryBuilder { + append("POSITION(\'", substring, "\' IN ", expr, ")") + } + override fun regexp( expr1: Expression, pattern: Expression, diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt index eddb1dcd66..520cc490f5 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLServerDialect.kt @@ -73,7 +73,11 @@ internal object SQLServerFunctionProvider : FunctionProvider() { } } - override fun locate(queryBuilder: QueryBuilder, expr: Expression, substring: String) = queryBuilder { + override fun locate( + queryBuilder: QueryBuilder, + expr: Expression, + substring: String + ) = queryBuilder { append("CHARINDEX(\'", substring, "\',", expr, ")") } diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLiteDialect.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLiteDialect.kt index 84bc6322df..318d7683ff 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLiteDialect.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLiteDialect.kt @@ -44,6 +44,14 @@ internal object SQLiteFunctionProvider : FunctionProvider() { } } + override fun locate( + queryBuilder: QueryBuilder, + expr: Expression, + substring: String + ) = queryBuilder { + append("INSTR(", expr, ",\'", substring, "\')") + } + override fun regexp( expr1: Expression, pattern: Expression, diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt index a6d415066c..5040b0ef7d 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt @@ -303,7 +303,7 @@ class FunctionsTests : DatabaseTestsBase() { @Test fun testLocate() { - withCitiesAndUsers(TestDB.values().asList().minus(TestDB.SQLSERVER)) { cities, _, _ -> + withCitiesAndUsers { cities, _, _ -> val locate = cities.name.locate("e") val results = cities.slice(locate).selectAll().toList() @@ -315,7 +315,7 @@ class FunctionsTests : DatabaseTestsBase() { @Test fun testLocate02() { - withCitiesAndUsers(TestDB.values().asList().minus(TestDB.SQLSERVER)) { cities, _, _ -> + withCitiesAndUsers { cities, _, _ -> val locate = cities.name.locate("Peter") val results = cities.slice(locate).selectAll().toList() @@ -327,7 +327,7 @@ class FunctionsTests : DatabaseTestsBase() { @Test fun testLocate03() { - withCitiesAndUsers(TestDB.values().asList().minus(TestDB.SQLSERVER)) { cities, _, _ -> + withCitiesAndUsers { cities, _, _ -> val locate = cities.name.locate("p") val results = cities.slice(locate).selectAll().toList() From 740447ca790e24dbc7a9833995d79eeca11f48ca Mon Sep 17 00:00:00 2001 From: Eugene Kleshnin Date: Fri, 3 Feb 2023 21:53:50 +0000 Subject: [PATCH 5/5] Add note to case-sensitive locale dialects and fix the test --- .../kotlin/org/jetbrains/exposed/sql/vendors/H2.kt | 4 ++++ .../org/jetbrains/exposed/sql/vendors/PostgreSQL.kt | 4 ++++ .../org/jetbrains/exposed/sql/vendors/SQLiteDialect.kt | 4 ++++ .../sql/tests/shared/functions/FunctionsTests.kt | 10 ++++++++-- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/H2.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/H2.kt index 461475e1e6..aa04c93a31 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/H2.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/H2.kt @@ -109,6 +109,10 @@ internal object H2FunctionProvider : FunctionProvider() { return super.insert(false, table, columns, sql, transaction).replaceFirst("INSERT", "MERGE") } + /** + * Implementation of [FunctionProvider.locate] + * Note: search is case-sensitive + * */ override fun locate( queryBuilder: QueryBuilder, expr: Expression, 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 15208125ee..787df029b7 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 @@ -46,6 +46,10 @@ internal object PostgreSQLFunctionProvider : FunctionProvider() { } } + /** + * Implementation of [FunctionProvider.locate] + * Note: search is case-sensitive + * */ override fun locate( queryBuilder: QueryBuilder, expr: Expression, diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLiteDialect.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLiteDialect.kt index 318d7683ff..a17b7ac0d1 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLiteDialect.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/vendors/SQLiteDialect.kt @@ -44,6 +44,10 @@ internal object SQLiteFunctionProvider : FunctionProvider() { } } + /** + * Implementation of [FunctionProvider.locate] + * Note: search is case-sensitive + * */ override fun locate( queryBuilder: QueryBuilder, expr: Expression, diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt index 5040b0ef7d..a4037979c6 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/functions/FunctionsTests.kt @@ -15,7 +15,9 @@ import org.jetbrains.exposed.sql.tests.shared.dml.DMLTestsData import org.jetbrains.exposed.sql.tests.shared.dml.withCitiesAndUsers import org.jetbrains.exposed.sql.vendors.H2Dialect import org.jetbrains.exposed.sql.vendors.OracleDialect +import org.jetbrains.exposed.sql.vendors.PostgreSQLDialect import org.jetbrains.exposed.sql.vendors.SQLServerDialect +import org.jetbrains.exposed.sql.vendors.SQLiteDialect import org.jetbrains.exposed.sql.vendors.h2Mode import org.junit.Test import kotlin.test.assertEquals @@ -328,12 +330,16 @@ class FunctionsTests : DatabaseTestsBase() { @Test fun testLocate03() { withCitiesAndUsers { cities, _, _ -> + val isCaseSensitiveDialect = currentDialectTest is SQLiteDialect || + currentDialectTest is PostgreSQLDialect || + currentDialectTest is H2Dialect + val locate = cities.name.locate("p") val results = cities.slice(locate).selectAll().toList() - assertEquals(5, results[0][locate]) // St. Petersburg + assertEquals(if (isCaseSensitiveDialect) 0 else 5, results[0][locate]) // St. Petersburg assertEquals(0, results[1][locate]) // Munich - assertEquals(1, results[2][locate]) // Prague + assertEquals(if (isCaseSensitiveDialect) 0 else 1, results[2][locate]) // Prague } }