Skip to content

Commit 06e4b33

Browse files
committed
fix: properly handle NULL UserName in database operations
Add scanSignature helper to convert sql.NullString to string type. Update Create method to insert NULL for empty UserName values. Fix integration tests to work with string type instead of pointer.
1 parent d6dd362 commit 06e4b33

File tree

2 files changed

+49
-80
lines changed

2 files changed

+49
-80
lines changed

internal/infrastructure/database/repository.go

Lines changed: 46 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,56 @@ func NewSignatureRepository(db *sql.DB) *SignatureRepository {
1818
return &SignatureRepository{db: db}
1919
}
2020

21+
// scanSignature scans a row into a Signature, handling NULL values properly
22+
func scanSignature(scanner interface {
23+
Scan(dest ...interface{}) error
24+
}, signature *models.Signature) error {
25+
var userName sql.NullString
26+
err := scanner.Scan(
27+
&signature.ID,
28+
&signature.DocID,
29+
&signature.UserSub,
30+
&signature.UserEmail,
31+
&userName,
32+
&signature.SignedAtUTC,
33+
&signature.PayloadHash,
34+
&signature.Signature,
35+
&signature.Nonce,
36+
&signature.CreatedAt,
37+
&signature.Referer,
38+
&signature.PrevHash,
39+
)
40+
if err != nil {
41+
return err
42+
}
43+
// Convert sql.NullString to string (empty string if NULL)
44+
if userName.Valid {
45+
signature.UserName = userName.String
46+
} else {
47+
signature.UserName = ""
48+
}
49+
return nil
50+
}
51+
2152
func (r *SignatureRepository) Create(ctx context.Context, signature *models.Signature) error {
2253
query := `
2354
INSERT INTO signatures (doc_id, user_sub, user_email, user_name, signed_at, payload_hash, signature, nonce, referer, prev_hash)
2455
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
2556
RETURNING id, created_at
2657
`
2758

59+
// Convert empty string to NULL for user_name
60+
var userName sql.NullString
61+
if signature.UserName != "" {
62+
userName = sql.NullString{String: signature.UserName, Valid: true}
63+
}
64+
2865
err := r.db.QueryRowContext(
2966
ctx, query,
3067
signature.DocID,
3168
signature.UserSub,
3269
signature.UserEmail,
33-
signature.UserName,
70+
userName,
3471
signature.SignedAtUTC,
3572
signature.PayloadHash,
3673
signature.Signature,
@@ -49,25 +86,12 @@ func (r *SignatureRepository) Create(ctx context.Context, signature *models.Sign
4986
func (r *SignatureRepository) GetByDocAndUser(ctx context.Context, docID, userSub string) (*models.Signature, error) {
5087
query := `
5188
SELECT id, doc_id, user_sub, user_email, user_name, signed_at, payload_hash, signature, nonce, created_at, referer, prev_hash
52-
FROM signatures
89+
FROM signatures
5390
WHERE doc_id = $1 AND user_sub = $2
5491
`
5592

5693
signature := &models.Signature{}
57-
err := r.db.QueryRowContext(ctx, query, docID, userSub).Scan(
58-
&signature.ID,
59-
&signature.DocID,
60-
&signature.UserSub,
61-
&signature.UserEmail,
62-
&signature.UserName,
63-
&signature.SignedAtUTC,
64-
&signature.PayloadHash,
65-
&signature.Signature,
66-
&signature.Nonce,
67-
&signature.CreatedAt,
68-
&signature.Referer,
69-
&signature.PrevHash,
70-
)
94+
err := scanSignature(r.db.QueryRowContext(ctx, query, docID, userSub), signature)
7195

7296
if err != nil {
7397
if errors.Is(err, sql.ErrNoRows) {
@@ -98,21 +122,7 @@ func (r *SignatureRepository) GetByDoc(ctx context.Context, docID string) ([]*mo
98122
var signatures []*models.Signature
99123
for rows.Next() {
100124
signature := &models.Signature{}
101-
err := rows.Scan(
102-
&signature.ID,
103-
&signature.DocID,
104-
&signature.UserSub,
105-
&signature.UserEmail,
106-
&signature.UserName,
107-
&signature.SignedAtUTC,
108-
&signature.PayloadHash,
109-
&signature.Signature,
110-
&signature.Nonce,
111-
&signature.CreatedAt,
112-
&signature.Referer,
113-
&signature.PrevHash,
114-
)
115-
if err != nil {
125+
if err := scanSignature(rows, signature); err != nil {
116126
continue
117127
}
118128
signatures = append(signatures, signature)
@@ -140,21 +150,7 @@ func (r *SignatureRepository) GetByUser(ctx context.Context, userSub string) ([]
140150
var signatures []*models.Signature
141151
for rows.Next() {
142152
signature := &models.Signature{}
143-
err := rows.Scan(
144-
&signature.ID,
145-
&signature.DocID,
146-
&signature.UserSub,
147-
&signature.UserEmail,
148-
&signature.UserName,
149-
&signature.SignedAtUTC,
150-
&signature.PayloadHash,
151-
&signature.Signature,
152-
&signature.Nonce,
153-
&signature.CreatedAt,
154-
&signature.Referer,
155-
&signature.PrevHash,
156-
)
157-
if err != nil {
153+
if err := scanSignature(rows, signature); err != nil {
158154
continue
159155
}
160156
signatures = append(signatures, signature)
@@ -195,26 +191,13 @@ func (r *SignatureRepository) CheckUserSignatureStatus(ctx context.Context, docI
195191
func (r *SignatureRepository) GetLastSignature(ctx context.Context) (*models.Signature, error) {
196192
query := `
197193
SELECT id, doc_id, user_sub, user_email, user_name, signed_at, payload_hash, signature, nonce, created_at, referer, prev_hash
198-
FROM signatures
199-
ORDER BY id DESC
194+
FROM signatures
195+
ORDER BY id DESC
200196
LIMIT 1
201197
`
202198

203199
signature := &models.Signature{}
204-
err := r.db.QueryRowContext(ctx, query).Scan(
205-
&signature.ID,
206-
&signature.DocID,
207-
&signature.UserSub,
208-
&signature.UserEmail,
209-
&signature.UserName,
210-
&signature.SignedAtUTC,
211-
&signature.PayloadHash,
212-
&signature.Signature,
213-
&signature.Nonce,
214-
&signature.CreatedAt,
215-
&signature.Referer,
216-
&signature.PrevHash,
217-
)
200+
err := scanSignature(r.db.QueryRowContext(ctx, query), signature)
218201

219202
if err != nil {
220203
if errors.Is(err, sql.ErrNoRows) {
@@ -243,21 +226,7 @@ func (r *SignatureRepository) GetAllSignaturesOrdered(ctx context.Context) ([]*m
243226
var signatures []*models.Signature
244227
for rows.Next() {
245228
signature := &models.Signature{}
246-
err := rows.Scan(
247-
&signature.ID,
248-
&signature.DocID,
249-
&signature.UserSub,
250-
&signature.UserEmail,
251-
&signature.UserName,
252-
&signature.SignedAtUTC,
253-
&signature.PayloadHash,
254-
&signature.Signature,
255-
&signature.Nonce,
256-
&signature.CreatedAt,
257-
&signature.Referer,
258-
&signature.PrevHash,
259-
)
260-
if err != nil {
229+
if err := scanSignature(rows, signature); err != nil {
261230
continue
262231
}
263232
signatures = append(signatures, signature)

internal/infrastructure/database/repository_constraints_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func TestRepository_DatabaseConstraints_Integration(t *testing.T) {
6262
{
6363
name: "valid signature with nulls",
6464
modifyFn: func(s *models.Signature) {
65-
s.UserName = nil
65+
s.UserName = ""
6666
s.Referer = nil
6767
s.PrevHash = nil
6868
},
@@ -403,7 +403,7 @@ func TestRepository_EdgeCases_Integration(t *testing.T) {
403403
// Test with empty strings for nullable fields
404404
sig := factory.CreateValidSignature()
405405
emptyString := ""
406-
sig.UserName = &emptyString
406+
sig.UserName = emptyString
407407
sig.Referer = &emptyString
408408
sig.PrevHash = &emptyString
409409

@@ -418,7 +418,7 @@ func TestRepository_EdgeCases_Integration(t *testing.T) {
418418
}
419419

420420
// Verify empty strings are preserved (not converted to NULL)
421-
if result.UserName == nil || *result.UserName != "" {
421+
if result.UserName != "" {
422422
t.Error("Empty string UserName not preserved")
423423
}
424424
if result.Referer == nil || *result.Referer != "" {

0 commit comments

Comments
 (0)