[Dbix-class] Re: SELECT .. FOR ... (and
other db-specific clauses).
Matt S Trout
dbix-class at trout.me.uk
Fri Jul 6 11:13:44 GMT 2007
On Fri, Jul 06, 2007 at 10:38:48AM +0900, Daisuke Maki wrote:
> Matt,
>
> per your suggestion, attatched is the revised patch. Basically the same
> logic, but this time the logic is stuffed into DBIC::SQL::Abstract::Pg,
> instead of the storage class.
Patch looks good in theory.
Strikes me we've got a number of sql_maker overrides that are purely to
change the class name now, so lets factor that out.
Also, what other databases support FOR UPDATE ? Is it worth pushing that
one down to DBIC::SQL::Abstract itself ?
> Let me know if there any other problems :)
>
> Regards,
> --d
>
> Matt S Trout wrote:
> >On Thu, Jul 05, 2007 at 02:45:23PM +0900, Daisuke Maki wrote:
> >>good to know that you're aware of these problems. thanks.
> >>
> >>I guess I'll just locally patch my DBIC for now.
> >>I only use Pg, so it does the job for me.
> >
> >If you can redo the patch so it adds the feature via an SQLA subclass ala
> >the way the Oracle WhereJoins subclass works, I'd be willing to accept it
> >for the moment.
> >
> >The thing I disliked is that you were putting SQL in storage.
> >
> >>meanwhile, is the SQL::Abstract discussion carried on elsewhere? I'd
> >>love to at least poke around see what I can contribute.
> >
> >I'm going to get the ball rolling on this list soon - don't see the point
> >in starting another one, we've got a load of ORM authors and interested
> >people on here already :)
> >
>
> Index: t/72pg.t
> ===================================================================
> --- t/72pg.t (revision 3567)
> +++ t/72pg.t (working copy)
> @@ -27,7 +27,7 @@
> plan skip_all => 'Set $ENV{DBICTEST_PG_DSN}, _USER and _PASS to run this test'
> . ' (note: creates and drops tables named artist and casecheck!)' unless ($dsn && $user);
>
> -plan tests => 8;
> +plan tests => 14;
>
> DBICTest::Schema->load_classes( 'Casecheck' );
> my $schema = DBICTest::Schema->connect($dsn, $user, $pass);
> @@ -87,6 +87,83 @@
> my $uc_name_info = $schema->source('Casecheck')->column_info( 'uc_name' );
> is( $uc_name_info->{size}, 3, "Case insensitive matching info for 'uc_name'" );
>
> +# Test SELECT ... FOR UPDATE
> +my $HaveSysSigAction = eval "require Sys::SigAction" && !$@;
> +if ($HaveSysSigAction) {
> + Sys::SigAction->import( 'set_sig_handler' );
> +}
> +
> +SKIP: {
> + skip "Sys::SigAction is not available", 3 unless $HaveSysSigAction;
> + # create a new schema
> + my $schema2 = DBICTest::Schema->connect($dsn, $user, $pass);
> + $schema2->source("Artist")->name("testschema.artist");
> +
> + $schema->txn_do( sub {
> + my $artist = $schema->resultset('Artist')->search(
> + {
> + artistid => 1
> + },
> + {
> + locking => 'update'
> + }
> + )->first;
> + is($artist->artistid, 1, "select for update returns artistid = 1");
> +
> + my $artist_from_schema2;
> + my $error_ok = 0;
> + eval {
> + my $h = set_sig_handler( 'ALRM', sub { die "DBICTestTimeout" } );
> + alarm(2);
> + $artist_from_schema2 = $schema2->resultset('Artist')->find(1);
> + $artist_from_schema2->name('fooey');
> + $artist_from_schema2->update;
> + alarm(0);
> + };
> + if (my $e = $@) {
> + $error_ok = $e =~ /DBICTestTimeout/;
> + }
> +
> + # Make sure that an error was raised, and that the update failed
> + ok($error_ok, "update from second schema times out");
> + ok($artist_from_schema2->is_column_changed('name'), "'name' column is still dirty from second schema");
> + });
> +}
> +
> +SKIP: {
> + skip "Sys::SigAction is not available", 3 unless $HaveSysSigAction;
> + # create a new schema
> + my $schema2 = DBICTest::Schema->connect($dsn, $user, $pass);
> + $schema2->source("Artist")->name("testschema.artist");
> +
> + $schema->txn_do( sub {
> + my $artist = $schema->resultset('Artist')->search(
> + {
> + artistid => 1
> + },
> + )->first;
> + is($artist->artistid, 1, "select for update returns artistid = 1");
> +
> + my $artist_from_schema2;
> + my $error_ok = 0;
> + eval {
> + my $h = set_sig_handler( 'ALRM', sub { die "DBICTestTimeout" } );
> + alarm(2);
> + $artist_from_schema2 = $schema2->resultset('Artist')->find(1);
> + $artist_from_schema2->name('fooey');
> + $artist_from_schema2->update;
> + alarm(0);
> + };
> + if (my $e = $@) {
> + $error_ok = $e =~ /DBICTestTimeout/;
> + }
> +
> + # Make sure that an error was raised, and that the update failed
> + ok(! $error_ok, "update from second schema DOES NOT timeout");
> + ok(! $artist_from_schema2->is_column_changed('name'), "'name' column is NOT dirty from second schema");
> + });
> +}
> +
> END {
> if($dbh) {
> $dbh->do("DROP TABLE testschema.artist;");
> Index: lib/DBIx/Class/Storage/DBI/Pg.pm
> ===================================================================
> --- lib/DBIx/Class/Storage/DBI/Pg.pm (revision 3567)
> +++ lib/DBIx/Class/Storage/DBI/Pg.pm (working copy)
> @@ -1,18 +1,51 @@
> package DBIx::Class::Storage::DBI::Pg;
> -
> use strict;
> use warnings;
> -
> use DBD::Pg qw(:pg_types);
> -
> use base qw/DBIx::Class::Storage::DBI/;
>
> +BEGIN
> +{
> +
> +# Temporary hack until we can fix SQL::Abstract
> +package DBIC::SQL::Abstract::Pg;
> +use strict;
> +use base qw/DBIC::SQL::Abstract/;
> +
> +sub select {
> + my $self = shift;
> + my ($sql, @rest) = $self->SUPER::select(@_);
> +
> + $sql .=
> + $self->{locking} ?
> + (
> + $self->{locking} eq 'update' ? 'FOR UPDATE' :
> + $self->{locking} eq 'share' ? 'FOR SHARE' :
> + ''
> + ) :
> + ''
> + ;
> + return wantarray ? ($sql, @rest) : $sql;
> +}
> +
> +}
> +
> +
> # __PACKAGE__->load_components(qw/PK::Auto/);
>
> # Warn about problematic versions of DBD::Pg
> warn "DBD::Pg 1.49 is strongly recommended"
> if ($DBD::Pg::VERSION < 1.49);
>
> +sub _select {
> + my $self = shift;
> +
> + my $locking = delete $_[3]->{locking};
> + my $sql_maker = $self->sql_maker;
> + local $sql_maker->{locking} = $locking;
> + $self->next::method(@_);
> +}
> +
> sub _dbh_last_insert_id {
> my ($self, $dbh, $seq) = @_;
> $dbh->last_insert_id(undef, undef, undef, undef, {sequence => $seq});
> @@ -73,6 +106,18 @@
> }
> }
>
> +sub sql_maker {
> + my ($self) = @_;
> +
> + unless ($self->_sql_maker) {
> + $self->_sql_maker(
> + new DBIC::SQL::Abstract::Pg( $self->_sql_maker_args )
> + );
> + }
> +
> + return $self->_sql_maker;
> +}
> +
> 1;
>
> =head1 NAME
> _______________________________________________
> List: http://lists.rawmode.org/cgi-bin/mailman/listinfo/dbix-class
> Wiki: http://dbix-class.shadowcatsystems.co.uk/
> IRC: irc.perl.org#dbix-class
> SVN: http://dev.catalyst.perl.org/repos/bast/trunk/DBIx-Class/
> Searchable Archive: http://www.mail-archive.com/dbix-class@lists.rawmode.org/
--
Matt S Trout Need help with your Catalyst or DBIx::Class project?
Technical Director Want a managed development or deployment platform?
Shadowcat Systems Ltd. Contact mst (at) shadowcatsystems.co.uk for a quote
http://chainsawblues.vox.com/ http://www.shadowcatsystems.co.uk/
More information about the Dbix-class
mailing list