Fix family-member POST: null JPQL param caused bigint <> varchar SQL error

The relationship-uniqueness query used (:excludeId IS NULL OR fm.id <> :excludeId).
On create, excludeId is null and PostgreSQL parses the parameter as varchar before
the IS NULL branch can short-circuit, so adding a family member returned 403 with
"operator does not exist: bigint <> character varying". Split into two queries so
neither has a nullable Long parameter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-05-19 09:40:14 +05:30
co-authored by Claude Opus 4.7
parent 45a041b023
commit 026e5fb875
2 changed files with 19 additions and 7 deletions
@@ -28,16 +28,27 @@ public interface ClientFamilyMembersRepository
extends JpaRepository<ClientFamilyMembers, Long>, JpaSpecificationExecutor<ClientFamilyMembers> {
/**
* Counts non-deleted family members for a client that hold a given relationship code, optionally
* ignoring one specific family member (used at update time so a row doesn't conflict with itself).
* Backs the single-use relationship rule for Mother / Father / Spouse.
* Counts non-deleted family members for a client that hold a given relationship code.
* Backs the single-use relationship rule for Mother / Father / Spouse on create.
*/
@Query("SELECT COUNT(fm) FROM ClientFamilyMembers fm "
+ "WHERE fm.client.id = :clientId "
+ "AND fm.relationship.id = :relationshipId "
+ "AND fm.isDeleted = false "
+ "AND (:excludeFamilyMemberId IS NULL OR fm.id <> :excludeFamilyMemberId)")
+ "AND fm.isDeleted = false")
long countActiveByClientAndRelationship(@Param("clientId") Long clientId,
@Param("relationshipId") Long relationshipId);
// Update variant: excludes one specific row so it doesn't conflict with itself.
// Why: PostgreSQL parses parameter types at parse-time; a nullable Long parameter compared with
// <> against a bigint column gets bound as varchar when null, producing
// "operator does not exist: bigint <> character varying". Splitting create vs. update keeps
// both queries non-nullable.
@Query("SELECT COUNT(fm) FROM ClientFamilyMembers fm "
+ "WHERE fm.client.id = :clientId "
+ "AND fm.relationship.id = :relationshipId "
+ "AND fm.isDeleted = false "
+ "AND fm.id <> :excludeFamilyMemberId")
long countActiveByClientAndRelationshipExcluding(@Param("clientId") Long clientId,
@Param("relationshipId") Long relationshipId,
@Param("excludeFamilyMemberId") Long excludeFamilyMemberId);
@@ -99,8 +99,9 @@ public class ClientFamilyMembersWritePlatformServiceImpl implements ClientFamily
if (!SINGLE_USE_RELATIONSHIPS.contains(label)) {
return;
}
final long existing = clientFamilyRepository.countActiveByClientAndRelationship(
clientId, relationship.getId(), excludeMemberId);
final long existing = (excludeMemberId == null)
? clientFamilyRepository.countActiveByClientAndRelationship(clientId, relationship.getId())
: clientFamilyRepository.countActiveByClientAndRelationshipExcluding(clientId, relationship.getId(), excludeMemberId);
if (existing > 0) {
final String displayName = relationship.getLabel();
throw new PlatformApiDataValidationException(java.util.List.of(ApiParameterError.parameterError(