NAME
DBIO::Diff::Op - Base class for DBIO driver diff operation objects
VERSION
version 0.900000
DESCRIPTION
Base class for the per-engine diff operation objects (the Diff::Table, Diff::Column, Diff::Index, Diff::ForeignKey classes each driver ships). Those classes were byte-for-byte identical in their new/accessor boilerplate and 90-95% identical in their diff() create/drop/alter walks across every driver; only as_sql (quoting, ENGINE=, schema qualification, type rendering) is genuinely engine-specific.
This base hosts the shared parts:
"new" and the
actionaccessor, plus "mk_diff_accessors" to declare the rest without hand-rollingsub foo { $_[0]->{foo} }lines."diff_toplevel" -- the create/drop walk for a flat keyed collection (tables: a name exists only in the target -> create, only in the source -> drop; no in-place change).
"diff_nested" -- the create/change/drop walk for members nested under tables (columns, indexes, foreign keys), parameterized by a per-engine "did this member change?" predicate (e.g. the
is_same_*functions from DBIO::Diff::Compare) and per-engine op-construction callbacks."summary_prefix" -- the
+/-/~action glyph used in summaries.
Subclasses keep as_sql (the real engine seam) and a thin summary, and call these helpers from their own diff class method. Drivers whose diff is orchestrated differently (e.g. PostgreSQL's registry-based dispatch, or its global definition-string index comparison) are free to ignore the walk helpers and use only "new"/"mk_diff_accessors"/"summary_prefix".
METHODS
new
my $op = $class->new(action => 'create', table_name => 'foo', ...);
Blesses the argument hash into the operation class.
mk_diff_accessors
__PACKAGE__->mk_diff_accessors(qw/table_name column_name old_info new_info/);
Declares simple hash-slot accessors for the operation class. action is already provided by this base. This is a thin alias over Class::Accessor::Grouped's simple group.
summary_prefix
my $glyph = $op->summary_prefix; # by action, sensible defaults
my $glyph = $op->summary_prefix(add => '*'); # override per action
Returns the one-character glyph for this op's action: + for create/add, - for drop, ~ for alter/modify/change. Pass an action => glyph map to override. Unknown actions default to ~.
diff_toplevel
my @ops = $class->diff_toplevel($source, $target,
create => sub { my ($name) = @_; $class->new(action => 'create', ...) },
drop => sub { my ($name) = @_; $class->new(action => 'drop', ...) },
);
The create/drop walk for a flat collection keyed by name (the table-level diff). $source and $target are hashrefs { $name => $info }. For each name present only in $target the create callback is invoked; for each name present only in $source the drop callback is invoked. Names present in both are left untouched (table identity has no in-place change here; column/index/fk changes are handled by "diff_nested"). Names are walked in sorted order. Each callback may return zero or more ops; all are collected.
diff_nested
my @ops = $class->diff_nested($source, $target,
index_by => 'column_name', # omit if members are already a hash
scope => 'both', # or 'all'
source_tables => $src_model->{tables},
target_tables => $tgt_model->{tables},
skip => sub { my ($m) = @_; ... }, # ignore a member (e.g. auto index)
changed_when => sub { my ($old, $new) = @_; scalar changed_column_fields($old, $new) },
on_new => sub { my ($table, $name, $new) = @_; ... },
on_changed => sub { my ($table, $name, $old, $new) = @_; ... },
on_gone => sub { my ($table, $name, $old) = @_; ... },
);
The create/change/drop walk for members nested one level under a table (columns, indexes, foreign keys). $source and $target are hashrefs { $table => $members }, where $members is either a hashref keyed by member name or an arrayref of member hashes (in which case pass index_by -- the field to key them by).
For each retained table, every member in the target is matched against the source by name: present only in the target -> on_new; present in both and changed_when returns true -> on_changed; present only in the source -> on_gone. changed_when is the per-engine seam -- pass one of the is_same_* predicates from DBIO::Diff::Compare (they return the list of changed fields, hence are true exactly when the member changed). skip, if given, drops a member from consideration on both sides (used to ignore auto-generated primary-key / unique indexes).
scope selects which tables are walked:
both(default) -- only tables present in bothsource_tablesandtarget_tables. Members of brand-new tables are emitted inline by the table-create op, and members of dropped tables vanish with the table, so they are skipped here. Pass the modeltablessections assource_tables/target_tables(they default to$source/$targetif omitted).all-- every target table (regardless of source), plus a trailing pass that emitson_gonefor members of source-only tables. Use this when the member's drop must be emitted even as its table is dropped (e.g. indexes).
Tables and members are both walked in sorted order. Each callback may return zero or more ops; all are collected.
AUTHOR
DBIO & DBIx::Class Authors
COPYRIGHT AND LICENSE
Copyright (C) 2026 DBIO Authors Portions Copyright (C) 2005-2025 DBIx::Class Authors Based on DBIx::Class, heavily modified.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.