126 lines
4.0 KiB
Go
126 lines
4.0 KiB
Go
package db
|
|
|
|
import (
|
|
"database/sql"
|
|
"strconv"
|
|
)
|
|
|
|
// MessagesState returns the current JMAP (internal/jmap) Email state string for
|
|
// mailboxID — the highest modseq across both esrv_mailbox_messages and its tombstones
|
|
// table, since a destroy must advance the reported state even though the message row
|
|
// itself is gone by then.
|
|
func (d *DB) MessagesState(mailboxID int64) (string, error) {
|
|
var msgMax, tombMax int64
|
|
if err := d.QueryRow(`SELECT COALESCE(MAX(modseq), 0) FROM esrv_mailbox_messages WHERE mailbox_id = ?`, mailboxID).Scan(&msgMax); err != nil {
|
|
return "", err
|
|
}
|
|
if err := d.QueryRow(`SELECT COALESCE(MAX(modseq), 0) FROM esrv_mailbox_message_tombstones WHERE mailbox_id = ?`, mailboxID).Scan(&tombMax); err != nil {
|
|
return "", err
|
|
}
|
|
max := msgMax
|
|
if tombMax > max {
|
|
max = tombMax
|
|
}
|
|
return strconv.FormatInt(max, 10), nil
|
|
}
|
|
|
|
// FoldersState returns the current JMAP (internal/jmap) Mailbox state string for
|
|
// mailboxID.
|
|
func (d *DB) FoldersState(mailboxID int64) (string, error) {
|
|
var max int64
|
|
err := d.QueryRow(`SELECT COALESCE(MAX(modseq), 0) FROM esrv_mailbox_folders WHERE mailbox_id = ?`, mailboxID).Scan(&max)
|
|
return strconv.FormatInt(max, 10), err
|
|
}
|
|
|
|
// MessageChanges is one Email/changes result (internal/jmap): what changed for
|
|
// mailboxID since sinceState. No maxChanges paging in v1 (see the JMAP plan's
|
|
// disclosed scope cuts) — HasMore is always false.
|
|
type MessageChanges struct {
|
|
Created []int64
|
|
Updated []int64
|
|
Destroyed []int64
|
|
NewState string
|
|
HasMore bool
|
|
}
|
|
|
|
func scanInt64Rows(rows *sql.Rows) ([]int64, error) {
|
|
defer rows.Close()
|
|
var out []int64
|
|
for rows.Next() {
|
|
var id int64
|
|
if err := rows.Scan(&id); err != nil {
|
|
return nil, err
|
|
}
|
|
out = append(out, id)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
// MessageChangesSince computes MessageChanges for mailboxID against sinceState (a
|
|
// modseq value, as returned by a prior MessagesState/MessageChangesSince call) — see
|
|
// schema.go's comment on esrv_mailbox_messages.modseq/created_modseq for the created/
|
|
// updated/destroyed derivation.
|
|
func (d *DB) MessageChangesSince(mailboxID, sinceState int64) (MessageChanges, error) {
|
|
var out MessageChanges
|
|
|
|
createdRows, err := d.Query(`SELECT id FROM esrv_mailbox_messages WHERE mailbox_id = ? AND created_modseq > ?`, mailboxID, sinceState)
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
if out.Created, err = scanInt64Rows(createdRows); err != nil {
|
|
return out, err
|
|
}
|
|
|
|
updatedRows, err := d.Query(`SELECT id FROM esrv_mailbox_messages WHERE mailbox_id = ? AND modseq > ? AND created_modseq <= ?`, mailboxID, sinceState, sinceState)
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
if out.Updated, err = scanInt64Rows(updatedRows); err != nil {
|
|
return out, err
|
|
}
|
|
|
|
destroyedRows, err := d.Query(`SELECT message_id FROM esrv_mailbox_message_tombstones WHERE mailbox_id = ? AND modseq > ?`, mailboxID, sinceState)
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
if out.Destroyed, err = scanInt64Rows(destroyedRows); err != nil {
|
|
return out, err
|
|
}
|
|
|
|
out.NewState, err = d.MessagesState(mailboxID)
|
|
return out, err
|
|
}
|
|
|
|
// FolderChanges is one Mailbox/changes result (internal/jmap). No destroyed list: a
|
|
// JMAP Mailbox/set destroy reparents under Trash (db.MoveFolderToTrash), the row never
|
|
// disappears — see schema.go's comment on esrv_mailbox_folders.modseq.
|
|
type FolderChanges struct {
|
|
Created []int64
|
|
Updated []int64
|
|
NewState string
|
|
HasMore bool
|
|
}
|
|
|
|
func (d *DB) FolderChangesSince(mailboxID, sinceState int64) (FolderChanges, error) {
|
|
var out FolderChanges
|
|
|
|
createdRows, err := d.Query(`SELECT id FROM esrv_mailbox_folders WHERE mailbox_id = ? AND created_modseq > ?`, mailboxID, sinceState)
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
if out.Created, err = scanInt64Rows(createdRows); err != nil {
|
|
return out, err
|
|
}
|
|
|
|
updatedRows, err := d.Query(`SELECT id FROM esrv_mailbox_folders WHERE mailbox_id = ? AND modseq > ? AND created_modseq <= ?`, mailboxID, sinceState, sinceState)
|
|
if err != nil {
|
|
return out, err
|
|
}
|
|
if out.Updated, err = scanInt64Rows(updatedRows); err != nil {
|
|
return out, err
|
|
}
|
|
|
|
out.NewState, err = d.FoldersState(mailboxID)
|
|
return out, err
|
|
}
|