API Reference

Entry points

comdab - Compare Database Schemas

Loïc Simon 2025, MIT License

comdab.generate_migrations(source_connection: Connection, target_connection: Connection, generator: MigrationGeneratorPort, *, source_schema: str = 'public', target_schema: str = 'public', rules: dict[ComdabPath, ComdabStrategy] | None = None, allow_unknown_types: bool = False) None[source]

Generate migrations from a source schema to a target schema.

This is the highest-level, all-in-one comdab function.

Parameters:
  • source_connection – A SQLAlchemy connection established to the database we want to migrate.

  • target_connection – A SQLAlchemy connection established to a database with the desired state to reach.

  • generator – The MigrationGeneratorPort to use to generate migrations after the differences are found.

  • source_schema – The name of the database schema to compare for the source_connection.

  • target_schema – The name of the database schema to compare for the target_connection.

  • rules – An optional set of rules to allow some differences between the schemas, or report them as warnings. See Ignore rules for more details.

  • allow_unknown_types – If True, SQL data types not handed (yet) by comdab will be modeled as “unknown” and compared based on their name only. If False (the default), encountering an unknown name will raise an UnhandledTypeError.

comdab.compare_databases(left_connection: Connection, right_connection: Connection, *, left_schema: str = 'public', right_schema: str = 'public', rules: dict[ComdabPath, ComdabStrategy] | None = None, allow_unknown_types: bool = False) list[ComdabReport][source]

Build and compare two database schemas, from their SQLAlchemy connection.

Parameters:
  • left_connection – A SQLAlchemy connection established to the first database to compare.

  • right_connection – A SQLAlchemy connection established to the second database to compare.

  • left_schema – The name of the database schema to compare for the left_connection.

  • right_schema – The name of the database schema to compare for the right_connection.

  • rules – An optional set of rules to allow some differences between the schemas, or report them as warnings. See Ignore rules for more details.

  • allow_unknown_types – If True, SQL data types not handed (yet) by comdab will be modeled as “unknown” and compared based on their name only. If False (the default), encountering an unknown name will raise an UnhandledTypeError.

comdab.build_comdab_schema(connection: Connection, *, schema: str = 'public', allow_unknown_types: bool = False) ComdabSchema[source]

Build a database-agnostic, SQLAlchemy-independent representation of a database schema.

This is the internal representation used for comparison by comdab.

Parameters:
  • connection – A SQLAlchemy connection established to the database to reflect.

  • schema – The name of the database schema to reflect.

  • allow_unknown_types – If True, SQL data types not handed (yet) by comdab will be modeled as “unknown” and compared based on their name only. If False (the default), encountering an unknown name will raise an UnhandledTypeError.

comdab.compare_comdab_schemas(left: ComdabSchema, right: ComdabSchema, *, rules: dict[ComdabPath, ComdabStrategy] | None = None) list[ComdabReport][source]

Compare two already built database schemas.

This is a pure function, needing no connection to a database.

Parameters:
  • left – The first schema to compare, as produced by build_comdab_schema().

  • right – The second schema to compare, as produced by build_comdab_schema().

  • rules – An optional set of rules to allow some differences between the schemas, or report them as warnings. See Ignore rules for more details.

comdab.generate_migrations_from_reports(target_schema: ComdabSchema, reports: Iterable[ComdabReport], generator: MigrationGeneratorPort) None[source]

Generate migrations from already build comdab reports.

This is a pure function, needing no connection to a database.

Parameters:
  • target_schema – The comdab-built schema to reach after migrations.

  • reports – The comdab-built reports of differences between the source and target schemas.

  • generator – The MigrationGeneratorPort to use to generate migrations from reports.

comdab.ROOT = ROOT

The top-level comdab path, pointing to a whole ComdabSchema.

It’s attributes are, recursively, the corresponding schema attributes: ROOT.tables['foo'].columns refer to all columns of the table named foo, eg. the dictionary some_schema.tables['foo'].columns.

Dictionary keys can be regular expressions (or ..., equivalent to '.*'), so that ROOT.tables['_.*'].columns refer to all columns of all tables beginning with an underscore.

class comdab.MigrationGeneratorPort[source]

Abstract base class to generate migrations based on comdab reports.

Inherit this class and implement every method with your migration generation backend, then pass it to generate_migrations().

abstractmethod create_table(*, table: ComdabTable) None[source]

Create a new table.

abstractmethod drop_table(*, table: ComdabTable) None[source]

Drop a table.

abstractmethod create_view(*, view: ComdabView) None[source]

Create a new view.

abstractmethod drop_view(*, view: ComdabView) None[source]

Drop a view.

abstractmethod create_sequence(*, sequence: ComdabSequence) None[source]

Create a new sequence.

abstractmethod drop_sequence(*, sequence: ComdabSequence) None[source]

Drop a sequence.

abstractmethod create_function(*, function: ComdabFunction) None[source]

Create a new function.

abstractmethod drop_function(*, function: ComdabFunction) None[source]

Drop a function.

abstractmethod create_custom_type(*, custom_type: ComdabCustomType) None[source]

Create a new custom type.

abstractmethod drop_custom_type(*, custom_type: ComdabCustomType) None[source]

Drop a custom type.

abstractmethod alter_schema_extra(*, old_extra: dict[str, Any], new_extra: dict[str, Any]) None[source]

Change some dialect-specific schema options.

abstractmethod create_column(*, table: ComdabTable, column: ComdabColumn) None[source]

Create a new column.

abstractmethod drop_column(*, table: ComdabTable, column: ComdabColumn) None[source]

Drop a column.

abstractmethod create_constraint(*, table: ComdabTable, constraint: ComdabConstraint) None[source]

Create a new constraint.

abstractmethod drop_constraint(*, table: ComdabTable, constraint: ComdabConstraint) None[source]

Drop a constraint.

abstractmethod create_index(*, table: ComdabTable, index: ComdabIndex) None[source]

Create a new index.

abstractmethod drop_index(*, table: ComdabTable, index: ComdabIndex) None[source]

Drop a index.

abstractmethod create_trigger(*, table: ComdabTable, trigger: ComdabTrigger) None[source]

Create a new trigger.

abstractmethod drop_trigger(*, table: ComdabTable, trigger: ComdabTrigger) None[source]

Drop a trigger.

abstractmethod alter_table_extra(*, table: ComdabTable, old_extra: dict[str, Any], new_extra: dict[str, Any]) None[source]

Change some dialect-specific table options.

abstractmethod alter_view_definition(*, view: ComdabView, old_definition: str, new_definition: str) None[source]

Change a view definition.

abstractmethod alter_view_materialized(*, view: ComdabView, old_materialized: bool, new_materialized: bool) None[source]

Change whether a view is materialized.

abstractmethod alter_view_extra(*, view: ComdabView, old_extra: dict[str, Any], new_extra: dict[str, Any]) None[source]

Change some dialect-specific view options.

abstractmethod alter_sequence_type_name(*, sequence: ComdabSequence, old_type_name: str, new_type_name: str) None[source]

Change a sequence type.

abstractmethod alter_sequence_start(*, sequence: ComdabSequence, old_start: int, new_start: int) None[source]

Change a sequence start point.

abstractmethod alter_sequence_increment(*, sequence: ComdabSequence, old_increment: int, new_increment: int) None[source]

Change a sequence increment.

abstractmethod alter_sequence_min(*, sequence: ComdabSequence, old_min: int, new_min: int) None[source]

Change a sequence minimal value.

abstractmethod alter_sequence_max(*, sequence: ComdabSequence, old_max: int, new_max: int) None[source]

Change a sequence maximal value.

abstractmethod alter_sequence_cycle(*, sequence: ComdabSequence, old_cycle: bool, new_cycle: bool) None[source]

Change whether a sequence cycles.

abstractmethod alter_sequence_extra(*, sequence: ComdabSequence, old_extra: dict[str, Any], new_extra: dict[str, Any]) None[source]

Change some dialect-specific sequence options.

abstractmethod alter_function_definition(*, function: ComdabFunction, old_definition: str, new_definition: str) None[source]

Change a function definition.

abstractmethod alter_function_extra(*, function: ComdabFunction, old_extra: dict[str, Any], new_extra: dict[str, Any]) None[source]

Change some dialect-specific function options.

abstractmethod alter_custom_type_values(*, custom_type: ComdabCustomType, old_values: list[str], new_values: list[str]) None[source]

Change the values of a custom type.

abstractmethod alter_custom_type_extra(*, custom_type: ComdabCustomType, old_extra: dict[str, Any], new_extra: dict[str, Any]) None[source]

Change some dialect-specific custom type options.

abstractmethod alter_column_type(*, table: ComdabTable, column: ComdabColumn, old_type: ComdabType, new_type: ComdabType) None[source]

Change a column type.

abstractmethod alter_column_nullable(*, table: ComdabTable, column: ComdabColumn, old_nullable: bool, new_nullable: bool) None[source]

Change the nullability of a column.

abstractmethod alter_column_default(*, table: ComdabTable, column: ComdabColumn, old_type: str | None, new_type: str | None) None[source]

Change the default value of a column.

abstractmethod alter_column_generation_expression(*, table: ComdabTable, column: ComdabColumn, old_expr: str | None, new_expr: str | None) None[source]

Change the generation expression of a column.

abstractmethod alter_column_extra(*, table: ComdabTable, column: ComdabColumn, old_extra: dict[str, Any], new_extra: dict[str, Any]) None[source]

Change some dialect-specific column options.

abstractmethod alter_constraint_type(*, table: ComdabTable, constraint: ComdabConstraint, old_type: ComdabConstraintType, new_type: ComdabConstraintType) None[source]

Change a constraint type.

abstractmethod alter_constraint_deferrable(*, table: ComdabTable, constraint: ComdabConstraint, old_deferrable: bool | None, new_deferrable: bool | None) None[source]

Change whether a constraint can be deferred.

abstractmethod alter_constraint_initially(*, table: ComdabTable, constraint: ComdabConstraint, old_initially: str | None, new_initially: str | None) None[source]

Change a constraint initial state.

abstractmethod alter_constraint_extra(*, table: ComdabTable, constraint: ComdabConstraint, old_extra: dict[str, Any], new_extra: dict[str, Any]) None[source]

Change some dialect-specific constraint options.

abstractmethod alter_constraint_columns(*, table: ComdabTable, constraint: ComdabPrimaryKeyConstraint | ComdabUniqueConstraint, old_columns: set[str], new_columns: set[str]) None[source]

Change a PK / unique constraint columns.

abstractmethod alter_constraint_columns_mapping(*, table: ComdabTable, constraint: ComdabForeignKeyConstraint, old_columns_mapping: dict[str, str], new_columns_mapping: dict[str, str]) None[source]

Change the columns mapping of a foreign key constraint.

abstractmethod alter_constraint_on_update(*, table: ComdabTable, constraint: ComdabForeignKeyConstraint, old_on_update: str | None, new_on_update: str | None) None[source]

Change the ON UPDATE clause of a foreign key constraint.

abstractmethod alter_constraint_on_delete(*, table: ComdabTable, constraint: ComdabForeignKeyConstraint, old_on_delete: str | None, new_on_delete: str | None) None[source]

Change the ON DELETE clause of a foreign key constraint.

abstractmethod alter_constraint_sql_text(*, table: ComdabTable, constraint: ComdabCheckConstraint, old_sql_text: str, new_sql_text: str) None[source]

Change the clause of a check constraint.

abstractmethod alter_constraint_attributes_and_operators(*, table: ComdabTable, constraint: ComdabExcludeConstraint, old_attributes_and_operators: list[tuple[str, str]], new_attributes_and_operators: list[tuple[str, str]]) None[source]

Change the attributes and operators of a exclusion constraint.

abstractmethod alter_index_expressions(*, table: ComdabTable, index: ComdabIndex, old_expressions: list[str], new_expressions: list[str]) None[source]

Change the expressions of an index.

abstractmethod alter_index_unique(*, table: ComdabTable, index: ComdabIndex, old_unique: bool, new_unique: bool) None[source]

Change if an index is unique.

abstractmethod alter_index_extra(*, table: ComdabTable, index: ComdabIndex, old_extra: dict[str, Any], new_extra: dict[str, Any]) None[source]

Change some dialect-specific index options.

abstractmethod alter_trigger_definition(*, table: ComdabTable, trigger: ComdabTrigger, old_definition: str, new_definition: str) None[source]

Change a trigger definition.

abstractmethod alter_trigger_extra(*, table: ComdabTable, trigger: ComdabTrigger, old_extra: dict[str, Any], new_extra: dict[str, Any]) None[source]

Change some dialect-specific trigger options.

class comdab.PartialMigrationGeneratorPort

Like MigrationGeneratorPort, but does not require all migration methods to be implemented.

By default, NotImplementedError will be raised if a missing method is called; to silently do nothing instead, use class MyGenerator(PartialMigrationGeneratorPort, strict=False).

Reports

class comdab.report.ComdabReport[source]

A difference between two database schemas, reported by comdab.

Should not be instantiated manually.

Examples

  • A column is nullable in the left schema, not in the right:

    ComdabReport(level="error", path=ROOT.tables['foo'].columns['bar'].nullable, left=True, right=False)
    
  • A table is present only in the left schema:

    ComdabReport(level="error", path=ROOT.tables.left_only, left={'foo': ComdabTable(...)}, right={})
    
  • Two columns are present only in the right schema:

    ComdabReport(level="error", path=ROOT.tables['foo'].right_only, left={}, right={'bar': ComdabColumn(...)})
    ComdabReport(level="error", path=ROOT.tables['foo'].right_only, left={}, right={'baz': ComdabColumn(...)})
    
level: Literal['warning', 'error']

Whether the report is for an error (default) or a warning (caused by a custom rule).

path: ComdabPath

The path to the difference between the two schemas.

left: object

The value in the left schema.

right: object

The value in the right schema.

Models

class comdab.models.ComdabSchema[source]

A database schema, the top-level comdab model.

Equivalent to a sqlalchemy.sql.schema.MetaData object.

tables: dict[str, ComdabTable]
views: dict[str, ComdabView]
sequences: dict[str, ComdabSequence]
functions: dict[str, ComdabFunction]
custom_types: dict[str, ComdabCustomType]
extra: dict[str, Any]
class comdab.models.ComdabTable[source]

A database table.

Equivalent to a sqlalchemy.sql.schema.Table object.

name: str
columns: dict[str, ComdabColumn]
constraints: dict[str, ComdabConstraint]
indexes: dict[str, ComdabIndex]
triggers: dict[str, ComdabTrigger]
extra: dict[str, Any]
class comdab.models.ComdabView[source]

A database view.

Equivalent to a sqlalchemy.sql.schema.Table object.

name: str
definition: str
materialized: bool
extra: dict[str, Any]
class comdab.models.ComdabSequence[source]

A database sequence.

Not natively handled by SQLAlchemy.

name: str
type_name: str
start: int
increment: int
min: int
max: int
cycle: bool
extra: dict[str, Any]
class comdab.models.ComdabFunction[source]

A database function.

Equivalent to a sqlalchemy.sql.functions.Function object.

name: str
definition: str
extra: dict[str, Any]
class comdab.models.ComdabCustomType[source]

A database custom type (only Enums supported).

Even types not used in any column are listed here.

name: str
values: list[str]
extra: dict[str, Any]
class comdab.models.ComdabColumn[source]

A database column.

Equivalent to a sqlalchemy.sql.schema.Column object.

name: str
type: ComdabType
nullable: bool
default: str | None
generation_expression: str | None
extra: dict[str, Any]
type comdab.models.ComdabConstraint = ComdabUniqueConstraint | ComdabPrimaryKeyConstraint | ComdabForeignKeyConstraint | ComdabCheckConstraint | ComdabExcludeConstraint
class comdab.models.ComdabUniqueConstraint[source]

A database unique constraint.

Equivalent to a sqlalchemy.sql.schema.UniqueConstraint object.

type: Literal['unique']
columns: set[str]
class comdab.models.ComdabPrimaryKeyConstraint[source]

A database primary key constraint.

Equivalent to a sqlalchemy.sql.schema.PrimaryKeyConstraint object.

type: Literal['primary_key']
columns: set[str]
class comdab.models.ComdabForeignKeyConstraint[source]

A database foreign key constraint.

Equivalent to a sqlalchemy.sql.schema.ForeignKeyConstraint object.

type: Literal['foreign_key']
columns_mapping: dict[str, str]
on_update: str | None
on_delete: str | None
class comdab.models.ComdabCheckConstraint[source]

A database check constraint.

Equivalent to a sqlalchemy.sql.schema.CheckConstraint object.

type: Literal['check']
sql_text: str
class comdab.models.ComdabExcludeConstraint[source]

A database constraint.

Not natively handled by SQLAlchemy.

type: Literal['exclude']
attributes_and_operators: list[tuple[str, str]]
type comdab.models.ComdabConstraintType = Literal['unique', 'primary_key', 'foreign_key', 'check', 'exclude']
class comdab.models.ComdabIndex[source]

A database index.

Equivalent to a sqlalchemy.sql.schema.Index object.

name: str
expressions: list[str]
unique: bool
extra: dict[str, Any]
class comdab.models.ComdabTrigger[source]

A database trigger.

Not natively handled by SQLAlchemy.

name: str
definition: str
extra: dict[str, Any]
class comdab.models.ComdabTypes[source]

Registry of all data types handled by comdab.

Each class member represent a database type, equivalent to a sqlalchemy.types.TypeEngine class.

class String[source]
length: int | None
collation: str | None
class Integer[source]
class Float[source]
class Numeric[source]
precision: int | None
scale: int | None
class Boolean[source]
class DateTime[source]
with_timezone: bool
class Date[source]
class Time[source]
class Interval[source]
second_precision: int | None
day_precision: int | None
class JSON[source]
class Binary[source]
length: int | None
class UUID[source]
class Array[source]
item_type: ComdabType
dimensions: int
class Enum[source]
values: set[str]
type_name: str | None
collation: str | None
class HSTORE[source]
class Range[source]
item_type: ComdabType
class MultiRange[source]
item_type: ComdabType
class Unknown[source]

A type not handled (yet) by comdab, when run with allow_unknown_types=True.

Exceptions

exception comdab.exceptions.ComdabException[source]

The root class of exceptions raised by comdab.

exception comdab.exceptions.UnhandledTypeError[source]

A SQL type not handled by comdab was encountered.

Consider passing allow_unknown_types=True, or opening a feature request!

exception comdab.exceptions.OverlappingPathsError[source]

Several constraint paths overlap.

exception comdab.exceptions.ComdabInternalError[source]

Something unexpected happened!

If it escapes comdab internals, please report a bug.