Hello, While experimenting with the sqlite backend, I noticed a big vacuum wrt its use for a quota dict. This is thus an opportunity to share a solution I managed to devise after some trials and errors and a (very quick and unscientific) look at the code. And also the opportunity to ask the community to check for possible flaws. Yes, nothing comes for free... ;-) TIA, Axel This is the SQL for creating the database or for adding needed table/trigger to an existing database (with many comments and few "active" lines): CREATE TABLE quota ( -- We will assume that, in the case of a sqlite DB (and probably a pgsql DB -- as well), Dovecot will *always* act along these lines: -- In case of a "row creation": -- INSERT INTO quota (subject, byte_count) -- VALUES (<some id>, <byte count>); -- INSERT INTO quota (subject, message_count) -- VALUES (<some id>, <msg count>); -- In case of a recalculation: -- UPDATE quota -- SET byte_count = byte_count + <byte count diff>, -- message_count = message_count + <msg count diff> -- WHERE subject = <some id>; -- In case of a row deletion: -- DELETE FROM quota -- WHERE subject = <some id>; -- For example, a "doveadm quota recalc" may first delete the relevant row -- if it exists, then (re-)create it. -- Clearly, the "row creation" case is a bit problematic, hence the need for -- a trigger. -- An id for the entity subjected to a quota (a user/mailbox, a domain...). subject TEXT PRIMARY KEY NOT NULL, -- The entity's size, expressed as a storage space use and as a message -- count. -- Note the NULL defaults: they allow to simplify the trigger's logics. byte_count INTEGER DEFAULT NULL, message_count INTEGER DEFAULT NULL ); CREATE TRIGGER quota_insert BEFORE INSERT ON quota FOR EACH ROW BEGIN -- If there's a row to update, that's because the first INSERT of the "row -- creation" has been performed. -- (this UPDATE could probably be simplified if the insertion order, -- byte_count then message_count, was guaranteed; anyway, since the "row -- creation" operation shouldn't be very frequent, we may afford a slight -- inefficiency). UPDATE quota SET byte_count CASE WHEN NEW.byte_count ISNULL THEN byte_count ELSE NEW.byte_count END, message_count CASE WHEN NEW.message_count ISNULL THEN message_count ELSE NEW.message_count END WHERE subject = NEW.subject; -- In which case we may ignore that second (current) INSERT, since the -- previously inserted row has just been updated with the value coming -- from the current INSERT statement. SELECT raise(IGNORE) WHERE EXISTS (SELECT * FROM quota WHERE subject = NEW.subject); END; The corresponding dict configuration being thus supposed to be similar to this one: connect = /path/to/database map { pattern = priv/quota/storage table = quota username_field = subject value_field = byte_count } map { pattern = priv/quota/messages table = quota username_field = subject value_field = message_count }