NAME
Mojar::Mysql::Connector - MySQL connector (dbh producer) with added convenience
SYNOPSIS
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}'
DESCRIPTION
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.
USAGE
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.
Class/object parameters
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
It is unlikely you will want
driver
to be anything other thanmysql
, the default.Common values to use for
host
arelocalhost
or127.0.0.1
.The
port
param should be set if connecting to a port other than3306
.The
schema
param can be set to avoid needing toUSE 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
The
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.The
cnf
param is the credentials file to use, optionally excluding the.cnf
suffix. The connection builder prependscnfdir
+/
to this to generate a fully-qualified filename (as long ascnfdir
is a non-empty string). However, ifcnf
identifies a readable file by itself thencnfdir
is ignored.The
cnfgroup
param is the configuration group to read in addition toclient
. eg If you setcnfgroup => '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
The
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
Using defaults
As you would expect, there is a hierarchy for setting parameters that take effect for new connections.
- 0.
-
Class definition file (.pm)
- 1.
-
'use' params
- 2.
-
connector params
- 3.
-
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.
Class definition
The values in the class definition file are shown above ("Class/object parameters") and should stay static through subsequent released versions.
Use-time parameters
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
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.
Improve the readability of your code by separating the handle configuration from handle usage; you can set your parameters in your one-time initialisation and keep the usage of database handles free of that clutter.
Having easy access to fresh handles may encourage developers to play safe by discarding stale ones; don't worry whether earlier code has changed the
sql_mode
on the handle, treat yourself to a brand new one.Database resources may be better utilised if dormant handles are closed rather than being kept open as long as possible. (On the flip-side, if you are making thousands of new connections per second then your usage needs a rethink.)
Relying on auto_reconnect can be problematic and difficult to debug.
There are no objects or references within a connector, so it can be serialised/deserialised safely and (unlike database handles) can be shared among threads/processes/invocations safely.
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');
...
DBH connection-specific parameters
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.
Inheritance vs parameter overlay
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'
Class methods
new
Mojar::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.
Defaults
print 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_string
Carp::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.
Object methods
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
.)
Attributes
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.
Database handle methods
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_var
my ($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_quotes
The 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_checks
The 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.
SUPPORT
Homepage
http://niczero.github.com/mojar-mysql
Wiki
http://github.com/niczero/mojar/wiki
RATIONALE
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.
SEE ALSO
Coro::Mysql, AnyEvent::DBI, DBIx::Custom, DBIx::Connector.
COPYRIGHT AND LICENCE
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.
3 POD Errors
The following errors were encountered while parsing the POD:
- Around line 603:
Expected text after =item, not a number
- Around line 607:
Expected text after =item, not a number
- Around line 611:
Expected text after =item, not a number