NAME
Rose::DBx::CannedQuery - Conveniently manage a specific SQL query
SYNOPSIS
use Rose::DBx::CannedQuery;
my $qry = Rose::DBx::CannedQuery->new(rdb_class => 'My::DB',
rdb_params => { type => 'real', domain => 'some' },
sql => 'SELECT * FROM table WHERE attr = ?');
foreach my $row ( $qry->results($bind_val) ) {
do_something($row);
}
sub do_something_repeatedly {
...
my $qry = Rose::DBx::CannedQuery->new_or_cached(rdb_class => 'My::DB',
rdb_params => { type => 'real', domain => 'some' },
sql => 'SELECT my, items FROM table WHERE attr = ?');
# Exdcute query and manage results
...
}
DESCRIPTION
This class provides a convenient means to execute specific queries
against a database fronted by Rose::DB subclasses, in a manner similar
to (and I hope a bit more flexible than) the DBI's "selectall_arrayref"
in DBI method. You can set up the query once, then execute it whenever
you need to, without worrying about the mechanics of the database
connection.
The database connection is not actually made and the query is not
actually executed until you retrieve results or the active statement
handle.
ATTRIBUTES
The specifics of the query are passed as attributes at object
construction. You may specify the database connection in either of two
ways:
rdb_class
rdb_params
These describe, respectively, the Rose::DB-derived class and the
parameters to be passed to that class' "new" in Rose::DB method to
create the Rose::DB object. The "rdb_params" attribute must be a
hash reference, the single-argument shortcut allowed by Rose::DB to
specify just a data source "type" is not supported.
When the Rose::DB>-derived object is created, the information in
rdb_params will be merged with the class' default attributes, with
attributes in rdb_params taking precedence. You may omit
"rdb_params" if "rdb_class" has default domain and type values that
point to a specific datasource; if this isn't the case, Rose::DB
will die noisily.
Rose::DBx::CannedQuery provides a small set of defaults:
{ connect_options =>
{ RaiseError => 1,
PrintError => 0,
AutoCommit => 1
}
}
Subclasses may change or extend these defaults (see below). The
merged parameters are then passed to the "rdb_class"' new_or_cached
constructor.
If a Rose::DBx::CannedQuery object was created by passing in a
Rose::DB database handle directly, the rdb_params attribute will
return "type" and "domain" information only; if you want more
information about the handle, you can call Rose::DB accessor methods
on it directly.
rdb This is the Rose::DB-derived object ("handle") managing the database
connection. It may be supplied at connection time instead of the
rdb_class and rdb_params parameters, if you want to make use of an
already-constructed database handle.
One or the other of these attribute sets must be provided when creating
a Rose::DBx::CannedQuery object.
Other attributes are:
sql The SQL query that this object mediates is supplied as a string.
This attribute is required.
sth The DBI statement handle mediating the canned query. This is a
read-only accessor; a statement handle cannot be specified at object
construction.
CLASS METHODS
new(*%args*)
Create a new Rose::DBx::CannedQuery, taking values for its
attributes from *%args*. In the style of Moose, *%args* may be
either a list of key-value pairs or a single hash reference.
The "sql" attribute is required, as is either "rdb" or enough of
"rdb_class" and "rdb_params" to construct a database handle. If
"rdb" is provided, it will be used regardless of the values in
"rdb_class" and "rdb_params". Otherwise, "rdb_class"'
"new_or_cached" in Rose::DB will be called with the contents of
"rdb_params" as parameters to obtain a new Rose::DB-derived database
object.
new_or_cached(*%args*)
Attempt to retrieve a cached Rose::DBx::CannedQuery matching
*%args*. If successful, return the existing query object. If not,
create a query via "new" and add it to the cache. See "CACHING
QUERIES" for a description of the query cache
OBJECT METHODS
dbh Convenience method that returns the DBI database handle associated
with this object. It is equivalent to "$obj->rdb->dbh".
setup_dbh_for_query
Establishes the DBI database connection. It also sets the
"FetchHashKeyName" in DBI attribute on the handle to "NAME_lc", so
the methods below will by default return hash references with
lowercase keys.
Returns the DBI database handle.
execute([*@bind_args*])
Executes the query, binding the elements of *@bind_args*, if any, to
placeholders in the SQL. Returns a DBI statement handle on success,
and raises an exception on failure.
You should use this method when you want to access the statement
handle directly for detailed control over how the results are
retrieved.
results([*@bind_args*])
Calls "execute", passing *@bind_args*, if any, and then fetches the
results. In scalar context, returns the number of rows fetched. In
array context, returns a list of hash references corresponding to
rows fetched. The keys in each hash are the lower-case column names
(cf. "setup_dbh_for_query"), and the values are the results for that
row, as described for "fetchrow_hashref" in DBI.
resultref([*$bind_args*, *$query_opts*])
This method provides more flexibility than "results", at the cost of
a slightly more complex calling sequence.
Calls "execute", passing the contents of the array referenced by
*$bind_args*, if any, and then fetches the results. If present,
*$query_opts* must be an array reference, whose contents are passed
to "fetchall_arrayref" in DBI. If *$query_opts* is omitted, an empty
hash reference is passed, causing each row of the resultset to be
returned as a hash reference.
Returns the array reference resulting from the call to
"fetchall_arrayref" in DBI.
INTERNAL METHODS
These methods are exposed to facilitate subclassing, and should not
otherwise be used to interact with a Rose::DBx::CannedQuery object.
_default_rdb_params
This method returns a hash reference that supplies default
parameters to be passed to the Rose::DB-derived constructor.
Specific parameters will be overridden by equivalent keys in the
"rdb_params" attribute.
_retcon_rdb_class
This method is used to generate the value of the "rdb_class"
attribute iff the object was constructed using an existing
Rose::DB-derived object rather than connection parameters.
_retcon_rdb_params
This method is used to generate the value of the "rdb_params"
attribute iff the object was constructed using an existing
Rose::DB-derived object rather than connection parameters. As noted
above, it is perhaps useful for reference, but is under no
obligation to accurately reproduce all of the parameters necessary
to construct a Rose::DB handle just like the one owned by this
object.
_init_rdb
Given the connection information in "rdb_class" and "rdb_params",
construct a Rose::DB-derived handle for the "rdb" attribute. If
necessary, you should arrange for "rdb_class" to be loaded. An
exception should be raised on failure; the Rose::DB constructor
usually does this for you.
_init_sth
Given a validly constructed and connected Rose::DBx::CannedQuery,
create a prepared DBI statement handle for the query in "sql". On
success, you should return the statement handle. On failure, you
should raise an exception.
BUILDARGS
The Rose::DBx::CannedQuery BUILDARGS simply checks that either a
Rose::DB-derived handle or the necessary connection class and
parameters are provided.
_query_cache([*$query_cache_object*)
If called without any parameters, returns the query cache currently
in use. In this form, may be called as a class or object method, but
remember that the cache is class-wide, no matter how you retrieve
it.
If called with *$query_cache_object*, the current query cache (if
any) is cleared, and the query cache is set to
*$query_cache_object*, which must conform to the API implemented by
Rose::DBx::CannedQuery::SimpleQueryCache. For simple variations on
the default behavior, you may be better served by supplying an
appropriately reconfigured Rose::DBx::CannedQuery::SimpleQueryCache
instance than by writing a new cache class.
If you have not set the query cache explicitly (or if you set it to
"undef"), an instance of Rose::DBx::CannedQuery::SimpleQueryCache
will be lazily constructed using its default behaviors when a cache
is needed.
CACHING QUERIES
Since one of the common uses for canned queries is execution of a
prepared SQL statement whenever a function is called,
Rose::DBx::CannedQuery provides a (very!) simplistic cache to keep
queries around without requiring each place that might need the query to
maintain state. You can use "new_or_cached" as an alternative to the
regular "new" constructor, and it will return to you the cached version
of a query, if any, in preference to creating a new one.
There are a few important limitations of this caching mechanism to keep
in mind:
* there is a single class-level query cache, so there will be at most
one query object compatible with any given set of parameters passed
to "new_or_cached" at a time. This cached object is returned in
whatever state the last user left it, so it may have already
"execute"d using a particular set of bind values, or be in the midst
of fetching a resultset.
This may be construed as a feature, if you want to be able to pick
up where you left off in collecting results, but be careful if you
plan to retrieve the same query from multiple places.
* the key used to determine whether a query is in the cache is up to
the cache class, which is passed the arguments that were given to
"new_or_cached". The default key generating function for
Rose::DBx::CannedQuery::SimpleQueryCache simply serializes the
arguments as a string. This means that two calls that refer to the
same conceptual database operation in different ways (e.g. one which
says "SELECT a FROM mytable ..." and another which says "SELECT a
FROM mytable tab ...") will result in creation and caching of two
queries. Other cache classes may be smarter.
It also means the cache is not aware of any bind parameter values,
so it's not possible to simultaneously cache the same query being
executed with different bind parameters.
* Rose::DBx::CannedQuery::SimpleQueryCache (q.v.) makes an attempt to
insure that a cached query hasn't been disconnected since it was
last used. However, the checks err on the side of low overhead
rather than comprehensiveness, and aren't foolproof. If you plan to
leave queries untouched in the cache for a long time, you need to
account for the possibility that you'll get a stale query back (or
you might want to avoid the cache altogether, since the benefit is
likely smaller).
If your application typically handles bursts of work with intervals
of rest in between (e.g. in responding to incoming requests), you
may benefit from caching queries while working, then explicitly
clearing the cache (e.g. by calling
"Rose::DBx::Cannedquery->_query_cache->clear") at the end of each
cycle.
EXPORT
None.
DIAGNOSTICS
Any message produced by an included package, as well as
Need either Rose::DB object or information to constuct one (F)
The constructor was called without either a "rdb" attribute or
necessary "rdb_class" and "rdb_params" attributes.
Failed to load class (F)
The Rose::DB-derived class specified by "rdb_class" either couldn't
be found or didn't load successfully.
Error preparing query (F)
Something went wrong when trying to "prepare" in DBI the DBI
statement handle using "sql".
Error executing query (F)
A problem was encountered trying to "execute" in DBI the prepared
query. This could be a sign of a database problem, or it may reflect
pilot error, such as passing the wrong number of bind parameters.
Can't recover Rose::DB class information (F)
Can't recover Rose::DB datasource information (F)
Somehow we managed to get an object with neither "rdb_class" or a
"rdb" handle. This shouldn't happen; it probably means a subclass
overrode BUILDARGS and forgot to call the superclass method.
BUGS AND CAVEATS
All query results are prefetched by the "results" and "resultref"
methods; if you want to iterate over a potentially large resultset,
you'll need to call appropriate DBI methods on the statement handle
returned by "execute".
The default connection parameters include "RaiseError", and the
exceptions thrown when "_init_sth" or "execute" fails also include the
error information, so you may see it twice. Better that than not at all,
if you happen to have changed the connection options in "rdb_params".
BUT *WHY?*
You might think, "What's the point? How hard can it be to write a little
wrapper around straight DBI calls? Anybody who uses a database has done
that already. Why should I bloat my dependency chain with Rose::DB and
some object system?" or "Why is this different from any of the other ORM
or SQL-simplifier packages out there?" And you may well be right, if
you're dealing with a single database connection, or are already up to
your elbows in DBI calls.
However, I find that this lands in a "sweet spot" for my coding style. I
find myself dealing with several databases on a recurring basis, and
Rose::DB is a handy way to wrap up connection information and
credentials so they're easy to use elsewhere. But when I just want to
pull some data, I don't necessarily need the weight of an ORM.
Rose::DBx::CannedQuery cuts down on the boilerplate I need to make these
queries, and lets me keep the credentials separate from the code
(particularly when using Rose::DBx::MoreConfig), without adding the
overhead of converting the results into objects.
Then there's the question, "What's with the Moo stuff?" Sure,
Rose::DBx::CannedQuery could be written using only "core" Perl
constructs. But again, I find the Mooy sugar makes the code cleaner for
me, and easier for someone else to subclass if they want to.
In the end, I hope Rose::DBx::CannedQuery makes your life sufficiently
easier that you find it worth using. If it's close, but you think it's
not quite there, suggestions (better still, patches!) are happily
received.
SEE ALSO
Rose::DB and DBI for more detailed information on options for managing a
canned query object.
Rose::DBx::MoreConfig for an alternative to vanilla Rose::DB that lets
you manage configuration data (such as server names and credentials) in
a manner that plays nicely with many CI and packaging sytems.
If you're using Rose::DB::Object as an ORM, see
"make_manager_method_from_sql" in Rose::DB::Object::Manager for a
similar apprach that produces objects rather than raw results.
Moo (or Moose), if you're interested in subclassing
Rose::DBx::CannedQuery::SimpleQueryCache for the default query cache
Rose::DBx::CannedQuery::Glycosylated for slightly more sugary variant
VERSION
version 1.00
AUTHOR
Charles Bailey <
[email protected]>
COPYRIGHT AND LICENSE
Copyright (C) 2015 by Charles Bailey
This software may be used under the terms of the Artistic License or the
GNU General Public License, as the user prefers.