develooper Front page | perl.perl6.language | Postings from July 2005

DBI v2 - Data In and Data Out

John Williams
July 13, 2005 16:23
DBI v2 - Data In and Data Out
Message ID:
The proposals so far have dealt mostly with the SQL itself, and
supporting database-neutral layers on top of DBI.

Personally, I don't mind writing the SQL myself, I rarely need to make
a particular statement work on two databases in my work, and I can
optimize a lot better than any SQL generator.

I like DBI shortcuts (selectrow_array, etc), and I would like them
to become even more convenient in DBIv2, so I have been thinking
about ways to streamline the movement of data in and out of DBI.

A lot of these ideas are probably obvious, but I haven't seen them
actually stated anywhere yet.  A lot may be bad or wrong, which is
where you readers come in...

Data Input

* placeholders (3 types):

	PRO: simple
	     everyone supports this
	CON: each parameter may only be accessed once
		and in the order determined by the SQL
		(rewrite your function and your interface changes)
  :1  :2  :3
	"true" positional placeholders
	PRO: elimiates redundancy (list the same argument twice)
		and dependancy (reorder args if sql changes)
	PRO: easier to manage for dynamic sql
	CON: requires separate bind_param for each

biggest CON: not all are supported by every driver.

DBI could have a (optional?) compatibility layer, which would
translate down for those drivers that do not support :1 or :name,
replacing them with ? and reordering and duplicating the args
if necessary.  I don't think a full SQL parser would be required.

For :name syntax, the execute function (do, selectrow_*, etc)
should do the binding automatically if Pairs are passed as arguments.

   $dbh->do('update table set x = :x where y = :y', undef,
		x => $foo, y => $bar );

* bind_param

I would like to avoid the need to call this if possible.
Perhaps attributes on the parameters could be used to flag
certain things, but I would prefer to do it semi-automatically,
based on the object type of the parameter, as described in
"Data Types on input" below.

* bind_param_array

I think execute_array with ArrayTupleFetch is a better solution.

* execute_array with ArrayTupleFetch

ArrayTupleFetch should accept a (Lazy) Array of Array|Hash as
well as a code block in perl6.

Data Types on input

strings and numbers are handled transparently (good!).

Other types less so: Dates, LOBs, etc.

It would be nice if we could tell DBI how to handle objects of
various types.  If I pass it a DateTime object, format it "this way"
to pass it to the driver.  If I pass it a LOB or IO object, bind it with
the type needed to handle it correctly.

I'm not sure what the best way to do this would be.
Maybe it looks for an object method with a specific name, which
we could define as a multi sub.

   multi sub _dbi_format( DateTime $d ) returns String { ... }

Or maybe we register types with DBI and tell it what to do.

   $dbh->register_type( DateTime, format => &my_format_sub , type => SQL_VARCHAR );

Data Output

ways to get data out:

* fetchrow	get a single row

I would like it if there was a single fetchrow function which returned a
row object.  Or at least if the unsuffixed fetchrow did that.

The row object can be accessed as both an array or a hash,
since query results have both a position and a name.

If the row object is evaluated in Array or Hash context, it should return
a list of values or key=>value pairs, so that it can be easily assigned
to an array or hash.

In String or Number context, it should probably return the value of the
first column.

* fetchall	get multiple rows

This should return a Lazy List (iterator) of row objects.

* selectcol	get column(s) as a list

This is a bit of an oddball, since there is no corresponding fetchcol
function.  But it is a very useful oddball for when you want to turn
a lookup table into a hash, for instance.

I think the thing to do is to unify this with fetchall, by allowing
the $slice parameter to fetchall to be a code block.  The code block
could return a arrayref or hashref to duplicate the existing slice
functionality, or it could return a list of values, a pair, a row object,
or something else entirely.

    %hash = $sth->fetchall( slice => sub($row) { return ($row[0] =>
$row[1]) } );

This could also be added to DBIv1.  A couple of the more obvious
slice funtions could be provided, allowing a nice looking call:

    %hash = $sth->fetchall_array( &as_pairs );

* bind_col

Honestly, I don't use this method personally, so I haven't thought
about it much.  fetchrow_hashref works well enough for me.

* bind_param_inout

I do use this, but only behind an sqlProcedure( $procname, $arg ... )
wrapper, which calls bind_param_inout for any $args which are

This type of binding could be invoked by an attribute on the

    $sth->execute( $arg but inout );

or maybe "but rw", but rw may be overoverloaded already.

Data Types on output

It would be nice to have some datatypes already objectified when
the are returned from DBI.  DATE columns as a DateTime object,
LOB colums as an IO object, etc.

I think this requires more cooperation from the drivers than
any of the previous suggestions. It might also be slower, due to
the need to ask for type information from the database.
So maybe this one is turned on optionally, and not guaranteed to
work with all drivers.

Something which is complementary to the method used to handle
data types on input would be good.

Maybe return values are assigned a role such a SqlInteger, SqlDate,
etc, and users can install multi subs to coerce them to the type
the user desires.  Not sure how well that would work for LOBs.

Or maybe we register a conversion sub with DBI for each database
type we want to be handled specially.

~ John Williams Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About