Skip to main content

Guide: Compare Schemas

MetaDbDiff supports two comparison modes. Both produce an ordered list of TDDLCommand objects through the same internal pipeline.

Mode 1: Model-to-Database

The master schema is extracted from registered Delphi entity classes via RTTI. The target schema is extracted from the live database.

How it works

  1. TDatabaseMapping (or any subclass inheriting TDatabaseFactory) creates a TCatalogMetadataMIK for the master side by walking RTTI on all registered classes.
  2. It creates a second TCatalogMetadataMIK for the target by querying the live database via the dialect-specific TMetadataDBAbstract extractor.
  3. GenerateDDLCommands(master, target) produces the ordered TDDLCommand list.

Entity registration

Register classes in one of two ways:

Explicit — call TRegisterClass.RegisterEntity (from MetaDbDiff.Mapping.Register) before BuildDatabase. There is no automatic implicit registration triggered by a uses clause; classes must be registered manually.

uses MetaDbDiff.Mapping.Register;

TRegisterClass.RegisterEntity(TCustomer);
TRegisterClass.RegisterEntity(TOrder);
TRegisterClass.RegisterEntity(TOrderItem);

Supported ORM attributes

AttributePurpose
[Table('TABLE_NAME')]Maps the class to a physical table
[Column('COL', ftString, 100)]Maps a property to a column with type and size
[PrimaryKey('COL', TAutoIncType.AutoInc)]Declares the primary key
[ForeignKey('FK_NAME', 'FROM_COL', 'REF_TABLE', 'TO_COL')]Declares a foreign key
[Indexe('IDX_NAME', 'COL1,COL2')]Declares an index
[Sequence('GEN_NAME', 0, 1)]Declares a sequence/generator
[Check('CHK_NAME', 'CONDITION')]Declares a CHECK constraint
[NotNullConstraint]Marks the column as NOT NULL

Mode 2: Database-to-Database

Both schemas are extracted from live databases.

uses
MetaDbDiff.Database.Compare,
DataEngine.Factory.FireDAC;

var
LCompare: TDatabaseCompare;
begin
// Master = source of truth; Target = database to evolve
LCompare := TDatabaseCompare.Create(LSourceConn, LTargetConn);
try
LCompare.BuildDatabase;
finally
LCompare.Free;
end;
end;

TDatabaseCompare connects both databases, extracts both catalogs, then delegates to TDatabaseFactory.GenerateDDLCommands.

What the comparison engine checks

The engine (TDatabaseFactory) performs the following checks in order:

  1. Tables — tables present in target but not in master trigger DROP TABLE; tables in master but not in target trigger CREATE TABLE (with all columns, PK, indexes, checks).
  2. Columns — per-table, columns missing from master trigger DROP COLUMN; columns missing from target trigger ADD COLUMN; columns present in both are deep-compared on: TypeName, Size, Precision, NotNull, AutoIncrement, SortingOrder, DefaultValue, FieldType (normalized), and CharSet.
  3. Default values — compared separately: missing default triggers ALTER COLUMN default set; extra default triggers DROP DEFAULT.
  4. Column positions — only when ComparerFieldPosition = True.
  5. Primary keys — compared by name and column set; mismatches trigger DROP PK + CREATE PK.
  6. Indexes — compared by uniqueness flag and column set; drift triggers DROP INDEX + CREATE INDEX.
  7. CHECK constraints — compared per table (if the dialect supports Checks).
  8. Views — compared by script text; changes trigger DROP VIEW then CREATE VIEW.
  9. Sequences — compared by name only; missing sequences trigger CREATE SEQUENCE, extra sequences trigger DROP SEQUENCE.
  10. Foreign keys — processed last (after all structural changes): compared by referencing table, OnDelete, OnUpdate, and column sets; drift triggers DROP FK + CREATE FK.

Feature guards

The following features are skipped automatically if the dialect does not declare them in SupportedFeatures:

  • Sequences — suppresses sequence creation/deletion
  • ForeignKeys — suppresses FK creation/deletion
  • Checks — suppresses CHECK constraint creation/deletion
  • Views — suppresses view synchronization
  • Triggers — suppresses trigger synchronization

Controlling foreign-key and trigger state during migration

The engine automatically wraps the DDL batch with:

-- disable all FKs
-- disable all triggers
-- ... structural DDL commands ...
-- enable all FKs
-- enable all triggers

These envelope commands are generated by GenerateEnableForeignKeys(False) / GenerateEnableForeignKeys(True) and their trigger equivalents on the dialect generator.