
Mojar::Mysql::Connector - MySQL connector (dbh producer) with added convenience

In an application making only one type of connection.
use Mojar::Mysql::Connector (
cnfdir => '/var/local/auth/myapp',
cnf => 'rw_localhost',
schema => 'Users'
);
...
my $dbh = Mojar::Mysql::Connector->connect;
In an application making multiple types of connection.
use Mojar::Mysql::Connector (
cnfdir => '/var/local/auth/myapp'
);
my $read_connector = Mojar::Mysql::Connector->new(
cnf => 'ro_remotehost',
schema => 'Orders'
);
my $write_connector = Mojar::Mysql::Connector->new(
cnf => 'rw_localhost',
schema => 'Reports'
);
...
$read_connector->connect->do(q{...});
...
my $read_dbh = $read_connector->connect(
auto_reconnect => 1
);
my $write_dbh = $write_connector->connect;
Employing a helper.
use Mojar::Mysql::Connector (
cnfdir => '/var/local/auth/myapp',
cnf => 'rw_localhost',
schema => 'Users',
-dbh => 1
);
sub do_some_db_doodah {
my $self = shift;
my $dbh = $self->dbh;
...
}
From the commandline.
perl -MMojar::Mysql::Connector="cnf,ro_localhost,schema,Users,-dbh,1"
-E'say join qq{\n}, @{main->dbh->real_tables}'

MySQL-specific extension (subclass) to DBI in order to improve convenience, security, and error handling. Supports easy use of credential/init files, akin to
mysql --defaults-file=$CRED_FILE
It aims to reduce boilerplate, verbosity, mistakes, and parameter overload, but above all it tries to make it quick and easy to Do The Right Thing.
As the name implies, the class provides connector objects -- containers for storing and updating your connection parameters. When you call connect, the connector returns a handle created using its retained parameters plus any call-time parameters you may have used in the call. You don't however have to use connectors and for simple usage it can be easier to use connect directly from the class.

One of the main advantages of using this subclass in place of the standard DBI is the provision of a set of defaults that can be overridden. For instance, a script can define a cnfdir and cnf early on, then redefine cnf for connections to a different server. Various examples of usage are given below ("Using defaults") after the parameters themselves have been introduced.
RaiseError => 1, PrintError => 0, PrintWarn => 0, AutoCommit => 1, TraceLevel => 0, mysql_enable_utf8 => 1, mysql_auto_reconnect => 0, label => undef, cnfdir => '.', cnf => undef, cnfgroup => undef, driver => 'mysql', host => undef, port => undef, user => undef, password => undef, schema => undef
These are the parameters that can be set as class defaults, connector defaults, connection-specific ('one of'), or a combination of those. The following subset are those parameters that govern behaviour of a db connection:
RaiseError PrintError PrintWarn AutoCommit TraceLevel mysql_enable_utf8 mysql_auto_reconnect
"ATTRIBUTES COMMON TO ALL HANDLES" in DBI is the authority on the first five, their effects and when/why to use them, while "DATABASE-HANDLES" in DBD::mysql is the authority on the latter two, their effects and when/why to use them.
Note that unless RaiseError is changed, connections will throw exceptions rather than need their return values checked. This is the most robust style since it is never fooled by genuinely 0 or undef return values, and the exception can be handled at whatever call-level you deem appropriate. (This style works best if you use a try-catch mechanism such as eval {} or do {} in core perl or try/catch in Try::Tiny or Error.)
If your data uses characters that are non-ascii and non-UTF8 then be sure to override mysql_enable_utf8:
mysql_enable_utf8 => 0
The following subset are those parameters that govern making a db connection (DSN) series:
driver host port user password schema
driver to be anything other than mysql, the default.host are localhost or 127.0.0.1.port param should be set if connecting to a port other than 3306.schema param can be set to avoid needing to USE someschema.None of host, port, user, password is required if you are using credentials files (recommended). One of the primary motivations for this (sub)class is to avoid having credentials mixed in with your code.
The following subset is simply to identify a credentials set that will be passed to the DBI (actually DBD::mysql) with the connection string.
cnfdir cnf cnfgroup
cnfdir param is the directory containing credentials files. For convenience (eg during testing) it defaults to . but best practice is to put your credentials in a dedicated directory, taking care of access privileges to keep them secure.cnf param is the credentials file to use, optionally excluding the .cnf suffix. The connection builder prepends cnfdir + / to this to generate a fully-qualified filename (as long as cnfdir is a non-empty string). However, if cnf identifies a readable file by itself then cnfdir is ignored.cnfgroup param is the configuration group to read in addition to client. eg If you set cnfgroup => 'myapp' then connections will use both of the [client] and [myapp] groups. Unless set, only the [client] group is used.Note that currently the credentials file is passed to DBI without being parsed first. (A future version may support parsing parameters from the file.)
Which leaves the following.
label
label param simply holds a string that lets you categorise or tag connectors. The motivation for this is when connection pooling you may want to sub-pool the available connections, but it can also be helpful for debugging.So in summary the parameters most likely to need overriding are
AutoCommit cnfdir cnf schema
As you would expect, there is a hierarchy for setting parameters that take effect for new connections.
Class definition file (.pm)
'use' params
connector params
dbh (connection-specific) params
Each level overlays its definitions over those of the level above so that the value in effect for a connection is the value in the latest level at the time the connection is created. This flexibility might be a bit confusing at first, but examples below should show that individual usage is very simple. It is expected that some users will use only class defaults, some will use only object defaults, some will use a combination, and some will use neither.
The values in the class definition file are shown above ("Class/object parameters") and should stay static through subsequent released versions.
The calling code can override/set defaults upon use.
#!/usr/bin/env perl
use Mojar::Mysql::Connector (
cnfdir => '/srv/myapp/cfg',
cnf => 'myuser_localhost',
schema => 'Stats'
);
That sets the class defaults for the remainder of that script or package, and for simple cases that is the only place your code needs to deal with connection parameters. When using a persistent multi-application environment (mod_perl, plack, hypnotoad, ...) bear in mind that class parameters are shared across the process (perl interpreter instance).
Connectors are objects that you pack with your preferred connection parameters and then have ready to supply you with database connections. Their use is entirely optional, but their advantages include the following.
sql_mode on the handle, treat yourself to a brand new one. use Mojar::Mysql::Connector;
my $connector_source = Mojar::Mysql::Connector->new(
cnfdir => '/srv/myapp/cfg',
cnf => 'myuser_sourcehost',
schema => 'Orders');
my $connector_target = $connector_source->new(
cnf => 'myuser_targethost',
schema => 'Reports');
while (...) {
my $dbh_s = $connector_source->connect;
my $dbh_t = $connector_target->connect;
...
$dbh_s->disconnect;
$dbh_t->disconnect;
}
If on the other hand your code makes several connections to one db server and then all its connections are to a second db server, you can use just one connector and modify its params when appropriate.
my $connector = Mojar::Mysql::Connector->new(cnf => 'myuser_source1');
my $dbh = $connector->connect(schema => 'Preorders';
...
$dbh->disconnect;
$connector->cnf('myuser_source2');
$dbh = $connector->connect;
...
You could of course achieve the same effect in that example using no connectors and overriding in the connect.
my $dbh = Mojar::Mysql::Connector->connect(
cnf => 'myuser_source1',
schema => 'Preorders');
...
$dbh->disconnect;
$dbh = Mojar::Mysql::Connector->connect(
cnf => 'myuser_source2',
schema => 'Profiles');
...
The illustrations above have all shown persisting changes to the defaults at either the class or object level; the new defaults are in effect till the end of the package/script or till they are changed. You may also want to pass parameters that you do not want to persist, a common example being turning off autocommit.
$dbh = Mojar::Mysql::Connector->connect(AutoCommit => 0);
or
$dbh = $connector->connect(AutoCommit => 0);
AutoCommit is 0 for this connection, but defaults are unaffected.
The above illustrates a general principle of the interplay of inheritance and parameter overlay; when an object is created it inherits its base values from the entity it is created from and overlays on top of those any additional parameters it is passed. Exactly as you would expect, these additional parameters have no effect on the entity from which the object was created.
$connector = Mojar::Mysql::Connector->new(label => 'readonly'); $dbh = $connector->new(label => 'readwrite'); say Mojar::Mysql::Connector->label; # still undef say $connector->label; # still 'readonly'
newMojar::Mysql::Connector->new(label => 'cache', cnf => 'myuser_localhost');
Constructor for a connector based on class defaults. Takes a (possibly empty) parameter hash. Returns a connector (Mojar::Mysql::Connector object) the defaults of which are those of the class overlaid with those passed to the constructor.
Defaultsprint Data::Dumper::Dumper(Mojar::Mysql::Connector->Defaults);
Provides access to the defaults hashref that holds the class defaults in order to help debugging. Each default has a getter/setter of the same name, but as described previously, it's fairly risky to change class defaults during runtime if you have other code sharing those defaults.
connect $dbh1 = Mojar::Mysql::Connector->connect(
'DBI:mysql:test;host=localhost', 'admin', 's3cr3t', {});
$dbh2 = Mojar::Mysql::Connector->connect(AutoCommit => 0);
$dbh3 = Mojar::Mysql::Connector->connect;
Constructor for a connection (db handle). If the first element passed has prefix DBI: then it is a DSN string (the traditional route) and so is passed straight to DBI::connect ("DBI Class Methods" in DBI). Otherwise a DSN is first constructed. (The DSN series does not persist and is constructed fresh on each call to connect.)
dsn @dbi_args = Mojar::Mysql::Connector->dsn(
cnf => 'myuser_localhost', schema => 'test');
A convenience method used internally by connect. Takes a (possibly empty) parameter hash. Returns a four-element array to pass to DBI-connect>, constructed from the default values of the constructing class overlaid with any additional parameters passed. Probably the only reason for you using this method is if you need to use DBI directly but want to avoid the inconvenience of constructing the parameters yourself.
use DBI;
use Mojar::Mysql::Connector (
cnfdir => '/srv/myapp/cfg',
cnf => 'myuser_localhost'
);
my $dbh = DBI->connect(
Mojar::Mysql::Connector->dsn(schema => 'foo', AutoCommit => 0));
dsn_as_stringCarp::carp(Mojar::Mysql::Connector->dsn_as_string(@dsn));
A convenience method used internally to chop up the four-element array (particularly the fourth element, the hash ref) into something more readable, eg for error reporting and debugging.
new$connector->new(label => 'transaction', AutoCommit => 0);
Constructor for a connector based on an existing connector's defaults. Takes a (possibly empty) parameter hash. Returns a connector (Mojar::Mysql::Connector object) the defaults of which are those of the given connector overlaid with those passed to the constructor.
connect $dbh = $connector->connect(
'DBI:mysql:test;host=localhost', 'admin', 's3cr3t', {});
$dbh = $connector->connect(AutoCommit => 0);
$dbh = $connector->connect;
Constructor for a connection (db handle). If the first element passed has prefix DBI: then it is a DSN string (the traditional route) and so is passed straight to DBI::connect ("DBI Class Methods" in DBI). Otherwise a DSN is first constructed. (The DSN series does not persist and is constructed fresh on each call to connect.)
All connector parameters are implemented as attributes with exactly the same spelling. So for example you can
$connector->RaiseError(undef); # disable RaiseError $connector->mysql_enable_utf8(1); # enable mysql_enable_utf8
These also function as class attributes, but as previously mentioned, safest to use only as getters rather than class setters.
mysqld_version if ($dbh->mysqld_version =~ /^5.0/) {...}
Returns the version of the db server connected to; the version part of
mysqld --version
thread_id $tmp_table_name = q{ConcurrencySafe_}. $dbh->thread_id;
Utility method to get the connection's thread identifier (unique on that db server at that point in time).
current_schema$schema_name = $dbh->current_schema;
The same string as given by
SELECT DATABASE();
session_varmy ($old) = $dbh->session_var(sql_mode => 'ANSI_QUOTES'); ... $dbh->session_var(sql_mode => $old);
Getter/setter for session variables. To get a value, simply pass the variable's name.
$value = $dbh->session_var('date_format');
In list context returns the old value and the new value; in scalar context returns the handle to facilitate chaining.
$dbh->session_var(var1 => ...)
->session_var(var2 => ...);
disable_quotes my @ddl = $dbh->disable_quotes->selectrow_array(q{SHOW CREATE ...});
Disable optional quotes around identifiers. Currently only affects output of SHOW CREATE TABLE. If you have unsafe identifiers (eg spaces or keywords) then those will still be quoted. Lasts the lifetime of the connection.
enable_quotesThe inverse of disable_quotes.
disable_fk_checks $dbh->disable_fk_checks->do(q{DROP TABLE ...});
Disable foreign key checks. Lasts the lifetime of the connection.
enable_fk_checksThe inverse of disable_fk_checks.
schemata for my $schema (@{$dbh->schemata}) {...}
Returns a hashref of schema names, similar to
SHOW DATABASES
but does not get fooled by lost+found.
tables_and_views foreach my $table ($dbh->tables_and_views) {...}
Returns a hashref of table and view names, similar to
SHOW TABLES
See also "tables" in DBI.
real_tables for my $table (@{$dbh->real_tables}) {...}
Returns a hashref of real table names, similar to
SHOW TABLES
but excluding views.
views for my $view (@{$dbh->views}) {...}
Returns a hashref of view names, similar to
SHOW TABLES
but excluding real tables.

http://niczero.github.com/mojar-mysql
http://github.com/niczero/mojar/wiki

This class was first used in production in 2002. Before then, connecting to databases was ugly and annoying. Setting RaiseError upon every connect was clumsy and irritating. In development teams it was tricky checking that all code was using sensible parameters and awkward ensuring use of risky parameters (eg disable_fk_checks) was kept local. As use of this class spread, it had to be useful in persistent high performance applications as well as many small scripts and the occasional commandline. More recently I discovered the Joy of Mojolicious and employed Mojo::Base to remove unwanted complexity and eliminate a long-standing bug. The ensuing fun motivated an extensive rewrite, fixing broken documentation, improved the tests (thank you travis), and we have, finally, its public release. As noted below there are now quite a few smart alternatives out there but I'm still surprised how little support there is for keeping passwords out of your codebase and helping you manage multiple connections.

Coro::Mysql, AnyEvent::DBI, DBIx::Custom, DBIx::Connector.

Copyright (C) 2002--2013, Nic Sandfield.
This program is free software, you can redistribute it and/or modify it under the terms of the Artistic License version 2.0.