NAME

DBIO::Manual::Heritage - Everything that changed from DBIx::Class to DBIO

VERSION

version 0.900004

OVERVIEW

DBIO is a fork of DBIx::Class. It retains the relational-mapping model and most of the API surface, but replaces, extends, and integrates a significant number of components. This document is the definitive reference for everyone migrating from DBIx::Class, or anyone who needs to understand what DBIO is relative to its ancestor.

Naming

DBIO stands for DBI Objects. The namespace DBIO:: replaces DBIx::Class:: throughout. Every module, every test, every tool has been renamed accordingly.

DBIO is a clean break - there is no runtime DBIx::Class compatibility shim. Existing code is migrated by mechanical rename (DBIx::Class::* to DBIO::*) plus the targeted API updates listed in DBIO::Manual::Migration.

FUNDAMENTAL ARCHITECTURE CHANGES

DBIO::Base - the new meta-infrastructure base

DBIx::Class distributed its meta-level machinery across several internal packages. DBIO consolidates it into DBIO::Base, which every internal class uses as its root:

use mro 'c3' - MRO forced to C3 for the entire inheritance tree
DBIO::Componentised - the load_components() machinery
Class::Accessor::Grouped - mk_group_accessors()
Perl subroutine attributes via MODIFY_CODE_ATTRIBUTES
_skip_namespace_frames for clean stack traces

Do not use DBIO::Base directly in application code. It is the machinery that everything else stands on. For your Result classes use DBIO::Core; for ResultSet classes use DBIO::ResultSet; for schema classes use DBIO::Schema.

SQL::Abstract replaces SQL::Abstract::Classic

DBIx::Class depended on SQL::Abstract::Classic, an older fork. DBIO uses the canonical SQL::Abstract (>= 2.000001) instead. The query API is compatible; the internal extensions required small adjustments in the SQLMaker layer.

apply_limit replaces limit_dialect

DBIx::Class used a sql_limit_dialect string and an emulate_limit() dispatch table inside the storage layer. DBIO removes both. The SQLMaker's apply_limit($sql, $rs_attrs, $rows, $offset) method renders the limit instead. The default implementation emits LIMIT ? OFFSET ?; a driver whose database needs another syntax overrides it in its SQLMaker subclass.

If you had custom emulate_limit() overrides, override apply_limit on your SQLMaker subclass.

Driver distributions split out of core

All database-specific storage classes have been removed from the DBIO core distribution and moved into dedicated CPAN distributions:

DBIO-PostgreSQL    DBIO::PostgreSQL    (most advanced: pg_catalog introspect,
                                        enums, schemas, RLS, indexes)
DBIO-MySQL         DBIO::MySQL
DBIO-SQLite        DBIO::SQLite
DBIO-DuckDB        DBIO::DuckDB        (embedded analytical; UUID, Arrow,
                                        Appender, quack RPC)
DBIO-DB2           DBIO::DB2
DBIO-Firebird      DBIO::Firebird
DBIO-Informix      DBIO::Informix
DBIO-MSSQL         DBIO::MSSQL
DBIO-Oracle        DBIO::Oracle
DBIO-Sybase        DBIO::Sybase

Install only the driver you need. Every driver ships its own native introspection, diff, and deploy modules (the test-and-compare strategy) and does not require SQL::Translator.

Async is an explicit, per-connection mode whose queries return Future objects; each mode's transport is its own distribution. There is also a GraphQL projection layer:

DBIO-Forked             DBIO::Forked              (mode 'forked': fork per
                                                   query, any driver, no loop)
DBIO-Async              DBIO::Async               (mode 'future_io': the
                                                   driver's own async DBD
                                                   binding via Future::IO)
DBIO-PostgreSQL-EV      DBIO::PostgreSQL::EV      (mode 'ev': libpq via EV::Pg)
DBIO-MySQL-EV           DBIO::MySQL::EV           (mode 'ev': MariaDB via
                                                   EV::MariaDB)
DBIO-GraphQL            DBIO::GraphQL             (Result classes -> GraphQL)

Replicated storage is now in core

DBIx::Class::Replicated was an external distribution. DBIO::Replicated ships in the core distribution. Load it as a Schema component; it forces +DBIO::Replicated::Storage and coordinates a master backend plus optional replicants:

package MyApp::Schema;
use DBIO 'Schema', -pg;
__PACKAGE__->load_components('Replicated');

my $schema = MyApp::Schema->connect($dsn, $user, $pass, {
    balancer_type => 'DBIO::Replicated::Balancer::First',
});
$schema->storage->connect_replicants([ $replica_dsn, $user, $pass ]);

Writes, transactions and deploy go through the master; reads are delegated to the balancer (default DBIO::Replicated::Balancer::First). The internals live in DBIO::Replicated::Pool, DBIO::Replicated::Backend, and DBIO::Replicated::Balancer.

SQL::Translator is optional

Every driver ships its own native Deploy, Introspect, and Diff modules. DBIO::Schema->deploy() checks the active storage class for a dbio_deploy_class() method; if present it routes to the native class, which deploys by introspecting both the live database and the desired schema and diffing the two models (test-and-compare). If a storage declares no native class, deploy() does not silently translate the schema for you: it falls through to deployment_statements(), which reads a pre-existing DDL file and throws if none is present. Core's deploy path never calls SQL::Translator, so SQL::Translator is an optional dependency rather than a required one.

RESULT CLASS STYLES

DBIx::Class had one way to write a Result class: the explicit __PACKAGE__->method() style. DBIO keeps that (called Vanilla) and adds two sugar layers.

Vanilla (classic, unchanged API)

package MyApp::Schema::Result::Artist;
use base 'DBIO::Core';
__PACKAGE__->table('artist');
__PACKAGE__->add_columns(
    artistid => { data_type => 'integer', is_auto_increment => 1 },
    name     => { data_type => 'varchar', size => 100 },
);
__PACKAGE__->set_primary_key('artistid');
__PACKAGE__->has_many(cds => 'MyApp::Schema::Result::CD', 'artistid');
1;

See DBIO::Core and DBIO::Manual::ResultClass.

Candy (import-sugar, method renaming)

DBIO::Candy removes the __PACKAGE__-> boilerplate and exports short aliases. It also infers the table name from the package name.

package MyApp::Schema::Result::Artist;
use DBIO::Candy;
column artistid => { data_type => 'integer', is_auto_increment => 1 };
column name     => { data_type => 'varchar', size => 100 };
primary_key 'artistid';
has_many cds => 'MyApp::Schema::Result::CD', 'artistid';
1;

Key aliases exported by Candy:

column             -> add_columns
primary_key        -> set_primary_key
unique_constraint  -> add_unique_constraint
relationship       -> add_relationship

Candy is derived from DBIx::Class::Candy, which has been integrated into the core distribution.

Cake (DDL-like DSL - most concise)

DBIO::Cake reads like schema DDL. Type functions (integer, varchar, serial, uuid, json, jsonb, text, boolean, ...) replace the verbose column-info hashref.

package MyApp::Schema::Result::Artist;
use DBIO::Cake;
col artistid => serial, auto_inc;
col name     => varchar(100);
primary_key 'artistid';
has_many cds => 'MyApp::Schema::Result::CD', 'artistid';
1;

Cake modifiers:

null        auto_inc     fk($target)
default     unsigned     on_create    on_update

Driver shortcuts activate driver-specific column types and components in one import flag:

use DBIO::Cake -Pg;       # PostgreSQL defaults (uuid, jsonb, ...)
use DBIO::Cake -MySQL;
use DBIO::Cake -SQLite;

Other import flags:

-inflate_datetime    load InflateColumn::DateTime automatically
-inflate_json        inflate json columns to hashrefs
-inflate_jsonb       inflate jsonb columns (PostgreSQL)
-retrieve_defaults   fetch server-side defaults after insert

The type registry on the active Storage class (cake_defaults(), type_info($name)) drives driver-aware column type resolution.

INTEGRATED COMPONENTS

The following features were previously separate CPAN distributions or required load_components. In DBIO they are part of core and need no explicit loading unless noted.

DBIO::Timestamp (was DBIx::Class::TimeStamp)

Automatic created_at / updated_at timestamps. Configure per column:

col created_at => timestamp, on_create;
col updated_at => timestamp, on_update;

Or use the helpers available in all three styles:

col_created 'created_at';        # set on insert
col_updated 'updated_at';        # set on insert and update
cols_updated_created();          # both with default names

Available via DBIO::Timestamp. No load_components needed in Cake or Candy; Vanilla code can still load it explicitly.

Row helpers (was DBIx::Class::Helpers)

The following helpers from DBIx::Class::Helpers are integrated directly into DBIO::Row and require no load_components call:

"TO_JSON" in DBIO::Row - serializable hashref, excludes blob/text columns
"serializable_columns" in DBIO::Row - list of serializable column names
"self_rs" in DBIO::Row - a ResultSet scoped to this row
"get_storage_value" in DBIO::Row - original DB value before in-memory changes
"before_column_change" in DBIO::Row - fire a callback before a column is modified
"after_column_change" in DBIO::Row - fire a callback after a column is modified
"around_column_change" in DBIO::Row - wrap a column update with an around-style hook
"proxy_resultset_method" in DBIO::Row - expose a ResultSet with_* method as a row accessor

These come from DBIx::Class::Helper::Row::StorageValues, DBIx::Class::Helper::Row::OnColumnChange, and DBIx::Class::Helper::ResultSet::ProxyResultSetMethod.

ResultSet helpers (was DBIx::Class::Helpers)

The DBIx::Class::Helper::ResultSet::* family is integrated into DBIO::ResultSet and likewise needs no load_components. Upstream these are components loaded per ResultSet class; in DBIO they are core API.

Attribute shortcuts, each of them a chainable ->search(undef, {...}):

"order_by" in DBIO::ResultSet - !col means DESC, -desc => 'col' too
"columns" in DBIO::ResultSet, "add_columns" in DBIO::ResultSet, "remove_columns" in DBIO::ResultSet, "no_columns" in DBIO::ResultSet
"distinct" in DBIO::ResultSet, "group_by" in DBIO::ResultSet, "prefetch" in DBIO::ResultSet, "hri" in DBIO::ResultSet
"rows" in DBIO::ResultSet, "limit" in DBIO::ResultSet, "limited_page" in DBIO::ResultSet, "has_rows" in DBIO::ResultSet

Search shortcuts. A leading dot qualifies the column with the current source alias, so ->null('.title') means me.title:

"null" in DBIO::ResultSet, "not_null" in DBIO::ResultSet, "like" in DBIO::ResultSet, "not_like" in DBIO::ResultSet
"search_or" in DBIO::ResultSet - OR together several ResultSets
"me" in DBIO::ResultSet - the current source alias with a dot appended

Set operations, existence and the rest:

"union" in DBIO::ResultSet, "union_all" in DBIO::ResultSet, "intersect" in DBIO::ResultSet, "intersect_all" in DBIO::ResultSet, "except" in DBIO::ResultSet, "except_all" in DBIO::ResultSet
"results_exist" in DBIO::ResultSet, "results_exist_as_query" in DBIO::ResultSet - cheaper than count
"one_row" in DBIO::ResultSet, "bare" in DBIO::ResultSet, "rand" in DBIO::ResultSet, "explain" in DBIO::ResultSet

Two divergences worth knowing. rand and explain are built on the storage hooks _random_function and _explain_query rather than upstream's DBIx::Introspector, so a driver declares its own dialect. And page is not the upstream shortcut - DBIO kept the DBIx::Class core method, which builds its ResultSet directly instead of routing through search.

DBIx::Class::Helper::ResultSet::IgnoreWantarray was deliberately not taken over; DBIO removes search's context-dependent return type outright instead. Also not integrated: DateMethods1, Errors, AutoRemoveColumns, ResultClassDWIM, and the upstream-deprecated VirtualView and Union. The reasoning for each is recorded in ADR 0011.

IntrospectableM2M (was DBIx::Class::IntrospectableM2M)

Many-to-many introspection is integrated into DBIO::Relationship::ManyToMany. The _m2m_metadata structure is populated automatically when you declare a many_to_many relationship. No load_components needed.

Accessible fields: accessor, relation, foreign_relation, rs_method, add_method, set_method, remove_method.

DBIO::UUIDColumns (was DBIx::Class::UUIDColumns)

Auto-generate UUID values on insert. Load the component and flag the columns with uuid_on_create:

__PACKAGE__->load_components('UUIDColumns');
__PACKAGE__->add_columns(
    id => { data_type => 'varchar', size => 36, uuid_on_create => 1 },
);

Cake's uuid type function is a column type only (data_type => 'uuid'); it does not generate a value.

The default generator makes random version 4 UUIDs from Crypt::URandom. uuid_class() selects Data::UUID or UUID explicitly.

See DBIO::UUIDColumns.

DBIO::EncodedColumn

One-way encoding of column values (passwords with Bcrypt or Argon2, tokens with Digest). Unlike the other components in this chapter it must be loaded:

__PACKAGE__->load_components('EncodedColumn');

Then mark a column:

__PACKAGE__->add_columns(
    password => {
        data_type       => 'varchar',
        size            => 100,
        encode_column   => 1,
        encode_check_method => 'check_password',
    },
);

The column is encoded transparently on write. The check_password (or whatever name you give encode_check_method) method is generated automatically on the Result class.

The default encode_class is Bcrypt (cost 12, stored as $2b$...); Argon2 stores $argon2id$...; Digest (SHA-256, SHA-384, SHA-512 or SHA3-*) stores dbio$<algo>$<salt_b64>$<digest_b64>. An unknown encode_class, an unknown encode_args key of the class or a Digest algorithm outside that list is refused when the class is loaded. A stored value without hash format matches only with accept_unencoded => 1 in encode_args.

See DBIO::EncodedColumn.

DBIO::HashAccessor

Accessor generation for serialized hash columns (JSON, YAML, MessagePack):

__PACKAGE__->add_hash_accessor('metadata', 'metadata_col');

Generates: metadata(), metadata_hash(), metadata_hash_delete(), metadata_push(), metadata_shift().

See DBIO::HashAccessor.

InflateColumn::Serializer (JSON, YAML, MessagePack)

Serialize complex Perl structures to a text/blob column. Available via load_components('InflateColumn::Serializer'). Serializer options: JSON, YAML, MessagePack.

PopulateMore (was DBIx::Class::Schema::PopulateMore)

DBIO::PopulateMore adds populate_more(), which fills several sources in one call and lets a row refer to one created earlier in the same call. Load it on the schema class:

__PACKAGE__->load_components('PopulateMore');

$schema->populate_more([
    { Gender => {
        fields => 'label',
        data   => { male => 'male', female => 'female' },
    } },
    { Person => {
        fields => [ 'name', 'gender' ],
        data   => { john => [ 'john', '!Index:Gender.male' ] },
    } },
]);

Values starting with ! are inflated: !Index:Source.key is the row created under that key, !Env:NAME a value from %ENV, and !Find:Source.[key=val] an existing row. $schema->populate is the plain per-source "populate" in DBIO::Schema.

NEW CONCEPTS

These features did not exist in DBIx::Class in any form.

DBIO::ChangeLog - audit trail per Result class

Automatic row-level change tracking. Add to a Result class:

__PACKAGE__->load_components('ChangeLog');

and to the schema class:

__PACKAGE__->load_components('ChangeLog::Schema');

The schema component registers a companion source <Source>_ChangeLog (table <table>_changelog) for every tracked source; $schema->deploy_changelog deploys those tables. By default every column change is recorded (INSERT, UPDATE, DELETE). Exclude sensitive columns with the changelog column flag:

__PACKAGE__->add_columns(
    password_hash => { data_type => 'varchar', size => 255, changelog => 0 },
);

Changes are stored as JSON. Methods on a row of a tracked Result class:

$row->changelog;                    # ResultSet of this row's log entries
$row->changelog_serialize_changes($hashref);
$row->changelog_deserialize_changes($json_str);

The changes column is text. A driver or application component can override changelog_column_definitions to use a native type (e.g. jsonb on PostgreSQL). The DBIx::Class::Schema::Versioned-style standalone versioning (DBIO::Version) was dropped in favour of DBIO::DeploymentHandler; there is no dbio_schema_versions table. The DBIO::DeploymentHandler version table was renamed from its upstream name dbix_class_deploymenthandler_versions to dbio_deploymenthandler_versions (fork-rename); databases deployed with an older DBIO must be migrated manually (see "COMPATIBILITY: VERSION TABLE RENAME" in DBIO::DeploymentHandler).

See DBIO::ChangeLog and DBIO::ChangeLog::Schema.

DBIO::AccessBroker - credential source interface

A storage-agnostic interface for credential lifecycle management. A broker is a CredentialSource: it supplies the connect info for one backend identity. It does not route and does not own a host list - read/write routing and the master/replicant topology belong to DBIO::Replicated. Pass a broker to connect() in place of a DSN; the storage detects it and attaches it:

my $schema = MyApp::Schema->connect(
    DBIO::AccessBroker::Static->new(
        host     => $host,
        dbname   => $dbname,
        username => $user,
        password => $pass,
    )
);

The storage calls connect_info_for_storage($storage) on the broker before each connection. Static answers connection parameters, not a DSN: the storage builds the DSN for its driver, so the schema must load its driver component. One credential can serve many servers: the application builds a $broker->for_host($host) view per server and hands each to the connection on that host (DBIO::Replicated passes the views through).

Built-in broker classes:

DBIO::AccessBroker::Static - one fixed set of connection parameters
DBIO::AccessBroker::Vault - rotating credentials with TTL
DBIO::AccessBroker::HostBound - one credential identity pinned to one host

Build a custom broker by subclassing DBIO::AccessBroker and implementing connect_info_for_storage, needs_refresh, and refresh.

DBIO::Moo and DBIO::Moose - OO framework bridges

Use Moo or Moose attributes alongside DBIO column accessors in the same Result, ResultSet, or Schema class.

package MyApp::Schema::Result::Artist;
use DBIO::Moo;
use DBIO::Cake;

col artistid => serial, auto_inc;
col name     => varchar(100);
primary_key 'artistid';

has display_name => (is => 'lazy');
sub _build_display_name { 'Artist: ' . $_[0]->name }

1;

DBIO::Moo and DBIO::Moose wire the framework constructor (FOREIGNBUILDARGS for Moo, BUILDARGS for Moose) into DBIO's new() so that DBIO column data is routed correctly and Moo/Moose attributes are filtered from DBIO's column constructor.

Combinations with Cake and Candy are fully supported. Use DBIO::Cake with use DBIO::Moo in the same file.

See DBIO::Moo and DBIO::Moose.

Async storage interface

DBIO::Storage::Async defines a storage-agnostic async interface (Phase 1 + 2). Queries return Future objects. Async is an explicit, per-connection mode -- connect($dsn, $user, $pass, { async => 'forked' | 'future_io' | 'ev' | 'immediate' }) -- and the ev mode bypasses DBI entirely through a per-driver native client:

DBIO::PostgreSQL::EV (DBIO-PostgreSQL-EV) - libpq via EV::Pg; adds COPY IN/OUT and request pipelining.
DBIO::MySQL::EV (DBIO-MySQL-EV) - the MariaDB client via EV::MariaDB, which pipelines consecutive queries automatically at the wire level.

The core async interface is in DBIO::Storage::Async. It includes transaction pinning: txn_do_async takes one connection from the pool and runs the whole transaction on it, and every pooled transport inherits that -- future_io and both ev backends alike. It also includes LISTEN/NOTIFY (ADR 0056): core owns the self-healing listen machinery, and PostgreSQL carries it on both the future_io and the ev transport. Application code that targets multiple backends should program against that interface; the modes and their transports are described in DBIO::Manual::AsyncStorage.

Type registry on Storage

Each driver's Storage class exposes:

$schema->storage->cake_defaults()        # hashref of driver-preferred options
$schema->storage->type_info($type_name)  # { components => [...], cake_options => {...} }

The Cake DSL reads this registry when resolving column types so that, for example, col data => jsonb on a PostgreSQL storage automatically activates InflateColumn::JSONB.

DBIO::Storage::DateTimeFormat - shared driver datetime base

DBIx::Class drivers each hand-rolled a private DateTime::Format::* wrapper package inside their storage class, typically require-ing DateTime::Format::Strptime at runtime. DBIO replaces these with a shared base, DBIO::Storage::DateTimeFormat. A driver's datetime format class declares its strptime patterns as class data and may name a preferred CPAN DateTime::Format::* class:

package DBIO::DriverName::DateTime::Format;
use base 'DBIO::Storage::DateTimeFormat';
__PACKAGE__->preferred_format_class('DateTime::Format::DriverName'); # optional
__PACKAGE__->datetime_parse_pattern('%Y-%m-%d %H:%M:%S');
__PACKAGE__->datetime_format_pattern('%Y-%m-%d %H:%M:%S');

When the preferred class is installed it handles parsing and formatting; otherwise the declared patterns are used via DateTime::Format::Strptime (a hard dependency of core, so drivers declare nothing extra). The pattern fallback round-trips identically to the preferred class.

UTILITY CHANGES

DBIO::Util replaces DBIx::Class::_ENV_

The internal DBIx::Class::_ENV_ pseudo-package has been removed. All runtime constants and utility functions are now in DBIO::Util:

use DBIO::Util qw( is_windows help_url scope_guard ... );

Exported functions include:

is_windows   is_dev_release   old_mro   has_ithreads
dbiotest     peepeeness       shuffle_unordered_resultsets
help_url     iv_size          os_name

sigwarn_silencer   modver_gt_or_eq   assert_no_list_context
refdesc  refcount  hrefaddr  scope_guard
quote_sub  qsub  perlstring  serialize  dump_value
UNRESOLVABLE_CONDITION

dir_path   file_path   parent_dir   slurp_file   write_file
mkpath     rmtree      class_path

firstidx  uniq  apply  array_eq
is_plain_value   is_literal_value

Path::Class is no longer a dependency; use the DBIO::Util path helpers instead.

DBIOx namespace

The DBIOx:: namespace is reserved for official DBIO extensions that are not part of the core distribution - analogous to DBIx:: but scoped to the DBIO ecosystem.

REMOVED FEATURES

Class::DBI compatibility (CDBICompat)

The DBIx::Class::CDBICompat layer and the legacy DB.pm shim have been removed entirely. There is no replacement. If you depend on Class::DBI compatibility, stay on DBIx::Class or port your code.

Hide-from-PAUSE package trick

Internal packages no longer use the package # hide from PAUSE\nFoo::Bar; trick. Meta::NoIndex in the distribution's META.yml handles PAUSE exclusion instead.

Removed external dependencies

SQL::Abstract::Classic - replaced by SQL::Abstract
Path::Class - replaced by helpers in DBIO::Util
Sub::Name - no longer used
SQL::Abstract::Util - merged into SQL::Abstract core

TESTING INFRASTRUCTURE

DBIO::Test - database-free unit testing

Core tests must not use a real database. DBIO::Test provides a mock storage back-end:

use DBIO::Test;
my $schema = DBIO::Test->init_schema();    # fake storage, no DBI

my $storage = $schema->storage;
$storage->mock(qr/SELECT .* FROM artist/, [
    { artistid => 1, name => 'Tori Amos' },
]);

Driver-specific tests (real SQL, real DBI) belong in the driver distributions.

Shared test schemas

The test schema shared across the core and driver distributions is DBIO::Test::Schema under dbio/lib/, with its result classes in DBIO::Test::Schema::*:

DBIO::Test::Schema::Artist
DBIO::Test::Schema::CD
DBIO::Test::Schema::Track
...

The optional OO-framework demo schemas are standalone schema classes and live as siblings of (not under) DBIO::Test::Schema. Keeping them out of the DBIO::Test::Schema::* namespace is deliberate: a no-arg load_classes sweep of that namespace would otherwise try to load them and die on any box lacking the optional dependency (Moose / MooseX::NonMoose / Moo). Tests that use them guard with eval { require ... }.

DBIO::Test::MooSchema         # Moo result classes
DBIO::Test::MooseSchema       # Moose result classes
DBIO::Test::MooCakeSchema     # Moo + Cake DDL
DBIO::Test::MooseSugarSchema  # Moose + Cake DDL

Load driver-specific result classes with the hashref form:

DBIO::Test::Schema->load_classes({
    'DBIO::PostgreSQL::Test' => ['SequenceTest'],
});

Never redefine a shared schema class inline inside a driver test.

Environment variables renamed

All test environment variables were renamed from DBICTEST_* to DBIO_TEST_*:

DBIO_TEST_PG_DSN         PostgreSQL DSN
DBIO_TEST_MYSQL_DSN      MySQL/MariaDB DSN

There is no SQLite equivalent. SQLite needs no connection configuration, so DBIO defines no DBIO_TEST_SQLITE_DSN and nothing reads one: in DBIO::Optional::Dependencies every server engine's live-test group is gated on its own DBIO_TEST_*_DSN, while SQLite carries a plain rdbms_sqlite group with no gate at all.

MIGRATION QUICK-REFERENCE

DBIx::Class concept               DBIO equivalent
---------------------------------------------------------------------
DBIx::Class::*                    DBIO::*
use base 'DBIx::Class::Core'      use base 'DBIO::Core'  (Vanilla)
                                  use DBIO::Candy;       (Candy)
                                  use DBIO::Cake;        (Cake)
sql_limit_dialect / emulate_limit apply_limit on SQLMaker subclass
SQL::Abstract::Classic            SQL::Abstract (>= 2.000001)
DBIx::Class::TimeStamp            DBIO::Timestamp (integrated)
DBIx::Class::Helpers              DBIO::Row + DBIO::ResultSet helpers
                                  (integrated)
DBIx::Class::IntrospectableM2M    ManyToMany (integrated)
DBIx::Class::UUIDColumns          DBIO::UUIDColumns (integrated)
DBIx::Class::Replicated           DBIO::Replicated (in core)
DBIx::Class::CDBICompat           removed (no replacement)
DBICTest::*                       DBIO::Test::*
DBICTEST_* env vars               DBIO_TEST_* env vars
dbix_class_schema_versions        removed (Schema::Versioned dropped;
                                  use DBIO::DeploymentHandler)
dbix_class_deploymenthandler_versions
                                  dbio_deploymenthandler_versions
                                  (fork-rename; migrate old DBs manually)
SQL::Translator (DDL/deploy)      native Deploy per driver (SQLT dropped)

SEE ALSO

DBIO::Manual - documentation overview
DBIO::Manual::DocMap - full documentation map
DBIO::Manual::Migration - step-by-step DBIx::Class to DBIO conversion
DBIO::Base - meta-infrastructure base class
DBIO::Candy - import-sugar layer
DBIO::Cake - DDL-like DSL layer
DBIO::Timestamp - automatic timestamp columns
DBIO::UUIDColumns - automatic UUID columns
DBIO::EncodedColumn - one-way column encoding
DBIO::ChangeLog - row-level audit trail
DBIO::AccessBroker - credential source interface
DBIO::Moo - Moo attribute bridge
DBIO::Moose - Moose attribute bridge
DBIO::Replicated - replicated storage
DBIO::Test - database-free test infrastructure
DBIO::Util - utility functions
DBIO::Storage::DateTimeFormat - shared driver datetime base

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.