diff --git a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/InsertStatement.kt b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/InsertStatement.kt index 940a13f18a..6db4f1994b 100644 --- a/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/InsertStatement.kt +++ b/exposed-core/src/main/kotlin/org/jetbrains/exposed/sql/statements/InsertStatement.kt @@ -2,6 +2,7 @@ package org.jetbrains.exposed.sql.statements import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.statements.api.PreparedStatementApi +import org.jetbrains.exposed.sql.transactions.TransactionManager import org.jetbrains.exposed.sql.vendors.* import org.jetbrains.exposed.sql.vendors.inProperCase import java.sql.ResultSet @@ -51,7 +52,7 @@ open class InsertStatement( */ fun getOrNull(column: Column): T? = resultedValues?.firstOrNull()?.getOrNull(column) - @Suppress("NestedBlockDepth", "ComplexMethod") + @Suppress("NestedBlockDepth", "ComplexMethod", "TooGenericExceptionCaught") private fun processResults(rs: ResultSet?, inserted: Int): List { val autoGeneratedKeys = arrayListOf, Any?>>() @@ -68,9 +69,33 @@ open class InsertStatement( val firstAutoIncColumn = autoIncColumns.firstOrNull { it.autoIncColumnType != null } ?: autoIncColumns.firstOrNull() if (firstAutoIncColumn != null || returnedColumns.isNotEmpty()) { while (rs?.next() == true) { - val returnedValues = returnedColumns.associateTo(mutableMapOf()) { it.first to rs.getObject(it.second) } - if (returnedValues.isEmpty() && firstAutoIncColumn != null) returnedValues[firstAutoIncColumn] = rs.getObject(1) - autoGeneratedKeys.add(returnedValues) + try { + val returnedValues = returnedColumns.associateTo(mutableMapOf()) { it.first to rs.getObject(it.second) } + if (returnedValues.isEmpty() && firstAutoIncColumn != null) { + returnedValues[firstAutoIncColumn] = rs.getObject(1) + } + autoGeneratedKeys.add(returnedValues) + } catch (cause: ArrayIndexOutOfBoundsException) { + // EXPOSED-191 Flaky Oracle test on TC build + // this try/catch should help to get information about the flaky test. + // try/catch can be safely removed after the fixing the issue. + // TooGenericExceptionCaught suppress also can be removed + + val preparedSql = this.prepareSQL(TransactionManager.current(), prepared = true) + + val returnedColumnsString = returnedColumns + .mapIndexed { index, pair -> "column: ${pair.first.name}, index: ${pair.second} (columns-list-index: $index)" } + .joinToString(prefix = "[", postfix = "]", separator = ", ") + + exposedLogger.error( + "ArrayIndexOutOfBoundsException on processResults. " + + "Table: ${table.tableName}, firstAutoIncColumn: ${firstAutoIncColumn?.name}, " + + "inserted: $inserted, returnedColumnsString: $returnedColumnsString. " + + "Failed SQL: $preparedSql", + cause + ) + throw cause + } } if (inserted > 1 && firstAutoIncColumn != null && autoGeneratedKeys.isNotEmpty() && !currentDialect.supportsMultipleGeneratedKeys) { diff --git a/exposed-dao/src/main/kotlin/org/jetbrains/exposed/dao/EntityCache.kt b/exposed-dao/src/main/kotlin/org/jetbrains/exposed/dao/EntityCache.kt index c1a00fec43..01bc02ac0d 100644 --- a/exposed-dao/src/main/kotlin/org/jetbrains/exposed/dao/EntityCache.kt +++ b/exposed-dao/src/main/kotlin/org/jetbrains/exposed/dao/EntityCache.kt @@ -226,6 +226,7 @@ class EntityCache(private val transaction: Transaction) { } } + @Suppress("TooGenericExceptionCaught") internal fun flushInserts(table: IdTable<*>) { var toFlush: List> = inserts.remove(table)?.toList().orEmpty() while (toFlush.isNotEmpty()) { @@ -236,12 +237,26 @@ class EntityCache(private val transaction: Transaction) { } } toFlush = partition.first - val ids = executeAsPartOfEntityLifecycle { - table.batchInsert(toFlush) { entry -> - for ((c, v) in entry.writeValues) { - this[c] = v + val ids = try { + executeAsPartOfEntityLifecycle { + table.batchInsert(toFlush) { entry -> + for ((c, v) in entry.writeValues) { + this[c] = v + } } } + } catch (cause: ArrayIndexOutOfBoundsException) { + // EXPOSED-191 Flaky Oracle test on TC build + // this try/catch should help to get information about the flaky test. + // try/catch can be safely removed after the fixing the issue + // TooGenericExceptionCaught suppress also can be removed + val toFlushString = toFlush.joinToString("; ") { + entry -> + entry.writeValues.map { writeValue -> "${writeValue.key.name}=${writeValue.value}" }.joinToString { ", " } + } + + exposedLogger.error("ArrayIndexOutOfBoundsException on attempt to make flush inserts. Table: ${table.tableName}, entries: ($toFlushString)", cause) + throw cause } for ((entry, genValues) in toFlush.zip(ids)) { diff --git a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/entities/EntityTests.kt b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/entities/EntityTests.kt index b117042918..9dd1ac99d0 100644 --- a/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/entities/EntityTests.kt +++ b/exposed-tests/src/test/kotlin/org/jetbrains/exposed/sql/tests/shared/entities/EntityTests.kt @@ -557,6 +557,7 @@ class EntityTests : DatabaseTestsBase() { @Test fun callLimitOnRelationDoesntMutateTheCachedValue() { withTables(Posts, Boards, Categories) { + addLogger(StdOutSqlLogger) val category1 = Category.new { title = "cat1" }