NAME
DBIO::Diff::Compare - Engine-agnostic comparison helpers for DBIO diff operations
VERSION
version 0.900000
DESCRIPTION
Shared "is this the same?" comparison logic for driver diff operations. Every DBIO driver that generates a migration diff has to answer the same question for each candidate change -- "did this column / index / foreign key actually change, or is it identical to what's already deployed?" -- and the mechanics of that answer (normalize undef, collapse type whitespace, compare column lists order-independently, walk a list of fields) are identical across engines. Only the set of fields a given engine cares about is engine-specific.
This module provides the mechanism. "changed_fields" is the generic field-walk; "norm", "norm_type" and "arr_differ" are the primitives it is built on and are exported for drivers that need to compose their own checks. "changed_column_fields", "changed_index_fields" and "changed_fk_fields" are ready-made comparators keyed to the canonical model shape documented in "CANONICAL MODEL" in DBIO::Introspect::Base -- a single-schema driver whose model follows that shape can use them directly; a driver with extra attributes (e.g. MySQL's column_type / collation) calls "changed_fields" with its own field spec instead.
Nothing here is exported by default; import what you need:
use DBIO::Diff::Compare qw(changed_column_fields changed_fields);
my $s = norm($value);
Normalizes a scalar for string comparison: undef becomes the empty string, everything else is returned unchanged.
my $t = norm_type($data_type);
Normalizes a SQL type string for comparison: collapses internal whitespace to single spaces and upper-cases the result (so 'character varying' and 'CHARACTER VARYING' compare equal). undef becomes the empty string. Engine-specific type aliasing (e.g. int vs integer) is not done here -- layer it on top in the driver.
if (arr_differ($old_cols, $new_cols)) { ... }
Order-independent comparison of two array references (treated as sets). Returns true if they differ. undef and non-array values are treated as the empty array. Use this for collections where membership matters but order does not; for ordered collections (composite-key column lists, [precision, scale] pairs) use a dim field in "changed_fields" instead.
my @changed = changed_fields($old, $new,
type => ['data_type'],
scalar => ['default_value'],
bool => ['not_null'],
dim => ['size'],
array => ['columns'],
);
The generic field-walk. $old is the live/introspected side, $new is the target/desired side. Compares two info hashrefs and returns the list of field names that differ, in declared order (deterministic). Each field is compared according to which group it is declared in:
scalar-- string comparison via "norm" (undefequalsundef, butundefdiffers from0).type-- SQL-type comparison via "norm_type" (whitespace-collapsed, case-insensitive).bool-- numeric comparison withundeftreated as0, so an absent flag and an explicit0compare equal.array-- order-independent set comparison via "arr_differ".dim-- order-preserving comparison of a scalar or arrayref (stringified), for ordered lists and[precision, scale]pairs.
Desired-state contract (always on). Any scalar/type/dim/array field whose value in the target $new is undef is skipped -- "the desired state did not prescribe this, so leave whatever the live database reports for it alone." This is unconditional: a portable DBIO schema does not prescribe server-assigned attributes (charset, collation, server-default expressions), so the target leaves them undef and they must not be diffed against the live introspect -- otherwise every upgrade emits a phantom ALTER. The rule fires only when the target side is undef: if both sides are set and differ it is a real change, and if the target prescribes a value the live DB lacks (target set, live undef) it is also a real change. bool fields are never skipped (undef there is a real value: 0).
A desired_state => 1 key is still accepted for back-compat but is now vestigial -- the contract is always on, so passing it has no effect.
my @changed = changed_column_fields($old_info, $new_info);
if (@changed) { ... column changed ... }
Compares two column info hashrefs from the canonical model shape and returns the list of changed field names (empty list = identical). The desired-state contract of "changed_fields" applies: a field the target $new leaves undef is treated as "don't care". Compares data_type as a type, not_null and default_value as scalars, and size order-preservingly (it may be a scalar length or a [precision, scale] pair).
my @changed = changed_index_fields($old_info, $new_info);
Compares two index info hashrefs from the canonical model shape: is_unique (as a bool) and the columns list (order-independently, matching the historical MySQL behaviour). A driver for which index column order is significant should compare columns as a dim field via "changed_fields" instead.
my @changed = changed_fk_fields($old_info, $new_info);
Compares two foreign-key info hashrefs from the canonical model shape: to_table, on_update and on_delete as scalars, and the from_columns / to_columns lists order-independently.
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.
7 POD Errors
The following errors were encountered while parsing the POD:
- Around line 163:
Unknown directive: =func
- Around line 170:
Unknown directive: =func
- Around line 180:
Unknown directive: =func
- Around line 190:
Unknown directive: =func
- Around line 238:
Unknown directive: =func
- Around line 250:
Unknown directive: =func
- Around line 260:
Unknown directive: =func