From 5107ce01917e01a87c630fa47e0700a35c001b70 Mon Sep 17 00:00:00 2001 From: Ralph Slooten Date: Sat, 14 Jun 2025 11:52:11 +1200 Subject: [PATCH] Fix: Use float64 for returned SQL value types for rqlite compatibility (#520) The goqlite library is designed to be flexible and does not make assumptions about the types of JSON values returned from rqlite, using the type `any` for variables. When a numeric value is received in the response, the `any` type does not specify a numeric type, leading Go to default to using `float64`. --- internal/storage/cron.go | 6 +++--- internal/storage/database.go | 17 ++++++++--------- internal/storage/messages.go | 14 ++++++++------ internal/storage/search.go | 22 +++++++++++----------- internal/storage/settings.go | 8 ++++---- internal/storage/tags.go | 4 ++-- 6 files changed, 36 insertions(+), 35 deletions(-) diff --git a/internal/storage/cron.go b/internal/storage/cron.go index 93707cf..f132325 100644 --- a/internal/storage/cron.go +++ b/internal/storage/cron.go @@ -57,7 +57,7 @@ func pruneMessages() { ids := []string{} var prunedSize uint64 - var size uint64 + var size float64 // use float64 for rqlite compatibility // prune using `--max` if set if config.MaxMessages > 0 { @@ -81,7 +81,7 @@ func pruneMessages() { return } ids = append(ids, id) - prunedSize = prunedSize + size + prunedSize = prunedSize + uint64(size) }); err != nil { logger.Log().Errorf("[db] %s", err.Error()) @@ -110,7 +110,7 @@ func pruneMessages() { if !tools.InArray(id, ids) { ids = append(ids, id) - prunedSize = prunedSize + size + prunedSize = prunedSize + uint64(size) } }); err != nil { diff --git a/internal/storage/database.go b/internal/storage/database.go index 3a1e0e4..73bd446 100644 --- a/internal/storage/database.go +++ b/internal/storage/database.go @@ -211,51 +211,50 @@ func StatsGet() MailboxStats { // CountTotal returns the number of emails in the database func CountTotal() uint64 { - var total uint64 + var total float64 // use float64 for rqlite compatibility _ = sqlf.From(tenant("mailbox")). Select("COUNT(*)").To(&total). QueryRowAndClose(context.TODO(), db) - return total + return uint64(total) } // CountUnread returns the number of emails in the database that are unread. func CountUnread() uint64 { - var total uint64 + var total float64 // use float64 for rqlite compatibility _ = sqlf.From(tenant("mailbox")). Select("COUNT(*)").To(&total). Where("Read = ?", 0). QueryRowAndClose(context.TODO(), db) - return total + return uint64(total) } // CountRead returns the number of emails in the database that are read. func CountRead() uint64 { - var total uint64 + var total float64 // use float64 for rqlite compatibility _ = sqlf.From(tenant("mailbox")). Select("COUNT(*)").To(&total). Where("Read = ?", 1). QueryRowAndClose(context.TODO(), db) - return total + return uint64(total) } // DbSize returns the size of the SQLite database. func DbSize() uint64 { - var total sql.NullInt64 + var total sql.NullFloat64 // use float64 for rqlite compatibility err := db.QueryRow("SELECT page_count * page_size AS size FROM pragma_page_count(), pragma_page_size()").Scan(&total) if err != nil { logger.Log().Errorf("[db] %s", err.Error()) - return uint64(total.Int64) } - return uint64(total.Int64) + return uint64(total.Float64) } // MessageIDExists checks whether a Message-ID exists in the DB diff --git a/internal/storage/messages.go b/internal/storage/messages.go index e23192f..3a11089 100644 --- a/internal/storage/messages.go +++ b/internal/storage/messages.go @@ -201,12 +201,12 @@ func List(start int, beforeTS int64, limit int) ([]MessageSummary, error) { } if err := q.QueryAndClose(context.TODO(), db, func(row *sql.Rows) { - var created uint64 + var created float64 // use float64 for rqlite compatibility var id string var messageID string var subject string var metadata string - var size uint64 + var size float64 // use float64 for rqlite compatibility var attachments int var read int var snippet string @@ -226,7 +226,7 @@ func List(start int, beforeTS int64, limit int) ([]MessageSummary, error) { em.ID = id em.MessageID = messageID em.Subject = subject - em.Size = size + em.Size = uint64(size) em.Attachments = attachments em.Read = read == 1 em.Snippet = snippet @@ -294,7 +294,7 @@ func GetMessage(id string) (*Message, error) { Where(`ID = ?`, id) if err := q.QueryAndClose(context.TODO(), db, func(row *sql.Rows) { - var created uint64 + var created float64 // use float64 for rqlite compatibility if err := row.Scan(&created); err != nil { logger.Log().Errorf("[db] %s", err.Error()) @@ -621,12 +621,14 @@ func DeleteMessages(ids []string) error { for rows.Next() { var id string - var size uint64 + var size float64 // use float64 for rqlite compatibility + if err := rows.Scan(&id, &size); err != nil { return err } + toDelete = append(toDelete, id) - totalSize = totalSize + size + totalSize = totalSize + uint64(size) } if err = rows.Err(); err != nil { diff --git a/internal/storage/search.go b/internal/storage/search.go index 9dc1cb3..ffd2c06 100644 --- a/internal/storage/search.go +++ b/internal/storage/search.go @@ -39,12 +39,12 @@ func Search(search, timezone string, start int, beforeTS int64, limit int) ([]Me var err error if err := q.QueryAndClose(context.TODO(), db, func(row *sql.Rows) { - var created uint64 + var created float64 // use float64 for rqlite compatibility var id string var messageID string var subject string var metadata string - var size uint64 + var size float64 // use float64 for rqlite compatibility var attachments int var snippet string var read int @@ -65,7 +65,7 @@ func Search(search, timezone string, start int, beforeTS int64, limit int) ([]Me em.ID = id em.MessageID = messageID em.Subject = subject - em.Size = size + em.Size = uint64(size) em.Attachments = attachments em.Read = read == 1 em.Snippet = snippet @@ -111,7 +111,7 @@ func SearchUnreadCount(search, timezone string, beforeTS int64) (int64, error) { q = q.Where(`Created < ?`, beforeTS) } - var unread int64 + var unread float64 // use float64 for rqlite compatibility q = q.Where("Read = 0").Select(`COUNT(*)`) @@ -128,9 +128,9 @@ func SearchUnreadCount(search, timezone string, beforeTS int64) (int64, error) { elapsed := time.Since(tsStart) - logger.Log().Debugf("[db] counted %d unread for \"%s\" in %s", unread, search, elapsed) + logger.Log().Debugf("[db] counted %d unread for \"%s\" in %s", int64(unread), search, elapsed) - return unread, err + return int64(unread), err } // DeleteSearch will delete all messages for search terms. @@ -144,12 +144,12 @@ func DeleteSearch(search, timezone string) error { deleteSize := uint64(0) if err := q.QueryAndClose(context.TODO(), db, func(row *sql.Rows) { - var created uint64 + var created float64 // use float64 for rqlite compatibility var id string var messageID string var subject string var metadata string - var size uint64 + var size float64 // use float64 for rqlite compatibility var attachments int var read int var snippet string @@ -161,7 +161,7 @@ func DeleteSearch(search, timezone string) error { } ids = append(ids, id) - deleteSize = deleteSize + size + deleteSize = deleteSize + uint64(size) }); err != nil { return err } @@ -264,12 +264,12 @@ func SetSearchReadStatus(search, timezone string, read bool) error { ids := []string{} if err := q.QueryAndClose(context.TODO(), db, func(row *sql.Rows) { - var created uint64 + var created float64 // use float64 for rqlite compatibility var id string var messageID string var subject string var metadata string - var size uint64 + var size float64 // use float64 for rqlite compatibility var attachments int var read int var snippet string diff --git a/internal/storage/settings.go b/internal/storage/settings.go index 81a6bf1..5d3660f 100644 --- a/internal/storage/settings.go +++ b/internal/storage/settings.go @@ -36,7 +36,7 @@ func SettingPut(k, v string) error { // The total deleted message size as an int64 value func getDeletedSize() uint64 { - var result sql.NullInt64 + var result sql.NullFloat64 // use float64 for rqlite compatibility err := sqlf.From(tenant("settings")). Select("Value").To(&result). Where("Key = ?", "DeletedSize"). @@ -47,12 +47,12 @@ func getDeletedSize() uint64 { return 0 } - return uint64(result.Int64) + return uint64(result.Float64) } // The total raw non-compressed messages size in bytes of all messages in the database func totalMessagesSize() uint64 { - var result sql.NullInt64 + var result sql.NullFloat64 err := sqlf.From(tenant("mailbox")). Select("SUM(Size)").To(&result). QueryAndClose(context.TODO(), db, func(row *sql.Rows) {}) @@ -61,7 +61,7 @@ func totalMessagesSize() uint64 { return 0 } - return uint64(result.Int64) + return uint64(result.Float64) } // AddDeletedSize will add the value to the DeletedSize setting diff --git a/internal/storage/tags.go b/internal/storage/tags.go index 9605bc5..6fff53a 100644 --- a/internal/storage/tags.go +++ b/internal/storage/tags.go @@ -171,7 +171,7 @@ func GetAllTags() []string { func GetAllTagsCount() map[string]int64 { var tags = make(map[string]int64) var name string - var total int64 + var total float64 // use float64 for rqlite compatibility if err := sqlf. Select(`Name`).To(&name). @@ -181,7 +181,7 @@ func GetAllTagsCount() map[string]int64 { GroupBy(tenant("message_tags.TagID")). OrderBy("Name"). QueryAndClose(context.TODO(), db, func(row *sql.Rows) { - tags[name] = total + tags[name] = int64(total) }); err != nil { logger.Log().Errorf("[db] %s", err.Error()) }