5 files changed,
40 insertions(+),
11 deletions(-)
Author:
Olexandr Smirnov
ss2316544@gmail.com
Committed by:
GitHub
noreply@github.com
Committed at:
2025-08-11 19:39:03 +0300
Parent:
e771742
M
e2e/e2e_utils_db_test.go
@@ -1,6 +1,7 @@
package e2e_test import ( + "database/sql" "errors" "time"@@ -8,6 +9,7 @@ "github.com/gofrs/uuid/v5"
"github.com/henvic/pgq" "github.com/jackc/pgx/v5" "github.com/olexsmir/onasty/internal/models" + "github.com/olexsmir/onasty/internal/store/psqlutil" ) // getUserByEmail queries user from db by it's email@@ -114,12 +116,15 @@ Where(pgq.Eq{"slug": slug}).
SQL() e.require.NoError(err) + var readAt sql.NullTime var note models.Note err = e.postgresDB.QueryRow(e.ctx, query, args...). - Scan(¬e.ID, ¬e.Content, ¬e.Slug, ¬e.BurnBeforeExpiration, ¬e.Password, ¬e.ReadAt, ¬e.CreatedAt, ¬e.ExpiresAt) + Scan(¬e.ID, ¬e.Content, ¬e.Slug, ¬e.BurnBeforeExpiration, ¬e.Password, &readAt, ¬e.CreatedAt, ¬e.ExpiresAt) if errors.Is(err, pgx.ErrNoRows) { return models.Note{} //nolint:exhaustruct } + + note.ReadAt = psqlutil.NullTimeToTime(readAt) e.require.NoError(err) return note
M
internal/store/psql/noterepo/noterepo.go
@@ -2,6 +2,7 @@ package noterepo
import ( "context" + "database/sql" "errors" "time"@@ -111,12 +112,14 @@ return models.Note{}, err
} var note models.Note + var readAt sql.NullTime err = s.db.QueryRow(ctx, query, args...). - Scan(¬e.Content, ¬e.Slug, ¬e.BurnBeforeExpiration, ¬e.ReadAt, ¬e.CreatedAt, ¬e.ExpiresAt) - + Scan(¬e.Content, ¬e.Slug, ¬e.BurnBeforeExpiration, &readAt, ¬e.CreatedAt, ¬e.ExpiresAt) if errors.Is(err, pgx.ErrNoRows) { return models.Note{}, models.ErrNoteNotFound } + + note.ReadAt = psqlutil.NullTimeToTime(readAt) return note, err }@@ -131,14 +134,14 @@ from notes n
where slug = $1 ` - var readAt time.Time + var readAt sql.NullTime var metadata dtos.NoteMetadata err := s.db.QueryRow(ctx, query, slug).Scan(&metadata.CreatedAt, &metadata.HasPassword, &readAt) if errors.Is(err, pgx.ErrNoRows) { return dtos.NoteMetadata{}, models.ErrNoteNotFound } - if !readAt.IsZero() { + if !psqlutil.NullTimeToTime(readAt).IsZero() { return dtos.NoteMetadata{}, models.ErrNoteNotFound }@@ -165,10 +168,13 @@
var notes []models.Note for rows.Next() { var note models.Note + var readAt sql.NullTime if err := rows.Scan(¬e.Content, ¬e.Slug, ¬e.BurnBeforeExpiration, ¬e.Password, - ¬e.ReadAt, ¬e.CreatedAt, ¬e.ExpiresAt); err != nil { + &readAt, ¬e.CreatedAt, ¬e.ExpiresAt); err != nil { return nil, err } + + note.ReadAt = psqlutil.NullTimeToTime(readAt) notes = append(notes, note) }@@ -207,12 +213,15 @@ return models.Note{}, err
} var note models.Note + var readAt sql.NullTime err = s.db.QueryRow(ctx, query, args...). - Scan(¬e.Content, ¬e.Slug, ¬e.BurnBeforeExpiration, ¬e.ReadAt, ¬e.CreatedAt, ¬e.ExpiresAt) + Scan(¬e.Content, ¬e.Slug, ¬e.BurnBeforeExpiration, &readAt, ¬e.CreatedAt, ¬e.ExpiresAt) if errors.Is(err, pgx.ErrNoRows) { return models.Note{}, models.ErrNoteNotFound } + + note.ReadAt = psqlutil.NullTimeToTime(readAt) return note, err }@@ -255,10 +264,8 @@ query, args, err := pgq.
Update("notes"). Set("content", ""). Set("read_at", readAt). - Where(pgq.Eq{ - "slug": slug, - "read_at": time.Time{}, // check if time is null - }). + Where(pgq.Eq{"slug": slug}). + Where("read_at is null"). SQL() if err != nil { return err
M
internal/store/psqlutil/psqlutil.go
@@ -2,7 +2,9 @@ package psqlutil
import ( "context" + "database/sql" "errors" + "time" "github.com/jackc/pgx/v5/pgconn" "github.com/jackc/pgx/v5/pgxpool"@@ -39,3 +41,12 @@ pgErr.ConstraintName == constraintName
} return false } + +// NullTimeToTime converts sql.NullTime to time.Time. +// Returns zero [time.Time] if NullTime is not valid. +func NullTimeToTime(t sql.NullTime) time.Time { + if t.Valid { + return t.Time + } + return time.Time{} +}
A
migrations/20250811132505_note_nullable_read_at.down.sql
@@ -0,0 +1,3 @@
+ALTER TABLE notes + ALTER COLUMN "read_at" SET NOT NULL, + ALTER COLUMN "read_at" SET DEFAULT '0001-01-01 00:00:00'::timestamptz;
A
migrations/20250811132505_note_nullable_read_at.up.sql
@@ -0,0 +1,3 @@
+ALTER TABLE notes + ALTER COLUMN "read_at" DROP NOT NULL, + ALTER COLUMN "read_at" SET DEFAULT NULL;