diff --git a/benchmarks/gluon_bench/imap_benchmarks/select_fetch.go b/benchmarks/gluon_bench/imap_benchmarks/select_fetch.go new file mode 100644 index 00000000..b2348992 --- /dev/null +++ b/benchmarks/gluon_bench/imap_benchmarks/select_fetch.go @@ -0,0 +1,72 @@ +package imap_benchmarks + +import ( + "context" + "flag" + "github.com/emersion/go-imap" + "net" + + "github.com/ProtonMail/gluon/benchmarks/gluon_bench/benchmark" + "github.com/bradenaw/juniper/xslices" + "github.com/emersion/go-imap/client" +) + +var ( + selectFetchRepetitionsFlag = flag.Uint("imap-select-fetch-repeat", 50, "Number of times to repeat the request.") +) + +type SelectFetch struct { + *stateTracker + mboxInfo []MailboxInfo +} + +func NewSelectFetch() benchmark.Benchmark { + return NewIMAPBenchmarkRunner(&SelectFetch{stateTracker: newStateTracker()}) +} + +func (*SelectFetch) Name() string { + return "imap-select-fetch" +} + +func (e *SelectFetch) Setup(ctx context.Context, addr net.Addr) error { + return WithClient(addr, func(cl *client.Client) error { + for i := uint(0); i < *selectFetchRepetitionsFlag; i++ { + if _, err := e.createAndFillRandomMBox(cl); err != nil { + return err + } + } + + e.mboxInfo = xslices.Map(e.MBoxes, func(m string) MailboxInfo { + return MailboxInfo{Name: m, ReadOnly: false} + }) + return nil + }) +} + +func (e *SelectFetch) TearDown(ctx context.Context, addr net.Addr) error { + return e.cleanupWithAddr(addr) +} + +func (e *SelectFetch) Run(ctx context.Context, addr net.Addr) error { + RunParallelClients(addr, func(cl *client.Client, u uint) { + for i := uint(0); i < *selectFetchRepetitionsFlag; i++ { + if _, err := cl.Select(e.mboxInfo[i].Name, e.mboxInfo[i].ReadOnly); err != nil { + panic(err) + } + + if err := FetchMessage(cl, NewSequenceSetAll(), imap.FetchUid, imap.FetchFlags); err != nil { + panic(err) + } + + if err := cl.Unselect(); err != nil { + panic(err) + } + } + }) + + return nil +} + +func init() { + benchmark.RegisterBenchmark(NewSelectFetch()) +} diff --git a/benchmarks/gluon_bench/utils/messages.go b/benchmarks/gluon_bench/utils/messages.go index dd37940e..ed573277 100644 --- a/benchmarks/gluon_bench/utils/messages.go +++ b/benchmarks/gluon_bench/utils/messages.go @@ -65,6 +65,7 @@ dGhpcyBpcyBteSBhdHRhY2htZW50Cg== const MessageAfterNoonMeeting = `Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST) From: Fred Foobar Subject: afternoon meeting +Date: Fri, 26 Mar 2021 20:01:23 +0100 To: mooch@owatagu.siam.edu Message-Id: MIME-Version: 1.0 @@ -77,6 +78,7 @@ const MessageEmbedded = `From: Nathaniel Borenstein To: Ned Freed Subject: Sample message MIME-Version: 1.0 +Date: Fri, 26 Mar 2021 20:01:23 +0100 Content-type: multipart/mixed; boundary="simple boundary" This is the preamble. It is to be ignored, though it diff --git a/db/ops_mailbox.go b/db/ops_mailbox.go index 1f4952be..9a3b35e1 100644 --- a/db/ops_mailbox.go +++ b/db/ops_mailbox.go @@ -22,7 +22,7 @@ type MailboxReadOps interface { GetMailboxMessageIDPairs(ctx context.Context, mboxID imap.InternalMailboxID) ([]MessageIDPair, error) - GetAllMailboxesWithAttr(ctx context.Context) ([]*Mailbox, error) + GetAllMailboxesWithAttr(ctx context.Context) ([]*MailboxWithAttr, error) GetAllMailboxesAsRemoteIDs(ctx context.Context) ([]imap.MailboxID, error) diff --git a/db/ops_message.go b/db/ops_message.go index 66d7531e..49eb29da 100644 --- a/db/ops_message.go +++ b/db/ops_message.go @@ -5,7 +5,6 @@ import ( "time" "github.com/ProtonMail/gluon/imap" - "github.com/bradenaw/juniper/xslices" ) type MessageReadOps interface { @@ -19,7 +18,7 @@ type MessageReadOps interface { GetMessageRemoteID(ctx context.Context, id imap.InternalMessageID) (imap.MessageID, error) - GetImportedMessageData(ctx context.Context, id imap.InternalMessageID) (*Message, error) + GetImportedMessageData(ctx context.Context, id imap.InternalMessageID) (*MessageWithFlags, error) GetMessageDateAndSize(ctx context.Context, id imap.InternalMessageID) (time.Time, int, error) @@ -39,7 +38,7 @@ type MessageReadOps interface { type MessageWriteOps interface { MessageReadOps - CreateMessages(ctx context.Context, reqs ...*CreateMessageReq) ([]*Message, error) + CreateMessages(ctx context.Context, reqs ...*CreateMessageReq) error CreateMessageAndAddToMailbox(ctx context.Context, mbox imap.InternalMailboxID, req *CreateMessageReq) (imap.UID, imap.FlagSet, error) @@ -74,19 +73,3 @@ type MessageFlagSet struct { RemoteID imap.MessageID FlagSet imap.FlagSet } - -func NewFlagSet(msgUID *UID, flags []*MessageFlag) imap.FlagSet { - flagSet := imap.NewFlagSetFromSlice(xslices.Map(flags, func(flag *MessageFlag) string { - return flag.Value - })) - - if msgUID.Deleted { - flagSet.AddToSelf(imap.FlagDeleted) - } - - if msgUID.Recent { - flagSet.AddToSelf(imap.FlagRecent) - } - - return flagSet -} diff --git a/db/types.go b/db/types.go index b11edb13..16f910bb 100644 --- a/db/types.go +++ b/db/types.go @@ -5,7 +5,6 @@ import ( "time" "github.com/ProtonMail/gluon/imap" - "github.com/bradenaw/juniper/xslices" ) type MailboxIDPair struct { @@ -86,27 +85,16 @@ type MailboxAttr struct { } type Mailbox struct { - ID imap.InternalMailboxID - RemoteID imap.MailboxID - Name string - UIDValidity imap.UID - Subscribed bool - Flags []*MailboxFlag - PermanentFlags []*MailboxFlag - Attributes []*MailboxAttr + ID imap.InternalMailboxID + RemoteID imap.MailboxID + Name string + UIDValidity imap.UID + Subscribed bool } -type MessageFlag struct { - ID int - Value string -} - -func MessageFlagsFromFlagSet(set imap.FlagSet) []*MessageFlag { - return xslices.Map(set.ToSlice(), func(t string) *MessageFlag { - return &MessageFlag{ - Value: t, - } - }) +type MailboxWithAttr struct { + Mailbox + Attributes imap.FlagSet } type Message struct { @@ -118,8 +106,11 @@ type Message struct { BodyStructure string Envelope string Deleted bool - Flags []*MessageFlag - UIDs []*UID +} + +type MessageWithFlags struct { + Message + Flags imap.FlagSet } type UID struct { diff --git a/internal/backend/connector_updates.go b/internal/backend/connector_updates.go index f966e9e9..d2100bdc 100644 --- a/internal/backend/connector_updates.go +++ b/internal/backend/connector_updates.go @@ -304,7 +304,7 @@ func (user *user) applyMessagesCreated(ctx context.Context, update *imap.Message } // Create message in the database - if _, err := tx.CreateMessages(ctx, xslices.Map(chunk, func(req *DBRequestWithLiteral) *db.CreateMessageReq { + if err := tx.CreateMessages(ctx, xslices.Map(chunk, func(req *DBRequestWithLiteral) *db.CreateMessageReq { return &req.CreateMessageReq })...); err != nil { return err @@ -686,10 +686,8 @@ func (user *user) applyMessageUpdated(ctx context.Context, update *imap.MessageU InternalID: newInternalID, } - if m, err := tx.CreateMessages(ctx, request); err != nil { + if err := tx.CreateMessages(ctx, request); err != nil { return err - } else if len(m) == 0 { - return fmt.Errorf("no messages were inserted") } if err := user.store.Set(newInternalID, literalReader); err != nil { diff --git a/internal/db_impl/sqlite3/migration_test.go b/internal/db_impl/sqlite3/migration_test.go index b1529a4b..f19cb0e6 100644 --- a/internal/db_impl/sqlite3/migration_test.go +++ b/internal/db_impl/sqlite3/migration_test.go @@ -100,33 +100,21 @@ func runAndValidateDB(t *testing.T, testDir, user string, testData *testData, ui { flags, err := rd.GetMailboxFlags(ctx, dbMBox.ID) require.NoError(t, err) - originalFlags := imap.NewFlagSet() - for _, f := range m.Flags { - originalFlags.AddToSelf(f.Value) - } - require.True(t, flags.Equals(originalFlags)) + require.True(t, flags.Equals(m.Flags)) } // Check Perm Flags. { flags, err := rd.GetMailboxPermanentFlags(ctx, dbMBox.ID) require.NoError(t, err) - originalFlags := imap.NewFlagSet() - for _, f := range m.PermanentFlags { - originalFlags.AddToSelf(f.Value) - } - require.True(t, flags.Equals(originalFlags)) + require.True(t, flags.Equals(m.PermanentFlags)) } // Check Attributes { attr, err := rd.GetMailboxAttributes(ctx, dbMBox.ID) require.NoError(t, err) - originalAttr := imap.NewFlagSet() - for _, f := range m.Attributes { - originalAttr.AddToSelf(f.Value) - } - require.True(t, attr.Equals(originalAttr)) + require.True(t, attr.Equals(m.Attributes)) } } @@ -149,11 +137,7 @@ func runAndValidateDB(t *testing.T, testDir, user string, testData *testData, ui flags, err := rd.GetMessagesFlags(ctx, []imap.InternalMessageID{m.ID}) require.Len(t, flags, 1) require.NoError(t, err) - originalFlags := imap.NewFlagSet() - for _, f := range m.Flags { - originalFlags.AddToSelf(f.Value) - } - require.True(t, flags[0].FlagSet.Equals(originalFlags)) + require.True(t, flags[0].FlagSet.Equals(m.Flags)) } } @@ -186,9 +170,32 @@ type messageToMBox struct { recent bool } +type mailbox struct { + ID imap.InternalMailboxID + RemoteID imap.MailboxID + Name string + UIDValidity imap.UID + Subscribed bool + Flags imap.FlagSet + PermanentFlags imap.FlagSet + Attributes imap.FlagSet +} + +type message struct { + ID imap.InternalMessageID + RemoteID imap.MessageID + Date time.Time + Size int + Body string + BodyStructure string + Envelope string + Deleted bool + Flags imap.FlagSet +} + type testData struct { - mailboxes []db.Mailbox - messages []db.Message + mailboxes []mailbox + messages []message messagesToMBox []messageToMBox } @@ -202,62 +209,26 @@ func newTestData(generator imap.UIDValidityGenerator) *testData { return uid } - mailboxes := []db.Mailbox{ + mailboxes := []mailbox{ { - ID: 1, - RemoteID: "RemoteID1", - Name: "Foobar", - UIDValidity: newUID(), - Subscribed: true, - Flags: []*db.MailboxFlag{ - { - Value: "Flag1", - }, - { - Value: "Flag2", - }, - }, - PermanentFlags: []*db.MailboxFlag{ - { - Value: "PermFlag1", - }, - { - Value: "PermFlag2", - }, - }, - Attributes: []*db.MailboxAttr{ - { - Value: "Attr1", - }, - }, + ID: 1, + RemoteID: "RemoteID1", + Name: "Foobar", + UIDValidity: newUID(), + Subscribed: true, + Flags: imap.NewFlagSet("Flag1", "Flag2"), + PermanentFlags: imap.NewFlagSet("PermFlag1", "PermFlag2"), + Attributes: imap.NewFlagSet("Attr1"), }, { - ID: 2, - RemoteID: "RemoteID2", - Name: "Abracadabra", - UIDValidity: newUID(), - Subscribed: true, - Flags: []*db.MailboxFlag{ - { - Value: "Flag3", - }, - { - Value: "Flag4", - }, - }, - PermanentFlags: []*db.MailboxFlag{ - { - Value: "PermFlag3", - }, - }, - Attributes: []*db.MailboxAttr{ - { - Value: "Attr2", - }, - { - Value: "Attr3", - }, - }, + ID: 2, + RemoteID: "RemoteID2", + Name: "Abracadabra", + UIDValidity: newUID(), + Subscribed: true, + Flags: imap.NewFlagSet("Flag3", "Flag4"), + PermanentFlags: imap.NewFlagSet("PermFlag3"), + Attributes: imap.NewFlagSet("Attr2", "Attr3"), }, { ID: 3, @@ -265,13 +236,13 @@ func newTestData(generator imap.UIDValidityGenerator) *testData { Name: "Mips", UIDValidity: newUID(), Subscribed: false, - Flags: nil, - PermanentFlags: nil, - Attributes: nil, + Flags: imap.NewFlagSet(), + PermanentFlags: imap.NewFlagSet(), + Attributes: imap.NewFlagSet(), }, } - messages := []db.Message{ + messages := []message{ { ID: imap.NewInternalMessageID(), RemoteID: "MessageID2", @@ -281,12 +252,7 @@ func newTestData(generator imap.UIDValidityGenerator) *testData { BodyStructure: "Message Structure 2", Envelope: "Message Envelope 2", Deleted: false, - Flags: []*db.MessageFlag{ - { - Value: "\\Seen", - }, - }, - UIDs: nil, + Flags: imap.NewFlagSet("\\Seen"), }, { ID: imap.NewInternalMessageID(), @@ -297,15 +263,7 @@ func newTestData(generator imap.UIDValidityGenerator) *testData { BodyStructure: "Message Structure 1", Envelope: "Message Envelope 1", Deleted: false, - Flags: []*db.MessageFlag{ - { - Value: "\\Seen", - }, - { - Value: "\\Flagged", - }, - }, - UIDs: nil, + Flags: imap.NewFlagSet("\\Seen", "\\Flagged"), }, { ID: imap.NewInternalMessageID(), @@ -316,8 +274,7 @@ func newTestData(generator imap.UIDValidityGenerator) *testData { BodyStructure: "Message Structure 3", Envelope: "Message Envelope 3", Deleted: true, - Flags: nil, - UIDs: nil, + Flags: imap.NewFlagSet(), }, } @@ -400,8 +357,8 @@ func prepareV0Database(t *testing.T, dir, user string, testData *testData, uidGe args := make([]any, 0, len(m.Flags)*2) - for _, f := range m.Flags { - args = append(args, m.ID, f.Value) + for _, f := range m.Flags.ToSliceUnsorted() { + args = append(args, m.ID, f) } require.NoError(t, utils.ExecQueryAndCheckUpdatedNotZero(ctx, qw, query, args...)) @@ -421,8 +378,8 @@ func prepareV0Database(t *testing.T, dir, user string, testData *testData, uidGe args := make([]any, 0, len(m.PermanentFlags)*2) - for _, f := range m.PermanentFlags { - args = append(args, m.ID, f.Value) + for _, f := range m.PermanentFlags.ToSliceUnsorted() { + args = append(args, m.ID, f) } require.NoError(t, utils.ExecQueryAndCheckUpdatedNotZero(ctx, qw, query, args...)) @@ -442,8 +399,8 @@ func prepareV0Database(t *testing.T, dir, user string, testData *testData, uidGe args := make([]any, 0, len(m.Attributes)*2) - for _, f := range m.Attributes { - args = append(args, m.ID, f.Value) + for _, f := range m.Attributes.ToSliceUnsorted() { + args = append(args, m.ID, f) } require.NoError(t, utils.ExecQueryAndCheckUpdatedNotZero(ctx, qw, query, args...)) @@ -488,8 +445,8 @@ func prepareV0Database(t *testing.T, dir, user string, testData *testData, uidGe args := make([]any, 0, len(testData.messages)*2) for _, m := range testData.messages { - for _, f := range m.Flags { - args = append(args, m.ID, f.Value) + for _, f := range m.Flags.ToSliceUnsorted() { + args = append(args, m.ID, f) } } diff --git a/internal/db_impl/sqlite3/read_ops.go b/internal/db_impl/sqlite3/read_ops.go index 2b2f9f14..ac50e6f0 100644 --- a/internal/db_impl/sqlite3/read_ops.go +++ b/internal/db_impl/sqlite3/read_ops.go @@ -96,10 +96,10 @@ func (r readOps) GetMailboxMessageIDPairs(ctx context.Context, mboxID imap.Inter }) } -func (r readOps) GetAllMailboxesWithAttr(ctx context.Context) ([]*db.Mailbox, error) { +func (r readOps) GetAllMailboxesWithAttr(ctx context.Context) ([]*db.MailboxWithAttr, error) { query := fmt.Sprintf("SELECT * FROM %v", v1.MailboxesTableName) - mailboxes, err := utils.MapQueryRowsFn(ctx, r.qw, query, ScanMailbox) + mailboxes, err := utils.MapQueryRowsFn(ctx, r.qw, query, ScanMailboxWithAttr) if err != nil { return nil, err } @@ -123,9 +123,7 @@ func (r readOps) GetAllMailboxesWithAttr(ctx context.Context) ([]*db.Mailbox, er return nil, err } - mbox.Attributes = xslices.Map(attrs, func(t string) *db.MailboxAttr { - return &db.MailboxAttr{Value: t} - }) + mbox.Attributes = imap.NewFlagSet(attrs...) } return mailboxes, nil @@ -421,7 +419,7 @@ func (r readOps) GetMessageRemoteID(ctx context.Context, id imap.InternalMessage return utils.MapQueryRow[imap.MessageID](ctx, r.qw, query, id) } -func (r readOps) GetImportedMessageData(ctx context.Context, id imap.InternalMessageID) (*db.Message, error) { +func (r readOps) GetImportedMessageData(ctx context.Context, id imap.InternalMessageID) (*db.MessageWithFlags, error) { flagsQuery := fmt.Sprintf("SELECT `%v` FROM %v WHERE `%v` = ?", v1.MessageFlagsFieldValue, v1.MessageFlagsTableName, @@ -433,25 +431,17 @@ func (r readOps) GetImportedMessageData(ctx context.Context, id imap.InternalMes v1.MessagesFieldID, ) - msg, err := utils.MapQueryRowFn(ctx, r.qw, messageQuery, ScanMessage, id) + msg, err := utils.MapQueryRowFn(ctx, r.qw, messageQuery, ScanMessageWithFlags, id) if err != nil { return nil, err } - flags, err := utils.MapQueryRowsFn(ctx, r.qw, flagsQuery, func(scanner utils.RowScanner) (*db.MessageFlag, error) { - mf := new(db.MessageFlag) - - if err := scanner.Scan(&mf.Value); err != nil { - return nil, err - } - - return mf, nil - }, id) + flags, err := utils.MapQueryRows[string](ctx, r.qw, flagsQuery, id) if err != nil { return nil, err } - msg.Flags = flags + msg.Flags = imap.NewFlagSet(flags...) return msg, nil } diff --git a/internal/db_impl/sqlite3/types.go b/internal/db_impl/sqlite3/types.go index 6437467f..dd1554ee 100644 --- a/internal/db_impl/sqlite3/types.go +++ b/internal/db_impl/sqlite3/types.go @@ -15,6 +15,16 @@ func ScanMailbox(scanner utils.RowScanner) (*db.Mailbox, error) { return mbox, nil } +func ScanMailboxWithAttr(scanner utils.RowScanner) (*db.MailboxWithAttr, error) { + mbox := new(db.MailboxWithAttr) + + if err := scanner.Scan(&mbox.ID, &mbox.RemoteID, &mbox.Name, &mbox.UIDValidity, &mbox.Subscribed); err != nil { + return nil, err + } + + return mbox, nil +} + func ScanMessage(scanner utils.RowScanner) (*db.Message, error) { msg := new(db.Message) @@ -24,3 +34,13 @@ func ScanMessage(scanner utils.RowScanner) (*db.Message, error) { return msg, nil } + +func ScanMessageWithFlags(scanner utils.RowScanner) (*db.MessageWithFlags, error) { + msg := new(db.MessageWithFlags) + + if err := scanner.Scan(&msg.ID, &msg.RemoteID, &msg.Date, &msg.Size, &msg.Body, &msg.BodyStructure, &msg.Envelope, &msg.Deleted); err != nil { + return nil, err + } + + return msg, nil +} diff --git a/internal/db_impl/sqlite3/utils/tracer.go b/internal/db_impl/sqlite3/utils/tracer.go index 214468cc..e52e7367 100644 --- a/internal/db_impl/sqlite3/utils/tracer.go +++ b/internal/db_impl/sqlite3/utils/tracer.go @@ -57,7 +57,7 @@ func (r ReadTracer) GetMailboxMessageIDPairs(ctx context.Context, mboxID imap.In return r.RD.GetMailboxMessageIDPairs(ctx, mboxID) } -func (r ReadTracer) GetAllMailboxesWithAttr(ctx context.Context) ([]*db.Mailbox, error) { +func (r ReadTracer) GetAllMailboxesWithAttr(ctx context.Context) ([]*db.MailboxWithAttr, error) { r.Entry.Tracef("GetAllMailboxesWithAttr") return r.RD.GetAllMailboxesWithAttr(ctx) @@ -189,7 +189,7 @@ func (r ReadTracer) GetMessageRemoteID(ctx context.Context, id imap.InternalMess return r.RD.GetMessageRemoteID(ctx, id) } -func (r ReadTracer) GetImportedMessageData(ctx context.Context, id imap.InternalMessageID) (*db.Message, error) { +func (r ReadTracer) GetImportedMessageData(ctx context.Context, id imap.InternalMessageID) (*db.MessageWithFlags, error) { r.Entry.Tracef("GetImportedMessageData") return r.RD.GetImportedMessageData(ctx, id) @@ -367,7 +367,7 @@ func (w WriteTracer) SetMailboxUIDValidity(ctx context.Context, mboxID imap.Inte return w.TX.SetMailboxUIDValidity(ctx, mboxID, uidValidity) } -func (w WriteTracer) CreateMessages(ctx context.Context, reqs ...*db.CreateMessageReq) ([]*db.Message, error) { +func (w WriteTracer) CreateMessages(ctx context.Context, reqs ...*db.CreateMessageReq) error { w.Entry.Tracef("CreateMessages") return w.TX.CreateMessages(ctx, reqs...) diff --git a/internal/db_impl/sqlite3/write_ops.go b/internal/db_impl/sqlite3/write_ops.go index 0abf7d80..a8fedb64 100644 --- a/internal/db_impl/sqlite3/write_ops.go +++ b/internal/db_impl/sqlite3/write_ops.go @@ -88,14 +88,11 @@ func (w writeOps) CreateMailbox( } return &db.Mailbox{ - ID: internalID, - RemoteID: mboxID, - Name: name, - UIDValidity: uidValidity, - Subscribed: true, - Flags: nil, - PermanentFlags: nil, - Attributes: nil, + ID: internalID, + RemoteID: mboxID, + Name: name, + UIDValidity: uidValidity, + Subscribed: true, }, nil } @@ -346,9 +343,7 @@ func (w writeOps) SetMailboxUIDValidity(ctx context.Context, mboxID imap.Interna return utils.ExecQueryAndCheckUpdatedNotZero(ctx, w.qw, query, uidValidity, mboxID) } -func (w writeOps) CreateMessages(ctx context.Context, reqs ...*db.CreateMessageReq) ([]*db.Message, error) { - result := make([]*db.Message, 0, len(reqs)) - +func (w writeOps) CreateMessages(ctx context.Context, reqs ...*db.CreateMessageReq) error { for _, chunk := range xslices.Chunk(reqs, db.ChunkLimit) { createMessageQuery := fmt.Sprintf("INSERT INTO %v (`%v`, `%v`, `%v`, `%v`, `%v`, `%v`, `%v`) VALUES %v", v1.MessagesTableName, @@ -378,23 +373,10 @@ func (w writeOps) CreateMessages(ctx context.Context, reqs ...*db.CreateMessageR for _, f := range req.Message.Flags.ToSliceUnsorted() { flagArgs = append(flagArgs, req.InternalID, f) } - - result = append(result, &db.Message{ - ID: req.InternalID, - RemoteID: req.Message.ID, - Date: req.Message.Date, - Size: req.LiteralSize, - Body: req.Body, - BodyStructure: req.Structure, - Envelope: req.Envelope, - Deleted: false, - Flags: db.MessageFlagsFromFlagSet(req.Message.Flags), - UIDs: nil, - }) } if _, err := utils.ExecQuery(ctx, w.qw, createMessageQuery, args...); err != nil { - return nil, err + return err } for _, chunk := range xslices.Chunk(flagArgs, db.ChunkLimit) { @@ -406,12 +388,12 @@ func (w writeOps) CreateMessages(ctx context.Context, reqs ...*db.CreateMessageR ) if _, err := utils.ExecQuery(ctx, w.qw, createFlagsQuery, chunk...); err != nil { - return nil, err + return err } } } - return result, nil + return nil } func (w writeOps) CreateMessageAndAddToMailbox(ctx context.Context, mbox imap.InternalMailboxID, req *db.CreateMessageReq) (imap.UID, imap.FlagSet, error) { diff --git a/internal/state/actions.go b/internal/state/actions.go index 7daa4484..18bce744 100644 --- a/internal/state/actions.go +++ b/internal/state/actions.go @@ -331,12 +331,7 @@ func (state *State) actionImportRecoveredMessage( return db.MessageIDPair{}, false, err } - messageFlags := imap.NewFlagSet() - for _, flag := range message.Flags { - messageFlags.AddToSelf(flag.Value) - } - - internalID, res, newLiteral, err := state.user.GetRemote().CreateMessage(ctx, mboxID, literal, messageFlags, message.Date) + internalID, res, newLiteral, err := state.user.GetRemote().CreateMessage(ctx, mboxID, literal, message.Flags, message.Date) if err != nil { return db.MessageIDPair{}, false, err } @@ -379,7 +374,7 @@ func (state *State) actionImportRecoveredMessage( InternalID: internalID, } - if _, err := tx.CreateMessages(ctx, &req); err != nil { + if err := tx.CreateMessages(ctx, &req); err != nil { return db.MessageIDPair{}, false, err } diff --git a/internal/state/match.go b/internal/state/match.go index fcf819ad..49e5f6aa 100644 --- a/internal/state/match.go +++ b/internal/state/match.go @@ -21,7 +21,7 @@ type matchMailbox struct { Name string Subscribed bool // EntMBox should be set to nil if there is no such value. - EntMBox *db.Mailbox + EntMBox *db.MailboxWithAttr } func getMatches( @@ -106,12 +106,7 @@ func prepareMatch( ) if mbox.EntMBox != nil { - atts = imap.NewFlagSetFromSlice(xslices.Map( - mbox.EntMBox.Attributes, - func(flag *db.MailboxAttr) string { - return flag.Value - }, - )) + atts = mbox.EntMBox.Attributes.Clone() recent, err := client.GetMailboxRecentCount(ctx, mbox.EntMBox.ID) if err != nil { diff --git a/internal/state/state.go b/internal/state/state.go index d9535b5d..7dda455e 100644 --- a/internal/state/state.go +++ b/internal/state/state.go @@ -90,7 +90,7 @@ func (state *State) List(ctx context.Context, ref, pattern string, lsub bool, fn recoveryMBoxMessageCount = 0 } - mailboxes = xslices.Filter(mailboxes, func(mailbox *db.Mailbox) bool { + mailboxes = xslices.Filter(mailboxes, func(mailbox *db.MailboxWithAttr) bool { if mailbox.ID == recoveryMailboxID && recoveryMBoxMessageCount == 0 { return false } @@ -397,7 +397,7 @@ func (state *State) Rename(ctx context.Context, oldName, newName string) error { return nil, err } - inferiors := listInferiors(oldName, state.delimiter, xslices.Map(mailboxes, func(mailbox *db.Mailbox) string { + inferiors := listInferiors(oldName, state.delimiter, xslices.Map(mailboxes, func(mailbox *db.MailboxWithAttr) string { return mailbox.Name }))