# # Copyright (c) 2015 Christian Jaeger, copying@christianjaeger.ch # # This is free software, offered under either the same terms as perl 5 # or the terms of the Artistic License version 2 or the terms of the # MIT License (Expat version). See the file COPYING.md that came # bundled with this file. # =head1 NAME FP::DBI - DBI with results as lazy lists =head1 SYNOPSIS use FP::DBI; $dbh = FP::DBI->connect($data_source, $username, $auth, \%attr); # same as `DBI`: .. $sth = $dbh->prepare($statement); .. $rv = $sth->execute; # then: my $s= $sth->row_stream; # purearrays blessed to FP::DBI::Row # or #my $s= $sth->array_stream; # arrays # or #my $s= $sth->hash_stream; # hashes use PXML::XHTML; TABLE (TH($s->first->map (*TD)), $s->rest->take (10)->map (sub {TR($_[0]->map (*TD))})) =head1 DESCRIPTION Get rows as items in a lazy linked list (functional stream). NOTE: `DBI` is designed so that when running another `execute` on the same statement handle, fetching returns rows for the new execute; this means, a new execute makes it impossible to retrieve further results from the previous one. Thus if a result stream isn't fully used before a new `execute` or a different result request is being made, then the original stream can't be further evaluated anymore; to prevent this from happening, an interlock mechanism is built in that throws an error in this case. =head1 SEE ALSO L, L =head1 NOTE This is alpha software! Read the status section in the package README or on the L. =cut package FP::DBI; use strict; use warnings; use warnings FATAL => 'uninitialized'; use DBI; use Chj::TEST; use Chj::NamespaceCleanAbove; { package FP::DBI::db; our @ISA= 'DBI::db'; sub prepare { my $s=shift; my $st= $s->SUPER::prepare(@_); bless $st, "FP::DBI::st" } } { package FP::DBI::Row; use FP::PureArray(); # XX oh, ugly, to load the below namespace! use base 'FP::_::PureArray'; } { package FP::DBI::st; our @ISA= 'DBI::st'; use FP::Lazy; use FP::Weak; use FP::List; sub make_X_stream { my ($method, $maybe_mapfn)=@_; sub { my $s=shift; my $id= ++$$s{private_fp__dbi__id}; my $lp; $lp= sub { my $lp=$lp; #keep strong reference lazy { $$s{private_fp__dbi__id} == $id or die ("stream was interrupted by another execute". " or stream request"); if (my $v= $s->$method) { cons ($maybe_mapfn ? &$maybe_mapfn($v) : $v, &$lp); } else { null } } }; Weakened ($lp)->() } } use Chj::NamespaceCleanAbove; sub execute { my $s=shift; $$s{private_fp__dbi__id}++; $s->SUPER::execute (@_) } *row_stream= make_X_stream ("fetchrow_arrayref", sub { bless ([ @{$_[0]} ], "FP::DBI::Row") }); *array_stream= make_X_stream ("fetchrow_arrayref"); *hash_stream= make_X_stream ("fetchrow_hashref"); _END_ } use base 'DBI'; sub connect { my $cl=shift; my $v= $cl->SUPER::connect (@_); bless $v, "FP::DBI::db" } _END_ # namespace cleaning