Dylan Doxey


DBIx::TNDBO - Wrapper for DBI offers concise syntax.


Your Database Module

  package YDB;

  use strict;
  use warnings;
  use base qw( DBIx::TNDBO );

  sub credentials {
      my ($dbname) = @_;
      return {
          user   => 'dylan',
          pass   => 'foobar1',
          driver => 'mysql',
          dbname => 'language',
          host   => 'localhost',
          port   => 3306,


Your MySQL Table

  mysql> desc word;
  | Field   | Type                 | Null | Key | Default           | Extra          |
  | id      | int(11)              | NO   | PRI | NULL              | auto_increment |
  | data    | varchar(100)         | YES  |     | NULL              |                |
  | lang    | enum('en','ja','es') | NO   |     | en                |                |
  | anagram | varchar(100)         | YES  |     |                   |                |
  | created | timestamp            | NO   |     | CURRENT_TIMESTAMP |                |

Your Program

  #!/usr/bin/perl -Tw

  use strict;
  use warnings;
  use YDB qw( :dbname );

  my $word_dbo = YDB();

      {   data => 'hello',
          lang => 'en',
  my $id = $word_dbo->commit();

  my $word_count = YDB();

  my @word_dbos = YDB( { data => 'hello' } );

  my $word_itr = YDB( { data => 'hello' } );

  while ( my $word_dbo = $word_itr->next() ) {

      my $data = $word_dbo->get();

      my $anagram = join '', sort { -1 + int rand 3 } split //, $data;

      $word_dbo->set( $anagram );

      print "$word_dbo\n";


This module is designed to be used as the base class for your own DBI wrapper module. Your module will define the credentials() method which is used to access the database and examine the schema. The schema data is then used to create a schema aware OO interface with abbreviated syntax for generating lists, iterators, and record objects. There is also a uniquely abbreviated syntax for getters and setters.


The use line for your module is where you state the names of the databases you intend to interact with. Otherwise, give the name of the key in the credentials hash which has the database name, with a leading colon.

  use YDB qw( :dbname );

In this case the name (or list of names) is the value keyed by dbname in the credentials hash.

  use YDB qw( language );

In this case your module will be interacting with the database called 'language'.

In these examples the names 'YDB' and 'dbname' are just sample names. You will choose a package name (and database name) which suits you.


Create a new record object.

  my $word_dbo = YDB();

This creates an object with schema relevant getters & setters. In this example we've created an instance of the `word` table in the `language` database.

Where clauses are given with a hash reference which is transformed to SQL via the SQL::Abstract module.

  my $word_dbo = YDB( { data => 'hello' } );

Scalar context implies that you only want a single result. If more than one (or zero) results are matched then undef is returned.

  my @word_dbos = YDB( { data => 'hello' } );

In list context all resuls will be returned. However, if there are likely to be a lot of results you might prefer an iterator object.

  my $word_itr = YDB( { data => 'hello' } );

  while ( my $word_dbo = $word_itr->next() ) {

For a count query you can do this.

  my $word_count = YDB( { data => 'hello' } );


Record Objects

set( $field )

The set method can assume several forms.

  $word_dbo->set_data( 'hello' );
  $word_dbo->set( $data );
  $word_dbo->set( { data => 'hello', lang => 'en' } );
get( $field )

The get method also assumes several forms.

  $data = $word_dbo->get();
  $word_dbo->get( 'data' );
  { data => $word_dbo->get(), lang => $word_dbo->get() }

This sets a record to be deleted on commit.


Changes made to the object are committed to the database as an update or an insert. The object will carp on destroy if there are uncommitted modifications.


Use this to discard the changes made to the object. The object will carp on destroy if there are uncommitted modifications.

Iterator Objects

Standard iterator interface includes:



This module uses source filtering to provide the abbreviated syntax for the different record object constructor types and the get/set operations on record objects.

Therefore the naming conventions are crucial for directing how a given statement should be expaned and also to avoid allowing the source filter to inadvertenly modify unrelated code.


  # _count suffix necessary to indicate select count(*) query.
  my $word_count = YDB();

  # _dbo suffix directs expansion for a normal constructor call.
  my $word_dbo = YDB();

  # _dbos suffix directs expansion for a list constructor call.
  my @word_dbos = YDB();

  # _itr suffix directs expansion for an iterator constructor call.
  my $word_itr = YDB();

  # _dbo suffix ensures unrelated code is not modified.
  my $data = $word_dbo->get();
  $word_dbo->set( $data );


  • Ambiguous table names

    There will be confusion if you have multiple databases which have a table of the same name. For example:

      use YDB qw( database_a database_b );
      my $word_dbo = YDB();

    If there is both a database_a.word table and a database_b.word table then the results in this case are not defined.

  • This module was developed and tested with MySQL. It will build a DSN based on whatever values are returned from your credentials() function. However, there is no accounting for whatever database idiosyncracies there may be.


This module is not the TierraNet DBO module. This is just a tribute.

The key features of this module are accomplished using:

  • source filtering

  • disk caching of schema data

  • dynamic symbol table manipulation at load time.


The SQL::Abstract module already does a great job ad defining a query definition syntax.

The Filter::Simple module makes source filtering a snap.


Dylan Doxey, <dylan@cpan.org>


Copyright (C) 2013 by Dylan Doxey

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.10 or, at your option, any later version of Perl 5 you may have available.