diff --git a/okapi-core/src/main/kotlin/com/softwaremill/okapi/core/OutboxStore.kt b/okapi-core/src/main/kotlin/com/softwaremill/okapi/core/OutboxStore.kt index 07c60e5..8b21a11 100644 --- a/okapi-core/src/main/kotlin/com/softwaremill/okapi/core/OutboxStore.kt +++ b/okapi-core/src/main/kotlin/com/softwaremill/okapi/core/OutboxStore.kt @@ -24,7 +24,7 @@ interface OutboxStore { * [OutboxEntry.createdAt] of its oldest entry (the minimum `createdAt` among entries in * that status). Statuses with no entries are omitted from the result -- callers rely on * absence to mean "no backlog" (e.g. the lag gauge reports 0 for an omitted status). - * Useful for lag metrics. + * An empty [statuses] set returns an empty map. */ fun findOldestCreatedAt(statuses: Set): Map diff --git a/okapi-integration-tests/src/test/kotlin/com/softwaremill/okapi/test/store/OutboxStoreContractTests.kt b/okapi-integration-tests/src/test/kotlin/com/softwaremill/okapi/test/store/OutboxStoreContractTests.kt index 6550a91..02d1394 100644 --- a/okapi-integration-tests/src/test/kotlin/com/softwaremill/okapi/test/store/OutboxStoreContractTests.kt +++ b/okapi-integration-tests/src/test/kotlin/com/softwaremill/okapi/test/store/OutboxStoreContractTests.kt @@ -246,6 +246,16 @@ fun FunSpec.outboxStoreContractTests( oldest.keys shouldBe setOf(OutboxStatus.PENDING) } + test("[$dbName] findOldestCreatedAt returns an empty map for an empty statuses set") { + // Regression guard (issue #60): an empty `statuses` set used to build `WHERE status IN ()`, + // a syntax error on both Postgres and MySQL. Must short-circuit to emptyMap() instead. + jdbc.withTransaction { store.persist(createTestEntry()) } + + val oldest = jdbc.withTransaction { store.findOldestCreatedAt(emptySet()) } + + oldest shouldBe emptyMap() + } + test("[$dbName] claimPending returns empty when no PENDING entries") { val claimed = jdbc.withTransaction { store.claimPending(10) } diff --git a/okapi-mysql/src/main/kotlin/com/softwaremill/okapi/mysql/MysqlOutboxStore.kt b/okapi-mysql/src/main/kotlin/com/softwaremill/okapi/mysql/MysqlOutboxStore.kt index b611a5b..38a84bd 100644 --- a/okapi-mysql/src/main/kotlin/com/softwaremill/okapi/mysql/MysqlOutboxStore.kt +++ b/okapi-mysql/src/main/kotlin/com/softwaremill/okapi/mysql/MysqlOutboxStore.kt @@ -34,9 +34,9 @@ class MysqlOutboxStore( Timestamp.from(entry.lastAttempt), ) } else { - stmt.setNull(9, java.sql.Types.TIMESTAMP) + stmt.setNull(9, Types.TIMESTAMP) } - if (entry.lastError != null) stmt.setString(10, entry.lastError) else stmt.setNull(10, java.sql.Types.VARCHAR) + if (entry.lastError != null) stmt.setString(10, entry.lastError) else stmt.setNull(10, Types.VARCHAR) stmt.setString(11, entry.deliveryMetadata) stmt.executeUpdate() } @@ -102,6 +102,8 @@ class MysqlOutboxStore( } override fun findOldestCreatedAt(statuses: Set): Map { + if (statuses.isEmpty()) return emptyMap() + val result = mutableMapOf() val placeholders = statuses.joinToString(",") { "?" } val sql = "SELECT status, MIN(created_at) AS min_created_at FROM okapi_outbox WHERE status IN ($placeholders) GROUP BY status" diff --git a/okapi-postgres/src/main/kotlin/com/softwaremill/okapi/postgres/PostgresOutboxStore.kt b/okapi-postgres/src/main/kotlin/com/softwaremill/okapi/postgres/PostgresOutboxStore.kt index 0d23a71..0ee29f5 100644 --- a/okapi-postgres/src/main/kotlin/com/softwaremill/okapi/postgres/PostgresOutboxStore.kt +++ b/okapi-postgres/src/main/kotlin/com/softwaremill/okapi/postgres/PostgresOutboxStore.kt @@ -34,9 +34,9 @@ class PostgresOutboxStore( Timestamp.from(entry.lastAttempt), ) } else { - stmt.setNull(9, java.sql.Types.TIMESTAMP) + stmt.setNull(9, Types.TIMESTAMP) } - if (entry.lastError != null) stmt.setString(10, entry.lastError) else stmt.setNull(10, java.sql.Types.VARCHAR) + if (entry.lastError != null) stmt.setString(10, entry.lastError) else stmt.setNull(10, Types.VARCHAR) stmt.setString(11, entry.deliveryMetadata) stmt.executeUpdate() } @@ -96,6 +96,8 @@ class PostgresOutboxStore( } override fun findOldestCreatedAt(statuses: Set): Map { + if (statuses.isEmpty()) return emptyMap() + val result = mutableMapOf() val placeholders = statuses.joinToString(",") { "?" } val sql = "SELECT status, MIN(created_at) AS min_created_at FROM okapi_outbox WHERE status IN ($placeholders) GROUP BY status"