NAME

DBIO - Native relational mapping for Perl, built on DBI

VERSION

version 0.900004

SYNOPSIS

The classes shown below can also be generated from an existing database with dbiogen, powered by DBIO::Generate.

Schema class

package MyApp::Schema;
use DBIO 'Schema', -pg;

__PACKAGE__->load_namespaces();

1;

Vanilla style (import sugar)

package MyApp::Schema::Result::Artist;
use DBIO;    # Role is auto-detected from the package name: Core

__PACKAGE__->table('artist');
__PACKAGE__->add_columns(qw/ artistid name /);
__PACKAGE__->set_primary_key('artistid');
__PACKAGE__->has_many(cds => 'MyApp::Schema::Result::CD', 'artistid');

1;

The classic equivalent (still supported):

package MyApp::Schema::Result::Artist;
use base 'DBIO::Core';
...

Candy style (import sugar)

DBIO::Candy removes the __PACKAGE__-> boilerplate:

package MyApp::Schema::Result::Artist;
use DBIO::Candy;

table 'artist';
column artistid => { data_type => 'int', is_auto_increment => 1 };
primary_key 'artistid';
column name => { data_type => 'varchar', size => 100 };
has_many cds => 'MyApp::Schema::Result::CD', 'artistid';

1;

Cake style (DDL-like DSL)

DBIO::Cake provides type functions that read like DDL:

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

table 'artist';
col artistid => integer, auto_inc;
col name     => varchar(100);
primary_key 'artistid';
has_many cds => 'MyApp::Schema::Result::CD', 'artistid';

1;

Using your schema

use MyApp::Schema;
my $schema = MyApp::Schema->connect($dbi_dsn, $user, $pass, \%dbi_params);

my @all_artists = $schema->resultset('Artist')->all;

my $johns_rs = $schema->resultset('Artist')->search(
  { name => { like => 'John%' } }
);

# Joins are automatic from relationship conditions
my @rock_cds = $schema->resultset('CD')->search(
  { 'artist.name' => 'John Doe' }
)->all;

# Prefetch related data in a single query
my $millennium_cds_rs = $schema->resultset('CD')->search(
  { year => 2000 },
  { prefetch => 'artist' }
);

my $cd = $millennium_cds_rs->next;
my $artist_name = $cd->artist->name;  # no extra query

# Create, update, delete
my $new_cd = $schema->resultset('CD')->create({ title => 'Spoon' });
$schema->txn_do(sub { $new_cd->update({ title => 'Fork' }) });
$millennium_cds_rs->update({ year => 2002 });

See t/dbio_sugar_pragma.t for a runnable example of the use DBIO import-sugar and role auto-detection shown above.

DESCRIPTION

DBIO (DBI Objects) is a relational mapper for Perl built on top of DBI. It combines an object model for rows and result classes with a resultset API for building queries without giving up database-native behavior.

Three styles are available for defining result classes: DBIO::Cake (DDL-like DSL), DBIO::Candy (import sugar), and the classic Vanilla style (use DBIO; or use base 'DBIO::Core').

Database-specific features are provided by native driver distributions (DBIO::PostgreSQL, DBIO::MySQL, DBIO::SQLite, etc.) that speak each database's dialect natively.

Key features:

  • Automatic joins from relationship conditions

  • Lazy ResultSets that only query when you ask for rows

  • Prefetch for efficient eager loading

  • Multi-column primary and foreign keys

  • Database-level paging, driver-specific SQL features

  • Three result class styles: Cake, Candy, Vanilla

DBIO is pre-1.0. The core API is substantial and usable, but some edges are still being refined. Please report anything that looks wrong or surprising.

METHODS

skill

my $markdown = DBIO->skill('postgresql-database');

Class-level shortcut for "skill" in DBIO::Skills: returns the markdown text of the named agent skill from whatever DBIO distributions are currently loaded, or undef. The leading dbio- is optional.

skills

my @names = DBIO->skills;

Class-level shortcut for "skills" in DBIO::Skills: the sorted names of all skills available from the currently loaded DBIO distributions.

apply_driver

DBIO->apply_driver($caller, 'PostgreSQL');

Applies a driver named $name to $caller by the DBIO::<Name>::{Storage,Result} convention: on a DBIO::Schema subclass it pins $caller->storage_type("+DBIO::${name}::Storage"); on a DBIO::Core subclass it loads the driver's Result component, but only if DBIO::${name}::Result exists. Called by driver shortcut stubs (DBIO::Shortcut::*) and by the use DBIO '-name' fallback path; not normally called directly.

On a schema this declares the driver, exactly as loading the driver's component does: the class pinned is the driver's base storage class, the same for every DBD the driver is reached through. The storage adapts itself to the access path of a connection from there ("_adapt_to_access_path" in DBIO::Storage::DBI, ADR 0059).

driver_for

my $driver = DBIO->driver_for($dsn, $user, $password, \%attrs);  # 'PostgreSQL'
my $driver = DBIO->driver_for(sub { DBI->connect(...) });

Returns the name of the driver a connection leads to -- the <Name> of DBIO::<Name>, as "apply_driver" takes it -- for the cases where it is not known in advance, such as loading a schema from an existing database (ADR 0067). Takes connect info as "connect" in DBIO::Schema does: a DSN with user, password and attributes, a hash with dsn or dbh_maker, or a code reference returning a database handle.

The answer is the exact driver and nothing else: the driver the DBD is registered for ("register_driver" in DBIO::Storage::DBI) -- the DBD named in the DSN, read without connecting, or, for a code reference, the DBD of the handle it returns (the code reference is called for that). No question is put to the server, and no driver is moved to another product.

A DBD that names no driver -- DBD::ODBC, DBD::ADO, a proxy, a DBD nothing is registered for -- throws: a connection through it does not say which engine is behind it. Declare the driver instead; a driver that runs over such an access path knows it and adapts itself to it ("_adapt_to_access_path" in DBIO::Storage::DBI). A driver that registers its DBD only when its storage class is loaded is known after that load.

schema_for

my $schema = DBIO->schema_for($dsn, $user, $password, \%attrs);

Takes the same connect info as "driver_for" and returns a connected schema object with that driver applied ("apply_driver") and no sources -- the starting point for loading a schema from an existing database. It is an ordinary schema with a driver: everything after the connect is the driver's, as for any schema that declares it.

The schema class is DBIO::SchemaFor::<Driver> (for example DBIO::SchemaFor::PostgreSQL), a subclass of DBIO::Schema built on the first call for that driver and shared by every later one. It carries no sources; register sources on the returned object, which has its own source registry, or derive a schema class of your own. For a code reference the reference is called twice: once by "driver_for", once by the connection.

USE-AS-PRAGMA

Since DBIO.pm itself is a sugar pragma (analogous to Moose.pm), it can be used directly to declare a DBIO class. The role to inherit from is auto-detected from the package name, or can be specified explicitly.

package MyApp::Schema::Result::Artist;
use DBIO;                    # -> @ISA = ('DBIO::Core')

package MyApp::Schema::ResultSet::Artist;
use DBIO;                    # -> @ISA = ('DBIO::ResultSet')

package MyApp::Schema;
use DBIO 'Schema', -pg;      # -> @ISA = ('DBIO::Schema'), driver PostgreSQL

package MyApp::Schema::Result::Photo;
use DBIO 'Core';             # explicit override

Shortcuts can be combined with role selection, separated by leading dashes. A shortcut applies a driver: on a Result class it loads the driver's ::Result component, on a Schema it pins the driver's storage class:

use DBIO -pg;                # Core + load the PostgreSQL::Result component
use DBIO 'Schema', -pg;      # Schema + storage_type('+DBIO::PostgreSQL::Storage')

Shortcuts are owned by the driver distributions, not by core; core never names a single driver. A shortcut resolves in two tiers:

  1. An explicit stub DBIO::Shortcut::<key>, which a driver ships for each curated alias (-pg, -mysql, ...). Core lazy-loads it and calls its apply($caller), which usually just delegates to DBIO->apply_driver:

    package DBIO::Shortcut::pg;
    sub apply { DBIO->apply_driver($_[1], 'PostgreSQL') }
    1;
  2. If there is no stub, core falls back to an already-loaded driver whose name matches the key case-insensitively -- so -postgresql resolves to DBIO::PostgreSQL once that driver is loaded, with no stub needed.

apply_driver($caller, $name) applies the driver by convention: on a Schema it pins storage_type('+DBIO::<name>::Storage'); on a Result class it loads the <name>::Result component if the driver ships one. So every driver gets a shortcut (the Schema path works for all), and a ::Result component is just an extra. A shortcut that resolves to nothing dies with a helpful message -- it only works when its driver is installed (and, for the bare canonical name, loaded).

On a schema the shortcut declares the driver, and a schema always declares its driver -- by a shortcut, or by loading the driver's component (__PACKAGE__->load_components('PostgreSQL')), which pins the same class. The declared storage class stands, whatever DSN the schema is connected with: the driver adapts itself to the access path of the connection -- the DBD and client library the DSN leads through -- within its own classes ("_adapt_to_access_path" in DBIO::Storage::DBI), and the storage never becomes the class of another driver. A DSN that leads through the DBD of another driver, or a server the driver notices is not its engine, is reported: a warning by default, an error with "driver_strict" in DBIO::Schema, nothing with driver_warnings(0) ("driver_warnings" in DBIO::Schema).

A schema class that declares no driver is refused when it connects ("connection" in DBIO::Schema, ADR 0067). Where the driver is not known in advance, "driver_for" names the driver of a DSN and "schema_for" returns a connected schema with it.

Role auto-detection rules:

  • package matches /::Result::WORD$/ → role Core

  • package matches /::ResultSet::WORD$/ → role ResultSet

  • anything else → role Core (the most common case)

use DBIO; additionally enables strict and warnings in the caller, matching the behavior of DBIO::Candy, DBIO::Cake, DBIO::Moo and DBIO::Moose.

In a class that is a DBIO::Schema, use DBIO also installs two declaration keywords for the schema's skill overrides (see "skills" in DBIO::Schema):

package MyApp::Schema;
use DBIO 'Schema', -pg;

skills { 'mysql-database' => $markdown, core => $more };  # set the whole map
skill  'postgresql-database' => $markdown;                # add or replace one entry

skills takes a hashref or a key/value list and replaces the map; skill merges a single entry into it. Both keywords are removed from the class once it has compiled, so afterwards MyApp::Schema->skills and MyApp::Schema->skill($name) are the methods inherited from DBIO::Schema.

WHERE TO START

See DBIO::Manual::DocMap for the full documentation map. New users should start with DBIO::Manual::QuickStart.

HERITAGE

DBIO is a fork of DBIx::Class with a clean namespace break. Key changes:

GETTING HELP

CONTRIBUTING

Contributions are welcome: bug reports, documentation improvements, pull requests, or patches.

AUTHORS

DBIO is built on top of DBIx::Class, which was a long-running collaborative effort by many contributors. See the AUTHORS file for the full list.

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.