The messages database contains all the MAP-specific data.
The following entity-relationship (ER) diagram shows the relationships among the tables in the messages database:

The database schema is specified in /db/messages.sql, which looks like this:
/* Turn foreign key constraints on */
PRAGMA foreign_keys = ON;
/* Message types */
CREATE TABLE message_types (
message_type_id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT NOT NULL UNIQUE
);
/* Accounts/Instances */
CREATE TABLE accounts (
account_id INTEGER PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
active BOOLEAN NOT NULL
);
/* Accounts/Instances */
CREATE TABLE accounts_message_types_rel (
account_id INTEGER NOT NULL REFERENCES accounts ON DELETE CASCADE,
message_type_id INTEGER NOT NULL REFERENCES message_types,
PRIMARY KEY (account_id, message_type_id)
);
/* Folders */
CREATE TABLE folders (
folder_id INTEGER PRIMARY KEY AUTOINCREMENT,
parent_id INTEGER REFERENCES folders ON DELETE CASCADE,
account_id INTEGER NOT NULL REFERENCES accounts ON DELETE CASCADE,
name TEXT NOT NULL,
path TEXT NOT NULL,
CHECK (parent_id <> folder_id)
);
/* Messages (all types: email, sms, mms) */
CREATE TABLE messages (
message_id INTEGER PRIMARY KEY AUTOINCREMENT,
message_type_id INTEGER NOT NULL REFERENCES message_types,
folder_id INTEGER NOT NULL REFERENCES folders ON DELETE CASCADE,
handle TEXT NOT NULL,
subject TEXT NOT NULL,
datetime TEXT NOT NULL,
sender_contact_id INTEGER NOT NULL REFERENCES contacts(contact_id),
reply_to_contact_id INTEGER REFERENCES contacts(contact_id),
read INTEGER NOT NULL DEFAULT 0,
sent INTEGER NOT NULL DEFAULT 0,
protected INTEGER NOT NULL DEFAULT 0,
priority INTEGER NOT NULL DEFAULT 0,
CHECK (read = 0 OR read = 1),
CHECK (sent = 0 OR sent = 1),
CHECK (protected = 0 OR protected = 1),
CHECK (priority = 0 OR priority = 1)
);
/* Contacts (senders/recipients) */
CREATE TABLE contacts (
contact_id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT,
number TEXT,
family_name TEXT,
given_name TEXT,
CHECK(email IS NOT NULL OR number IS NOT NULL)
);
/* Message contents */
CREATE TABLE message_contents (
message_content_id INTEGER PRIMARY KEY AUTOINCREMENT,
message_id INTEGER NOT NULL REFERENCES messages ON DELETE CASCADE,
subject TEXT ,
body_html TEXT ,
body_plain_text TEXT ,
CHECK (body_html IS NOT NULL OR body_plain_text IS NOT NULL)
);
/* Message recipient types */
CREATE TABLE message_recipient_types (
message_recipient_type_id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT NOT NULL UNIQUE
);
/* Message recipients */
CREATE TABLE message_recipients (
message_recipient_id INTEGER PRIMARY KEY AUTOINCREMENT,
message_id INTEGER NOT NULL REFERENCES messages ON DELETE CASCADE,
contact_id INTEGER NOT NULL REFERENCES contacts,
message_recipient_type_id INTEGER NOT NULL REFERENCES message_recipient_types
);
/* Attachments */
CREATE TABLE attachments (
attachment_id INTEGER PRIMARY KEY AUTOINCREMENT,
message_id INTEGER NOT NULL REFERENCES messages ON DELETE CASCADE,
filename TEXT NOT NULL,
size INTEGER NOT NULL,
embedded BOOLEAN NOT NULL
);
/**
VIEWS
*/
/* View to retrieve a list of brief messages */
CREATE VIEW "messages_view" AS
SELECT
accounts.account_id,
accounts.name as account_name,
messages.message_id,
messages.folder_id,
folders.name as folder_name,
folders.path as folder_path,
message_types.type,
messages.handle,
messages.subject,
messages.datetime,
messages.sender_contact_id,
contacts_sender.email as sender_email,
contacts_sender.number as sender_number,
contacts_sender.family_name as sender_last_name,
contacts_sender.given_name as sender_first_name,
messages.reply_to_contact_id,
contacts_reply.email as reply_to_email,
contacts_reply.number as reply_to_number,
contacts_reply.family_name as reply_to_last_name,
contacts_reply.given_name as reply_to_first_name,
messages.read,
messages.sent,
messages.protected,
messages.priority,
recipients.email as recipient_email,
recipients.number as recipient_number,
recipients.family_name as recipient_last_name,
recipients.given_name as recipient_first_name
FROM messages
LEFT JOIN contacts contacts_sender ON messages.sender_contact_id
= contacts_sender.contact_id
LEFT JOIN contacts contacts_reply ON messages.reply_to_contact_id
= contacts_reply.contact_id
LEFT JOIN folders ON messages.folder_id = folders.folder_id
LEFT JOIN accounts ON folders.account_id = accounts.account_id
LEFT JOIN message_types ON messages.message_type_id
= message_types.message_type_id
LEFT JOIN contacts recipients ON recipients.contact_id
= (SELECT contact_id FROM message_recipients
WHERE message_recipients.message_id = messages.message_id
AND message_recipients.message_recipient_type_id
= 1 ORDER BY message_recipients.message_recipient_id DESC LIMIT 1);
/* View to retrieve full messages */
CREATE VIEW "full_messages_view" AS
SELECT
accounts.account_id,
accounts.name as account_name,
messages.message_id,
messages.folder_id,
folders.name as folder_name,
folders.path as folder_path,
message_types.type,
messages.handle,
messages.datetime,
messages.sender_contact_id,
contacts_sender.email as sender_email,
contacts_sender.number as sender_number,
contacts_sender.family_name as sender_last_name,
contacts_sender.given_name as sender_first_name,
messages.reply_to_contact_id,
contacts_reply.email as reply_to_email,
contacts_reply.number as reply_to_number,
contacts_reply.family_name as reply_to_last_name,
contacts_reply.given_name as reply_to_first_name,
messages.read,
messages.sent,
messages.protected,
messages.priority,
COALESCE(message_contents.subject, messages.subject) as subject,
message_contents.body_plain_text,
message_contents.body_html
FROM messages
LEFT JOIN message_contents ON messages.message_id
= message_contents.message_id
LEFT JOIN contacts contacts_sender ON messages.sender_contact_id
= contacts_sender.contact_id
LEFT JOIN contacts contacts_reply ON messages.reply_to_contact_id
= contacts_reply.contact_id
LEFT JOIN folders ON messages.folder_id = folders.folder_id
LEFT JOIN accounts ON folders.account_id = accounts.account_id
LEFT JOIN message_types ON messages.message_type_id
= message_types.message_type_id
WHERE message_contents.message_content_id IS NOT NULL;
/* view to retrieve contacts */
CREATE VIEW "contacts_view" AS
select contacts.contact_id, contacts.email, contacts.number, contacts.family_name,
contacts.given_name, message_recipient_types.type, message_recipients.message_id
from message_recipients
LEFT JOIN message_recipient_types ON message_recipients.message_recipient_type_id
= message_recipient_types. message_recipient_type_id
LEFT JOIN contacts ON message_recipients.contact_id = contacts.contact_id;
/*
TRIGGERS
*/
/* Constrain messages to be a message type that is of its parent account supported
* message types */
/*
CREATE TRIGGER insert_message_check_message_type BEFORE INSERT ON messages
FOR EACH ROW WHEN NOT EXISTS (SELECT *
FROM accounts_message_types_rel
LEFT JOIN folders ON accounts_message_types_rel.account_id = folders.account_id
LEFT JOIN messages ON folders.folder_id = new.folder_id
WHERE new.message_type_id = accounts_message_types_rel.message_type_id)
BEGIN
SELECT RAISE(ABORT,
'Message type must be a supported message type of the message''s account');
END
CREATE TRIGGER insert_message_check_handle BEFORE INSERT ON messages
FOR EACH ROW WHEN (SELECT count(*) FROM messages JOIN folders
ON messages.folder_id = folders.folder_id
WHERE new.handle = messages.handle AND folders.account_id =
(SELECT accounts.account_id FROM accounts JOIN folders
ON accounts.account_id = folders.account_id
WHERE folders.folder_id = new.folder_id)) > 0
BEGIN
SELECT RAISE(ABORT, 'Handle must be unique per a message''s account');
END
*/