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.