Skip to content

Commit 0d4293b

Browse files
jpicklykclaude
andcommitted
fix: use HEX() for short UUID prefix lookup in SQLite
CAST(blob AS VARCHAR) produces raw bytes in SQLite, not a formatted UUID string, causing prefix lookups to always return empty results. Switch to LOWER(HEX(id)) for SQLite and LOWER(RAWTOHEX(id)) for H2, which both produce dashless lowercase hex strings suitable for LIKE prefix matching. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b21c012 commit 0d4293b

File tree

1 file changed

+14
-26
lines changed

1 file changed

+14
-26
lines changed

current/src/main/kotlin/io/github/jpicklyk/mcptask/current/infrastructure/repository/SQLiteWorkItemRepository.kt

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import io.github.jpicklyk.mcptask.current.domain.repository.Result
88
import io.github.jpicklyk.mcptask.current.domain.repository.WorkItemRepository
99
import io.github.jpicklyk.mcptask.current.infrastructure.database.DatabaseManager
1010
import io.github.jpicklyk.mcptask.current.infrastructure.database.schema.WorkItemsTable
11+
import org.jetbrains.exposed.v1.core.CustomFunction
12+
import org.jetbrains.exposed.v1.core.LowerCase
1113
import org.jetbrains.exposed.v1.core.Op
1214
import org.jetbrains.exposed.v1.core.ResultRow
1315
import org.jetbrains.exposed.v1.core.SortOrder
@@ -18,9 +20,10 @@ import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.lessEq
1820
import org.jetbrains.exposed.v1.core.SqlExpressionBuilder.like
1921
import org.jetbrains.exposed.v1.core.VarCharColumnType
2022
import org.jetbrains.exposed.v1.core.and
21-
import org.jetbrains.exposed.v1.core.castTo
2223
import org.jetbrains.exposed.v1.core.dao.id.EntityID
2324
import org.jetbrains.exposed.v1.core.or
25+
import org.jetbrains.exposed.v1.core.vendors.H2Dialect
26+
import org.jetbrains.exposed.v1.core.vendors.currentDialect
2427
import org.jetbrains.exposed.v1.jdbc.Query
2528
import org.jetbrains.exposed.v1.jdbc.deleteWhere
2629
import org.jetbrains.exposed.v1.jdbc.insert
@@ -364,40 +367,25 @@ class SQLiteWorkItemRepository(
364367
prefix: String,
365368
limit: Int
366369
): Result<List<WorkItem>> {
367-
// Convert a hex-only prefix into a UUID-formatted prefix for LIKE matching.
368-
// UUID format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx (8-4-4-4-12)
369-
// Dash positions in the hex string: after 8, 12, 16, 20
370-
val formattedPrefix = formatHexAsUuidPrefix(prefix.lowercase())
370+
val normalizedPrefix = prefix.lowercase()
371371
return databaseManager.suspendedTransaction("Failed to find WorkItems by ID prefix") {
372+
// Convert UUID id column to lowercase hex string (no dashes) for prefix matching.
373+
// H2 uses RAWTOHEX() for native UUID columns; SQLite uses HEX() for BLOB columns.
374+
// Both produce uppercase hex without dashes — wrap in LOWER for case-insensitive match.
375+
val hexFunctionName = if (currentDialect is H2Dialect) "RAWTOHEX" else "HEX"
376+
val hexId = LowerCase(
377+
CustomFunction(hexFunctionName, VarCharColumnType(32), WorkItemsTable.id)
378+
)
372379
val items =
373380
WorkItemsTable
374381
.selectAll()
375-
.where {
376-
WorkItemsTable.id.castTo<String>(VarCharColumnType(36)).like("$formattedPrefix%")
377-
}.limit(limit)
382+
.where { hexId like "$normalizedPrefix%" }
383+
.limit(limit)
378384
.mapNotNull { toWorkItemOrNull(it) }
379385
Result.Success(items)
380386
}
381387
}
382388

383-
/**
384-
* Converts a hex-only prefix string into UUID-formatted prefix with dashes
385-
* inserted at the correct positions (after hex positions 8, 12, 16, 20).
386-
*/
387-
private fun formatHexAsUuidPrefix(hexPrefix: String): String {
388-
val dashPositions = listOf(8, 12, 16, 20)
389-
val sb = StringBuilder()
390-
var hexIndex = 0
391-
for (ch in hexPrefix) {
392-
if (hexIndex in dashPositions) {
393-
sb.append('-')
394-
}
395-
sb.append(ch)
396-
hexIndex++
397-
}
398-
return sb.toString()
399-
}
400-
401389
override suspend fun findAncestorChains(itemIds: Set<UUID>): Result<Map<UUID, List<WorkItem>>> {
402390
if (itemIds.isEmpty()) return Result.Success(emptyMap())
403391
return databaseManager.suspendedTransaction("Failed to find ancestor chains") {

0 commit comments

Comments
 (0)