This document explains how to use the table-qualified field mapping system to support different database schemas across multiple databases.
- Overview
- Configuration Format
- Field Mapping Syntax
- Usage in Code
- Field Mapping Resolution
- Example Configurations
- Supported Tables
- Best Practices
- Migration from Simple Field Names
- Troubleshooting
The field mapping system allows you to map logical field names to actual database field names for each database configuration. This is useful when different databases have different schema field names but you want to use consistent logical names in your application code.
Field mappings are configured in the config.toml file using table-qualified field names:
[[databases]]
id = "primary"
label = "Main Server"
url = "mysql://user:pass@localhost/db"
[databases.field_map]
# Users table field mappings
users.id = "user_id"
users.enabled = "is_active"
users.email = "email_address"
# Domains table field mappings
domains.id = "domain_id"
domains.enabled = "is_enabled"
# Aliases table field mappings
aliases.id = "alias_id"
aliases.enabled = "is_active"Use the format table.field to specify which table a field mapping applies to:
users.id- Maps theidfield in theuserstabledomains.enabled- Maps theenabledfield in thedomainstablealiases.mail- Maps themailfield in thealiasestable
Simple field names without table qualification are still supported for backward compatibility:
[databases.field_map]
enabled = "is_active" # Applies to all tables if not overriddenuse crate::config::DatabaseConfig;
// Get the mapped field name for a specific table and field
let user_id_field = db_config.field_for_table("users", "id");
let domain_enabled_field = db_config.field_for_table("domains", "enabled");
// Backward compatibility - works with simple field names
let enabled_field = db_config.field("enabled");The system provides field-mapped versions of database functions:
// Get users with field mapping
let users = get_users_with_field_map(&pool, &db_config)?;
// Get domains with field mapping
let domains = get_domains_with_field_map(&pool, &db_config)?;
// Get aliases with field mapping
let aliases = get_aliases_with_field_map(&pool, &db_config)?;Use the helper function to build field-mapped queries:
use crate::db::build_field_mapped_query;
let fields = [
("id", "pkid"),
("domain", "domain"),
("enabled", "enabled"),
];
let sql = build_field_mapped_query("domains", &fields, &db_config);
// Result: "SELECT domain_id as pkid, domain as domain, is_enabled as enabled FROM domains"The system resolves field mappings in the following order:
- Table-qualified field name:
users.id→user_id - Simple field name:
id→user_id(if no table-qualified mapping exists) - Original field name:
id→id(if no mapping exists)
[[databases]]
id = "legacy"
label = "Legacy Database"
url = "mysql://user:pass@legacy/db"
[databases.field_map]
users.id = "user_id"
users.enabled = "is_active"
domains.id = "domain_id"
domains.enabled = "is_enabled"
[[databases]]
id = "modern"
label = "Modern Database"
url = "mysql://user:pass@modern/db"
[databases.field_map]
users.id = "id"
users.enabled = "enabled"
domains.id = "id"
domains.enabled = "enabled"[[databases]]
id = "mixed"
label = "Mixed Schema Database"
url = "mysql://user:pass@mixed/db"
[databases.field_map]
# Some tables use different field names
users.id = "user_id"
users.enabled = "is_active"
# Other tables use standard names
domains.id = "id"
domains.enabled = "enabled"
aliases.id = "id"
aliases.enabled = "enabled"The field mapping system currently supports the following tables:
users- User accountsdomains- Domain configurationsaliases- Email aliasesbackups- Backup domain configurationsrelays- Relay configurationsrelocated- Relocated email addressesclients- Client configurations
- Use table-qualified field names for clarity and to avoid conflicts
- Document your field mappings in comments within the config file
- Test field mappings with different database schemas
- Use consistent logical names across your application code
- Provide fallback mappings for backward compatibility when possible
If you're migrating from the old simple field mapping system:
- Update your config to use table-qualified field names
- Test the new mappings thoroughly
- Update any custom queries to use the new field mapping functions
- Remove old simple field mappings once migration is complete
- Field not found: Ensure the field mapping exists for the specific table
- Wrong field name: Check that the mapped field name matches the actual database schema
- Case sensitivity: Database field names are case-sensitive
- Table name mismatch: Verify the table name in the field mapping matches your schema
Enable debug logging to see field mapping resolution:
tracing::debug!("Field mapping for users.id: {}", db_config.field_for_table("users", "id"));