diff --git a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/h2/H2Adapter.kt b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/h2/H2Adapter.kt index ca5caa5..83657c2 100644 --- a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/h2/H2Adapter.kt +++ b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/h2/H2Adapter.kt @@ -97,7 +97,7 @@ internal class H2Adapter(driver: JdbcDriver, tableName: String) : "createdAt" FROM "$tableName" WHERE "pKind" = ? AND "scheduledAt" <= ? - ORDER BY "scheduledAt" + ORDER BY "scheduledAt", "id" LIMIT 1 FOR UPDATE """ @@ -136,7 +136,7 @@ internal class H2Adapter(driver: JdbcDriver, tableName: String) : SELECT TOP $limit "id" FROM "$tableName" WHERE "pKind" = ? AND "scheduledAt" <= ? - ORDER BY "scheduledAt" + ORDER BY "scheduledAt", "id" ) """ diff --git a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/h2/H2Migrations.kt b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/h2/H2Migrations.kt index bd5cb9e..95ef431 100644 --- a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/h2/H2Migrations.kt +++ b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/h2/H2Migrations.kt @@ -31,11 +31,44 @@ internal object H2Migrations { ON "$tableName" ("pKind", "pKey"); CREATE INDEX "${tableName}__KindPlusScheduledAtIndex" - ON "$tableName" ("pKind", "scheduledAt"); + ON "$tableName" ("pKind", "scheduledAt", "id"); CREATE INDEX "${tableName}__LockUuidPlusIdIndex" ON "$tableName" ("lockUuid", "id"); """, - ) + ), + // Migration 2: Update index to include 'id' column for deterministic ordering + Migration( + sql = + """ + DROP INDEX IF EXISTS "${tableName}__KindPlusScheduledAtIndex"; + CREATE INDEX "${tableName}__KindPlusScheduledAtIndex" + ON "$tableName" ("pKind", "scheduledAt", "id"); + """, + needsExecution = { conn -> + // Check if index exists but doesn't have the 'id' column + val metadata = conn.underlying.metaData + var hasIndex = false + var hasIdColumn = false + + metadata.getIndexInfo(null, null, tableName, false, false).use { rs -> + while (rs.next()) { + val indexName = rs.getString("INDEX_NAME") + if (indexName == "${tableName}__KindPlusScheduledAtIndex") { + hasIndex = true + val columnName = rs.getString("COLUMN_NAME") + val ordinalPosition = rs.getShort("ORDINAL_POSITION") + // Check if 'id' is in position 3 + if (columnName == "id" && ordinalPosition == 3.toShort()) { + hasIdColumn = true + } + } + } + } + + // Run migration if index exists but doesn't have id column + hasIndex && !hasIdColumn + }, + ), ) } diff --git a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/hsqldb/HSQLDBAdapter.kt b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/hsqldb/HSQLDBAdapter.kt index dee4c1b..d4a2cce 100644 --- a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/hsqldb/HSQLDBAdapter.kt +++ b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/hsqldb/HSQLDBAdapter.kt @@ -74,7 +74,7 @@ internal class HSQLDBAdapter(driver: JdbcDriver, tableName: String) : "createdAt" FROM "$tableName" WHERE "pKind" = ? AND "scheduledAt" <= ? - ORDER BY "scheduledAt" + ORDER BY "scheduledAt", "id" FETCH FIRST 1 ROWS ONLY """ @@ -112,7 +112,7 @@ internal class HSQLDBAdapter(driver: JdbcDriver, tableName: String) : SELECT "id" FROM "$tableName" WHERE "pKind" = ? AND "scheduledAt" <= ? - ORDER BY "scheduledAt" + ORDER BY "scheduledAt", "id" LIMIT $limit ) """ diff --git a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/hsqldb/HSQLDBMigrations.kt b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/hsqldb/HSQLDBMigrations.kt index ee6811e..b3aad26 100644 --- a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/hsqldb/HSQLDBMigrations.kt +++ b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/hsqldb/HSQLDBMigrations.kt @@ -31,11 +31,44 @@ internal object HSQLDBMigrations { ON "$tableName" ("pKind", "pKey"); CREATE INDEX "${tableName}__KindPlusScheduledAtIndex" - ON "$tableName" ("pKind", "scheduledAt"); + ON "$tableName" ("pKind", "scheduledAt", "id"); CREATE INDEX "${tableName}__LockUuidPlusIdIndex" ON "$tableName" ("lockUuid", "id"); """, - ) + ), + // Migration 2: Update index to include 'id' column for deterministic ordering + Migration( + sql = + """ + DROP INDEX "${tableName}__KindPlusScheduledAtIndex" IF EXISTS; + CREATE INDEX "${tableName}__KindPlusScheduledAtIndex" + ON "$tableName" ("pKind", "scheduledAt", "id"); + """, + needsExecution = { conn -> + // Check if index exists but doesn't have the 'id' column + val metadata = conn.underlying.metaData + var hasIndex = false + var hasIdColumn = false + + metadata.getIndexInfo(null, null, tableName, false, false).use { rs -> + while (rs.next()) { + val indexName = rs.getString("INDEX_NAME") + if (indexName == "${tableName}__KindPlusScheduledAtIndex") { + hasIndex = true + val columnName = rs.getString("COLUMN_NAME") + val ordinalPosition = rs.getShort("ORDINAL_POSITION") + // Check if 'id' is in position 3 + if (columnName == "id" && ordinalPosition == 3.toShort()) { + hasIdColumn = true + } + } + } + } + + // Run migration if index exists but doesn't have id column + hasIndex && !hasIdColumn + }, + ), ) } diff --git a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mariadb/MariaDBAdapter.kt b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mariadb/MariaDBAdapter.kt index 5a0f7ec..51b1f96 100644 --- a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mariadb/MariaDBAdapter.kt +++ b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mariadb/MariaDBAdapter.kt @@ -93,7 +93,7 @@ internal class MariaDBAdapter(driver: JdbcDriver, tableName: String) : `createdAt` FROM `$tableName` WHERE `pKind` = ? AND `scheduledAt` <= ? - ORDER BY `scheduledAt` + ORDER BY `scheduledAt`, `id` LIMIT 1 FOR UPDATE SKIP LOCKED """ @@ -133,7 +133,7 @@ internal class MariaDBAdapter(driver: JdbcDriver, tableName: String) : SELECT `id` FROM `$tableName` WHERE `pKind` = ? AND `scheduledAt` <= ? - ORDER BY `scheduledAt` + ORDER BY `scheduledAt`, `id` LIMIT $limit FOR UPDATE SKIP LOCKED ) AS subq diff --git a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mariadb/MariaDBMigrations.kt b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mariadb/MariaDBMigrations.kt index 8838ee0..8c004b6 100644 --- a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mariadb/MariaDBMigrations.kt +++ b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mariadb/MariaDBMigrations.kt @@ -29,9 +29,41 @@ internal object MariaDBMigrations { UNIQUE KEY `${tableName}__KindPlusKeyUniqueIndex` (`pKind`, `pKey`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - CREATE INDEX `${tableName}__KindPlusScheduledAtIndex` ON `$tableName`(`pKind`, `scheduledAt`); + CREATE INDEX `${tableName}__KindPlusScheduledAtIndex` ON `$tableName`(`pKind`, `scheduledAt`, `id`); CREATE INDEX `${tableName}__LockUuidPlusIdIndex` ON `$tableName`(`lockUuid`, `id`) """, - ) + ), + // Migration 2: Update index to include 'id' column for deterministic ordering + Migration( + sql = + """ + DROP INDEX IF EXISTS `${tableName}__KindPlusScheduledAtIndex` ON `$tableName`; + CREATE INDEX `${tableName}__KindPlusScheduledAtIndex` ON `$tableName`(`pKind`, `scheduledAt`, `id`); + """, + needsExecution = { conn -> + // Check if index exists but doesn't have the 'id' column + val metadata = conn.underlying.metaData + var hasIndex = false + var hasIdColumn = false + + metadata.getIndexInfo(null, null, tableName, false, false).use { rs -> + while (rs.next()) { + val indexName = rs.getString("INDEX_NAME") + if (indexName == "${tableName}__KindPlusScheduledAtIndex") { + hasIndex = true + val columnName = rs.getString("COLUMN_NAME") + val ordinalPosition = rs.getShort("ORDINAL_POSITION") + // Check if 'id' is in position 3 + if (columnName == "id" && ordinalPosition == 3.toShort()) { + hasIdColumn = true + } + } + } + } + + // Run migration if index exists but doesn't have id column + hasIndex && !hasIdColumn + }, + ), ) } diff --git a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mssql/MsSqlServerAdapter.kt b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mssql/MsSqlServerAdapter.kt index fa1587c..ac141c2 100644 --- a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mssql/MsSqlServerAdapter.kt +++ b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mssql/MsSqlServerAdapter.kt @@ -112,7 +112,7 @@ internal class MsSqlServerAdapter(driver: JdbcDriver, tableName: String) : WITH (UPDLOCK, READPAST) WHERE [pKind] = ? AND [scheduledAt] <= ? - ORDER BY [scheduledAt] + ORDER BY [scheduledAt], [id] """ return conn.prepareStatement(sql) { stmt -> @@ -153,7 +153,7 @@ internal class MsSqlServerAdapter(driver: JdbcDriver, tableName: String) : WITH (UPDLOCK, READPAST) WHERE [pKind] = ? AND [scheduledAt] <= ? - ORDER BY [scheduledAt] + ORDER BY [scheduledAt], [id] ) """ diff --git a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mssql/MsSqlServerMigrations.kt b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mssql/MsSqlServerMigrations.kt index 8c0e2ed..27a63fb 100644 --- a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mssql/MsSqlServerMigrations.kt +++ b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/mssql/MsSqlServerMigrations.kt @@ -32,11 +32,45 @@ internal object MsSqlServerMigrations { ON [$tableName] ([pKind], [pKey]); CREATE INDEX [${tableName}__KindPlusScheduledAtIndex] - ON [$tableName]([pKind], [scheduledAt]); + ON [$tableName]([pKind], [scheduledAt], [id]); CREATE INDEX [${tableName}__LockUuidPlusIdIndex] ON [$tableName]([lockUuid], [id]); """, - ) + ), + // Migration 2: Update index to include 'id' column for deterministic ordering + Migration( + sql = + """ + IF EXISTS (SELECT * FROM sys.indexes WHERE name = '${tableName}__KindPlusScheduledAtIndex') + DROP INDEX [${tableName}__KindPlusScheduledAtIndex] ON [$tableName]; + CREATE INDEX [${tableName}__KindPlusScheduledAtIndex] + ON [$tableName]([pKind], [scheduledAt], [id]); + """, + needsExecution = { conn -> + // Check if index exists but doesn't have the 'id' column + val metadata = conn.underlying.metaData + var hasIndex = false + var hasIdColumn = false + + metadata.getIndexInfo(null, null, tableName, false, false).use { rs -> + while (rs.next()) { + val indexName = rs.getString("INDEX_NAME") + if (indexName == "${tableName}__KindPlusScheduledAtIndex") { + hasIndex = true + val columnName = rs.getString("COLUMN_NAME") + val ordinalPosition = rs.getShort("ORDINAL_POSITION") + // Check if 'id' is in position 3 + if (columnName == "id" && ordinalPosition == 3.toShort()) { + hasIdColumn = true + } + } + } + } + + // Run migration if index exists but doesn't have id column + hasIndex && !hasIdColumn + }, + ), ) } diff --git a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/oracle/OracleAdapter.kt b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/oracle/OracleAdapter.kt index 04cb3ed..b03310f 100644 --- a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/oracle/OracleAdapter.kt +++ b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/oracle/OracleAdapter.kt @@ -141,7 +141,7 @@ internal class OracleAdapter(driver: JdbcDriver, tableName: String) : SELECT ROWID FROM "$tableName" WHERE "pKind" = ? AND "scheduledAt" <= ? - ORDER BY "scheduledAt" + ORDER BY "scheduledAt", "id" FETCH FIRST 1 ROWS ONLY ) FOR UPDATE SKIP LOCKED @@ -180,7 +180,7 @@ internal class OracleAdapter(driver: JdbcDriver, tableName: String) : SELECT ROWID FROM "$tableName" WHERE "pKind" = ? AND "scheduledAt" <= ? - ORDER BY "scheduledAt" + ORDER BY "scheduledAt", "id" FETCH FIRST $limit ROWS ONLY ) FOR UPDATE SKIP LOCKED diff --git a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/oracle/OracleMigrations.kt b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/oracle/OracleMigrations.kt index b30a11f..f12eb98 100644 --- a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/oracle/OracleMigrations.kt +++ b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/oracle/OracleMigrations.kt @@ -31,11 +31,52 @@ internal object OracleMigrations { ON "$tableName"("pKey", "pKind"); CREATE INDEX "${tableName}__KindPlusScheduledAtIndex" - ON "$tableName"("pKind", "scheduledAt"); + ON "$tableName"("pKind", "scheduledAt", "id"); CREATE INDEX "${tableName}__LockUuidPlusIdIndex" ON "$tableName"("lockUuid", "id"); """, - ) + ), + // Migration 2: Update index to include 'id' column for deterministic ordering + Migration( + sql = + """ + BEGIN + EXECUTE IMMEDIATE 'DROP INDEX "${tableName}__KindPlusScheduledAtIndex"'; + EXCEPTION + WHEN OTHERS THEN + IF SQLCODE != -1418 THEN + RAISE; + END IF; + END; + / + CREATE INDEX "${tableName}__KindPlusScheduledAtIndex" + ON "$tableName"("pKind", "scheduledAt", "id"); + """, + needsExecution = { conn -> + // Check if index exists but doesn't have the 'id' column + val metadata = conn.underlying.metaData + var hasIndex = false + var hasIdColumn = false + + metadata.getIndexInfo(null, null, tableName, false, false).use { rs -> + while (rs.next()) { + val indexName = rs.getString("INDEX_NAME") + if (indexName == "${tableName}__KindPlusScheduledAtIndex") { + hasIndex = true + val columnName = rs.getString("COLUMN_NAME") + val ordinalPosition = rs.getShort("ORDINAL_POSITION") + // Check if 'id' is in position 3 + if (columnName == "id" && ordinalPosition == 3.toShort()) { + hasIdColumn = true + } + } + } + } + + // Run migration if index exists but doesn't have id column + hasIndex && !hasIdColumn + }, + ), ) } diff --git a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/postgres/PostgreSQLAdapter.kt b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/postgres/PostgreSQLAdapter.kt index 9b37a24..e8f8b0a 100644 --- a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/postgres/PostgreSQLAdapter.kt +++ b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/postgres/PostgreSQLAdapter.kt @@ -99,7 +99,7 @@ internal class PostgreSQLAdapter(driver: JdbcDriver, tableName: String) : "createdAt" FROM "$tableName" WHERE "pKind" = ? AND "scheduledAt" <= ? - ORDER BY "scheduledAt" + ORDER BY "scheduledAt", "id" LIMIT 1 FOR UPDATE SKIP LOCKED """ @@ -140,7 +140,7 @@ internal class PostgreSQLAdapter(driver: JdbcDriver, tableName: String) : FROM "$tableName" WHERE "pKind" = ? AND "scheduledAt" <= ? - ORDER BY "scheduledAt" + ORDER BY "scheduledAt", "id" LIMIT $limit FOR UPDATE SKIP LOCKED ) diff --git a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/postgres/PostgreSQLMigrations.kt b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/postgres/PostgreSQLMigrations.kt index cd6e8fc..5e46501 100644 --- a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/postgres/PostgreSQLMigrations.kt +++ b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/postgres/PostgreSQLMigrations.kt @@ -31,11 +31,44 @@ internal object PostgreSQLMigrations { ON "$tableName"("pKey", "pKind"); CREATE INDEX "${tableName}__KindPlusScheduledAtIndex" - ON "$tableName"("pKind", "scheduledAt"); + ON "$tableName"("pKind", "scheduledAt", "id"); CREATE INDEX "${tableName}__LockUuidPlusIdIndex" ON "$tableName"("lockUuid", "id"); """, - ) + ), + // Migration 2: Update index to include 'id' column for deterministic ordering + Migration( + sql = + """ + DROP INDEX IF EXISTS "${tableName}__KindPlusScheduledAtIndex"; + CREATE INDEX "${tableName}__KindPlusScheduledAtIndex" + ON "$tableName"("pKind", "scheduledAt", "id"); + """, + needsExecution = { conn -> + // Check if index exists but doesn't have the 'id' column + val metadata = conn.underlying.metaData + var hasIndex = false + var hasIdColumn = false + + metadata.getIndexInfo(null, null, tableName, false, false).use { rs -> + while (rs.next()) { + val indexName = rs.getString("INDEX_NAME") + if (indexName == "${tableName}__KindPlusScheduledAtIndex") { + hasIndex = true + val columnName = rs.getString("COLUMN_NAME") + val ordinalPosition = rs.getShort("ORDINAL_POSITION") + // Check if 'id' is in position 3 + if (columnName == "id" && ordinalPosition == 3.toShort()) { + hasIdColumn = true + } + } + } + } + + // Run migration if index exists but doesn't have id column + hasIndex && !hasIdColumn + }, + ), ) } diff --git a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/sqlite/SqliteAdapter.kt b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/sqlite/SqliteAdapter.kt index 43fdd3c..72c8d06 100644 --- a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/sqlite/SqliteAdapter.kt +++ b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/sqlite/SqliteAdapter.kt @@ -81,7 +81,7 @@ internal class SqliteAdapter(driver: JdbcDriver, tableName: String) : "createdAt" FROM "$tableName" WHERE "pKind" = ? AND "scheduledAt" <= ? - ORDER BY "scheduledAt" + ORDER BY "scheduledAt", "id" LIMIT 1 """ @@ -120,7 +120,7 @@ internal class SqliteAdapter(driver: JdbcDriver, tableName: String) : SELECT "id" FROM "$tableName" WHERE "pKind" = ? AND "scheduledAt" <= ? - ORDER BY "scheduledAt" + ORDER BY "scheduledAt", "id" LIMIT $limit ) """ diff --git a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/sqlite/SqliteMigrations.kt b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/sqlite/SqliteMigrations.kt index fca8d39..401c7f3 100644 --- a/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/sqlite/SqliteMigrations.kt +++ b/delayedqueue-jvm/src/main/kotlin/org/funfix/delayedqueue/jvm/internals/jdbc/sqlite/SqliteMigrations.kt @@ -31,11 +31,44 @@ internal object SqliteMigrations { ON $tableName (pKind, pKey); CREATE INDEX ${tableName}__KindPlusScheduledAtIndex - ON $tableName (pKind, scheduledAt); + ON $tableName (pKind, scheduledAt, id); CREATE INDEX ${tableName}__LockUuidPlusIdIndex ON $tableName (lockUuid, id); """, - ) + ), + // Migration 2: Update index to include 'id' column for deterministic ordering + Migration( + sql = + """ + DROP INDEX IF EXISTS ${tableName}__KindPlusScheduledAtIndex; + CREATE INDEX ${tableName}__KindPlusScheduledAtIndex + ON $tableName (pKind, scheduledAt, id); + """, + needsExecution = { conn -> + // Check if index exists but doesn't have the 'id' column + val metadata = conn.underlying.metaData + var hasIndex = false + var hasIdColumn = false + + metadata.getIndexInfo(null, null, tableName, false, false).use { rs -> + while (rs.next()) { + val indexName = rs.getString("INDEX_NAME") + if (indexName == "${tableName}__KindPlusScheduledAtIndex") { + hasIndex = true + val columnName = rs.getString("COLUMN_NAME") + val ordinalPosition = rs.getShort("ORDINAL_POSITION") + // Check if 'id' is in position 3 + if (columnName == "id" && ordinalPosition == 3.toShort()) { + hasIdColumn = true + } + } + } + } + + // Run migration if index exists but doesn't have id column + hasIndex && !hasIdColumn + }, + ), ) }