Files
sasa-maillist/database
James Pattinson 35f710049a MySQL support
2025-10-12 19:24:14 +00:00
..
2025-10-12 19:24:14 +00:00
2025-10-12 19:24:14 +00:00

Database-Driven Mailing List Management

This mailing list system uses MySQL with Postfix's native MySQL support for real-time dynamic list management. Postfix queries the database directly for each email - no scripts or reloads needed.

Database Schema

Three-table design with many-to-many relationships:

Tables

lists - Mailing list definitions

  • list_id (primary key)
  • list_name - Display name
  • list_email - Full email address (e.g., community@lists.sasalliance.org)
  • description - Optional description
  • active - Boolean flag to enable/disable list

members - Member information

  • member_id (primary key)
  • name - Display name
  • email - Email address
  • active - Boolean flag to enable/disable member

list_members - Subscription junction table

  • list_id + member_id (composite unique key)
  • active - Boolean flag to enable/disable subscription
  • Foreign keys to lists and members

How It Works

  1. Incoming email arrives for community@lists.sasalliance.org
  2. Postfix queries MySQL using the config in mysql_virtual_alias_maps.cf
  3. Database returns comma-separated list of active member emails
  4. Postfix expands the alias and delivers to all members
  5. Changes take effect immediately - no restart or reload needed!

Managing Lists and Members

Via MySQL Client

Connect to the database:

docker-compose exec mysql mysql -u maillist -p maillist

Common Operations

View all lists:

SELECT list_id, list_name, list_email, active FROM lists;

View all members:

SELECT member_id, name, email, active FROM members;

View subscriptions for a list:

SELECT m.name, m.email 
FROM members m
JOIN list_members lm ON m.member_id = lm.member_id
JOIN lists l ON lm.list_id = l.list_id
WHERE l.list_email = 'community@lists.sasalliance.org'
  AND lm.active = TRUE AND m.active = TRUE;

Add a new member:

INSERT INTO members (name, email) 
VALUES ('John Doe', 'john.doe@example.com');

Subscribe member to list:

-- Method 1: Using subqueries (one step)
INSERT INTO list_members (list_id, member_id) 
VALUES (
    (SELECT list_id FROM lists WHERE list_email = 'community@lists.sasalliance.org'),
    (SELECT member_id FROM members WHERE email = 'john.doe@example.com')
);

Unsubscribe member from list:

DELETE FROM list_members 
WHERE list_id = (SELECT list_id FROM lists WHERE list_email = 'community@lists.sasalliance.org')
  AND member_id = (SELECT member_id FROM members WHERE email = 'john.doe@example.com');

Create a new mailing list:

INSERT INTO lists (list_name, list_email, description)
VALUES ('Developers', 'dev@lists.sasalliance.org', 'Developer discussions');

Disable a list (keeps data, stops delivery):

UPDATE lists SET active = FALSE WHERE list_email = 'community@lists.sasalliance.org';

Re-enable a list:

UPDATE lists SET active = TRUE WHERE list_email = 'community@lists.sasalliance.org';

Verification

Test that Postfix can query the database:

docker-compose exec postfix postmap -q "community@lists.sasalliance.org" mysql:/etc/postfix/mysql_virtual_alias_maps.cf

This should return a comma-separated list of member email addresses.

Database Initialization

The database is automatically initialized from database/schema.sql when the MySQL container first starts. Sample data includes:

  • 4 mailing lists (community, board, members, announcements)
  • 2 sample members
  • Sample subscriptions

Reset Database

To completely reset the database (deletes all data!):

docker-compose down -v  # Remove volumes
docker-compose up -d    # Reinitialize from schema.sql

Performance

Postfix caches MySQL query results, so the database isn't queried for every single email. The cache TTL is configurable in mysql_virtual_alias_maps.cf if needed.