From 639e081ca7c22f28d9ddf290d39f2e013aed2cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=92=E6=9F=A0?= <865274218@qq.com> Date: Sat, 15 Aug 2026 18:10:43 +0800 Subject: [PATCH] fix(namespace): stop the backfill from querying with a null keyword The backfill preview returned 500 on PostgreSQL: SQLState 42883: function lower(bytea) does not exist It reused UserAccountRepository.search(keyword, status, pageable) with a null keyword. That query compares the keyword with lower(...), and a null bind leaves PostgreSQL to infer the parameter type as bytea, so lower() has no matching signature. Nothing had exercised that branch before: the admin user list goes through AdminUserSearchRepository, and the member-candidate lookup always passes a real keyword. The backfill was the first caller to pass null. Give callers that want every account in a status a query without a keyword to bind, rather than papering over the null with a cast or an empty string. Neither test layer would have caught this. The unit tests mock the repository, and the integration tests run on H2 in PostgreSQL mode, which accepts the null-typed bind that PostgreSQL rejects. Verified instead against a real PostgreSQL: preview, apply, and a second preview showing alreadyProvisioned with nothing left to do, with namespace_member rows confirming each owner holds OWNER on a TEAM namespace. --- .../PersonalNamespaceProvisioningService.java | 2 +- .../skillhub/domain/user/UserAccountRepository.java | 11 +++++++++++ .../PersonalNamespaceProvisioningServiceTest.java | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/namespace/PersonalNamespaceProvisioningService.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/namespace/PersonalNamespaceProvisioningService.java index b5b3bf81..8c42bac9 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/namespace/PersonalNamespaceProvisioningService.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/namespace/PersonalNamespaceProvisioningService.java @@ -141,7 +141,7 @@ public class PersonalNamespaceProvisioningService { boolean truncated = false; for (int page = 0; !truncated; page++) { - Page batch = userAccountRepository.search(null, UserStatus.ACTIVE, + Page batch = userAccountRepository.findByStatus(UserStatus.ACTIVE, PageRequest.of(page, BACKFILL_PAGE_SIZE, Sort.by("id"))); if (batch.isEmpty()) { break; diff --git a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/user/UserAccountRepository.java b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/user/UserAccountRepository.java index c0f4f295..bcfbb3b3 100644 --- a/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/user/UserAccountRepository.java +++ b/server/skillhub-domain/src/main/java/com/iflytek/skillhub/domain/user/UserAccountRepository.java @@ -14,5 +14,16 @@ public interface UserAccountRepository { List findByIdIn(List ids); Optional findByEmailIgnoreCase(String email); Page search(String keyword, UserStatus status, Pageable pageable); + + /** + * Lists accounts in one status. + * + *

Separate from {@link #search} on purpose: that query compares the keyword with + * {@code lower(...)}, and passing a null keyword leaves PostgreSQL to infer the bind type as + * {@code bytea}, which fails with "function lower(bytea) does not exist". Callers that want + * every account in a status have no keyword to give, so they get a query without one. + */ + Page findByStatus(UserStatus status, Pageable pageable); + UserAccount save(UserAccount user); } diff --git a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/namespace/PersonalNamespaceProvisioningServiceTest.java b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/namespace/PersonalNamespaceProvisioningServiceTest.java index 559ff271..e3b8503a 100644 --- a/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/namespace/PersonalNamespaceProvisioningServiceTest.java +++ b/server/skillhub-domain/src/test/java/com/iflytek/skillhub/domain/namespace/PersonalNamespaceProvisioningServiceTest.java @@ -63,7 +63,7 @@ class PersonalNamespaceProvisioningServiceTest { } private void directoryContains(UserAccount... users) { - when(userAccountRepository.search(isNull(), eq(UserStatus.ACTIVE), any())) + when(userAccountRepository.findByStatus(eq(UserStatus.ACTIVE), any())) .thenReturn(new PageImpl<>(List.of(users))); }