Multi-Dialect Support Guide
This guide explains how to work with multiple SQL dialects in sqlmeta.
Setting Dialects
Always specify the dialect when creating objects:
from sqlmeta import Table, SqlColumn
# PostgreSQL
pg_table = Table(
"users",
dialect="postgresql",
columns=[SqlColumn("id", "SERIAL", is_primary_key=True)]
)
# MySQL
mysql_table = Table(
"users",
dialect="mysql",
columns=[SqlColumn("id", "INT AUTO_INCREMENT", is_primary_key=True)]
)
# Oracle
oracle_table = Table(
"users",
dialect="oracle",
columns=[SqlColumn("id", "NUMBER GENERATED ALWAYS AS IDENTITY", is_primary_key=True)]
)
Dialect-Specific Features
PostgreSQL
from sqlmeta import Table, SqlColumn
from sqlmeta.objects.extension import Extension
from sqlmeta.objects.view import View
# Extensions
uuid_extension = Extension(
name="uuid-ossp",
schema="public",
dialect="postgresql"
)
# Materialized views
mat_view = View(
name="user_stats",
definition="SELECT user_id, COUNT(*) FROM posts GROUP BY user_id",
materialized=True,
dialect="postgresql"
)
# SERIAL types
table = Table(
"users",
dialect="postgresql",
columns=[
SqlColumn("id", "SERIAL", is_primary_key=True),
SqlColumn("uuid", "UUID", default_value="uuid_generate_v4()"),
]
)
MySQL
from sqlmeta.objects.event import Event
# Storage engines
innodb_table = Table(
"users",
dialect="mysql",
storage_engine="InnoDB",
columns=[SqlColumn("id", "INT AUTO_INCREMENT", is_primary_key=True)]
)
# Events (scheduled tasks)
cleanup_event = Event(
name="cleanup_old_logs",
schedule="EVERY 1 DAY",
body="DELETE FROM logs WHERE created_at < DATE_SUB(NOW(), INTERVAL 30 DAY)",
dialect="mysql"
)
Oracle
from sqlmeta.objects.package import Package
from sqlmeta.objects.database_link import DatabaseLink
# Packages
pkg = Package(
name="user_pkg",
schema="public",
spec="PROCEDURE update_user(p_id NUMBER, p_name VARCHAR2);",
body="...",
dialect="oracle"
)
# Database links
db_link = DatabaseLink(
name="remote_db",
connect_string="user/pass@remote",
dialect="oracle"
)
SQL Server
from sqlmeta.objects.linked_server import LinkedServer
# Memory-optimized tables
memory_table = Table(
"sessions",
dialect="mssql",
memory_optimized=True,
columns=[SqlColumn("id", "INT", is_primary_key=True)]
)
# Temporal tables
temporal_table = Table(
"employees",
dialect="mssql",
system_versioned=True,
history_table="employees_history",
columns=[
SqlColumn("id", "INT", is_primary_key=True),
SqlColumn("name", "VARCHAR(100)"),
]
)
# Linked servers
linked = LinkedServer(
name="REMOTE_SERVER",
product_name="SQL Server",
data_source="remote.server.com",
dialect="mssql"
)
Cross-Dialect Conversion
Convert table definitions between dialects:
def convert_dialect(table, target_dialect):
"""Convert a table to a different dialect."""
# Export to dict
table_dict = table.to_dict()
# Update dialect
table_dict['dialect'] = target_dialect
# Update dialect-specific data types
for col in table_dict['columns']:
col['data_type'] = convert_type(
col['data_type'],
table.dialect,
target_dialect
)
# Recreate table
return Table.from_dict(table_dict)
def convert_type(data_type, source_dialect, target_dialect):
"""Convert data type between dialects."""
type_mappings = {
('postgresql', 'mysql'): {
'SERIAL': 'INT AUTO_INCREMENT',
'BOOLEAN': 'TINYINT(1)',
'TEXT': 'LONGTEXT',
},
('mysql', 'postgresql'): {
'INT AUTO_INCREMENT': 'SERIAL',
'TINYINT(1)': 'BOOLEAN',
'LONGTEXT': 'TEXT',
},
# Add more mappings...
}
mapping = type_mappings.get((source_dialect, target_dialect), {})
return mapping.get(data_type.upper(), data_type)
# Example usage
pg_table = Table("users", dialect="postgresql", columns=[
SqlColumn("id", "SERIAL", is_primary_key=True),
SqlColumn("active", "BOOLEAN"),
])
mysql_table = convert_dialect(pg_table, "mysql")
Type Normalization
The type normalizer handles dialect-specific type variations:
from sqlmeta.comparison.type_normalizer import DataTypeNormalizer
# PostgreSQL
pg_normalizer = DataTypeNormalizer(dialect="postgresql")
assert pg_normalizer.normalize("VARCHAR(255)") == "VARCHAR(255)"
assert pg_normalizer.normalize("CHARACTER VARYING(255)") == "VARCHAR(255)"
assert pg_normalizer.normalize("BOOL") == "BOOLEAN"
# MySQL
mysql_normalizer = DataTypeNormalizer(dialect="mysql")
assert mysql_normalizer.normalize("INT") == "INTEGER"
assert mysql_normalizer.normalize("TINYINT(1)") == "BOOLEAN"
Best Practices
Always Specify Dialect
# Good table = Table("users", dialect="postgresql", columns=[...]) # Bad - dialect may be guessed incorrectly table = Table("users", columns=[...])
Use Dialect-Agnostic Types When Possible
# Use standard SQL types SqlColumn("name", "VARCHAR(100)") # Works everywhere SqlColumn("created_at", "TIMESTAMP") # Works everywhere # Avoid dialect-specific types unless necessary # SqlColumn("id", "SERIAL") # PostgreSQL-specific
Test with Multiple Dialects
If supporting multiple databases, test schema definitions with each dialect.
Document Dialect Requirements
class UserSchema: """User table schema. Requires: - PostgreSQL 12+ for UUID support - MySQL 8+ for JSON columns """ pass
Use Dialect Detection
def get_table_for_dialect(dialect): """Get table definition for specific dialect.""" if dialect == "postgresql": return pg_table elif dialect == "mysql": return mysql_table elif dialect == "oracle": return oracle_table else: raise ValueError(f"Unsupported dialect: {dialect}")
Comparison Across Dialects
Compare schemas from different databases:
from sqlmeta.comparison.comparator import ObjectComparator
# Source is PostgreSQL
pg_table = Table("users", dialect="postgresql", columns=[
SqlColumn("id", "SERIAL", is_primary_key=True),
])
# Target is MySQL
mysql_table = Table("users", dialect="mysql", columns=[
SqlColumn("id", "INT AUTO_INCREMENT", is_primary_key=True),
])
# Compare (handles type normalization)
comparator = ObjectComparator(dialect="generic")
diff = comparator.compare_tables(pg_table, mysql_table)
# No differences (SERIAL and INT AUTO_INCREMENT are both identity columns)
assert not diff.has_diffs