Massive rewrite of bind handling, and overall simplification of ::Storage::DBI
authorPeter Rabbitson <ribasushi@cpan.org>
Wed, 26 Jan 2011 13:03:22 +0000 (14:03 +0100)
committerPeter Rabbitson <ribasushi@cpan.org>
Wed, 30 Mar 2011 10:24:10 +0000 (12:24 +0200)
commit0e773352a9c6c034dfb2526b8d68bf6ac1e2323b
treef1b56a84cde32ee31132fa1e7669a4f5e9f88c69
parent402ac1c9aa0b5bb5120ee8f6d8e62298a7a14223
Massive rewrite of bind handling, and overall simplification of ::Storage::DBI

There's no practical way to split this into smaller pieces so here it goes:

Bind attribute handling was badly integrated into dbic almost from the
start. Until now the only information about a value was encoded as the
column name contained as the first element of the bind arrayref. The
column name was then resolved to the proper colinfo (deep in ::Storage::DBI)
and then a match was ran on the datatype to try to find an appropriate
set of bind attributes. Besides being fragile and inefficient, this
method also broke down completely when:
  * No column name could be associated with a bind (arguments to complex
    literal functions)
  * as_query results would encode the column names that can no longer
    be resolved since the inner result sources are no longer visible

To fix this all up and provide more flexibility the standard [ $col => $val ]
was replaced with [ \%args => $val ]. The format of \%args is currently:

  {
    dbd_attrs => '
      If present (in any form) this is what is being passed directly to
      bind_param. Note that different DBD's expect different bind args,
      e.g. DBD::SQLite takes a single numerical type, while DBD::Pg takes
      a hashref if bind options. If this is specified all other bind
      options described below are ignored
    ',
    sqlt_datatype => '
      If present it is used to infer the actual bind attribute by passing
      to $resolved_storage->bind_attribute_by_data_type(). Note that the
      data type is somewhat freeform (hence the sqlt_ prefix) - currently
      drivers are expected to dtrt when given a common datatype name (not
      ideal, but that's what we got at this point). Defaults to the
      "data_type" from the add_columns colinfo.
    ',
    sqlt_size => '
      Currently used to correctly allocate buffers for bind_param_inout().
      Defaults to "size" from the add_columns colinfo, or to a sensible value
      based on the "data_type"
    ',
    dbic_colname => '
      Used to fill in missing sqlt_datatype and sqlt_size attributes (if
      they are explicitly specified they are never overriden). Also used
      by some weird DBDs where the column name should be available at
      bind_param time (hello Oracle).
    ',
  }

For backcompat/convenience the following shortcuts are supported:

  [ $name => $val ] === [ { dbic_colname => $name }, $val ]

  [ \$dt => $val ] === [ { sqlt_datatype => $dt }, $val ]

  [ undef => $val ] === [ {}, $val ]

  ( pending in the next patch: [ $val ] === [ {}, $val ] )

On each passage through the storages (either for execute or for as_query
formatting) the information is filled in whenever available, so that by
the time the final binds_param takes place ::Storage::DBI::_dbi_attrs_for_bind
has all the available information about a particular bind value (no matter
where it came from).

A side efect of this is that as_query now always returns resolved
[ \%args => $val ] forms of bind values (hence the huge amount of test changes
in this patchset). While it should not be a major concern, it could
potentially throw off tests that expect a specific output of as_query. If
this becomes a problem a "compat mode as_query" flag will be introduced asap.

Additional changes in this patchset are:

* The signatures of pretty much the entire execution chain changed. Luckily
  everything that required changing was private. All drivers were adjusted
  appropriately (though something could have been missed). Affected methods
  on ::Storage::DBI are:

  _prep_for_execute
  _dbh_execute
  _execute
  _select_args_to_query
  _max_column_bytesize

  additionally the invocation of _prep_for_execute moved from _dbh_execute
  to _execute, and the return of _select_args also changed

* source_bind_attributes was deprecated. Luckily it was never documented in
  the main documentation. Sadly it was documented in individual storage
  drivers. As such it was necessary to provide a compat shim that would invoke
  the thing if it is detected (with the approproate warning)

* _fix_bind_params was renamed to _format_for_trace
35 files changed:
Changes
lib/DBIx/Class/Storage/DBI.pm
lib/DBIx/Class/Storage/DBI/ADO/Microsoft_SQL_Server.pm
lib/DBIx/Class/Storage/DBI/AutoCast.pm
lib/DBIx/Class/Storage/DBI/MSSQL.pm
lib/DBIx/Class/Storage/DBI/NoBindVars.pm
lib/DBIx/Class/Storage/DBI/ODBC/ACCESS.pm
lib/DBIx/Class/Storage/DBI/Oracle/Generic.pm
lib/DBIx/Class/Storage/DBI/Replicated.pm
lib/DBIx/Class/Storage/DBI/SQLite.pm
lib/DBIx/Class/Storage/DBI/Sybase/ASE.pm
t/73oracle_hq.t
t/746mssql.t
t/93autocast.t
t/count/count_rs.t
t/count/prefetch.t
t/lib/DBICTest/Schema/FourKeys.pm
t/prefetch/correlated.t
t/prefetch/count.t
t/prefetch/grouped.t
t/prefetch/o2m_o2m_order_by_with_limit.t
t/prefetch/standard.t
t/prefetch/with_limit.t
t/relationship/core.t
t/resultset/as_query.t
t/resultset/as_subselect_rs.t
t/resultset/bind_attr.t
t/search/related_strip_prefetch.t
t/search/subquery.t
t/sqlmaker/bind_transport.t
t/sqlmaker/limit_dialects/generic_subq.t
t/sqlmaker/limit_dialects/rno.t
t/sqlmaker/limit_dialects/toplimit.t
t/sqlmaker/order_by_bindtransport.t
t/storage/source_bind_compat.t [new file with mode: 0644]