From: Gianni Ceccarelli Date: Fri, 28 May 2010 21:03:51 +0000 (+0000) Subject: cursor usage can now be switched on/off X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=590a6c438f5482f79051e433836c2981f98ee769;p=dbsrgits%2FDBIx-Class.git cursor usage can now be switched on/off default off, turn on via (increasing order of importance): - package variable on DBIx::Class::Storage::DBI::Pg - connection attributes - RS attributes also, "page" size used for each fetch from cursor can be tweaked in the same way --- diff --git a/lib/DBIx/Class/Storage/DBI/Pg.pm b/lib/DBIx/Class/Storage/DBI/Pg.pm index 5854644..dae4094 100644 --- a/lib/DBIx/Class/Storage/DBI/Pg.pm +++ b/lib/DBIx/Class/Storage/DBI/Pg.pm @@ -20,6 +20,9 @@ __PACKAGE__->_use_multicolumn_in (1); __PACKAGE__->mk_group_accessors('simple' => '_pg_cursor_number'); +our $DEFAULT_USE_PG_CURSORS=0; +our $DEFAULT_PG_CURSORS_PAGE_SIZE=1000; + sub _determine_supports_insert_returning { return shift->_server_info->{normalized_dbms_version} >= 8.002 ? 1 @@ -247,14 +250,61 @@ sub _get_next_pg_cursor_number { return $ret; } +sub __get_tweak_value { + my ($self,$attrs,$slot,$default,$extra_test)=@_; + + $extra_test||=sub{1}; + + if ( exists $attrs->{$slot} + && defined $attrs->{$slot} + && $extra_test->($attrs->{$slot}) + ) { + return $attrs->{$slot}; + } + my @info=@{$self->_dbi_connect_info}; + if ( @info + && ref($info[-1]) eq 'HASH' + && exists $info[-1]->{$slot} + && defined $info[-1]->{$slot} + && $extra_test->($info[-1]->{$slot}) + ) { + return $info[-1]->{$slot}; + } + return $default; +} + +sub _should_use_pg_cursors { + my ($self,$attrs) = @_; + + return $self->__get_tweak_value($attrs,'use_pg_cursors',$DEFAULT_USE_PG_CURSORS); +} + +sub _get_pg_cursor_page_size { + my ($self,$attrs) = @_; + + return $self->__get_tweak_value($attrs,'pg_cursors_page_size',$DEFAULT_PG_CURSORS_PAGE_SIZE, + sub { $_[0] =~ /^\d+$/ }); +} + +sub _select { + my $self = shift; + my ($ident, $select, $where, $attrs) = @_; + + local $self->{_use_pg_cursors}=$self->_should_use_pg_cursors($attrs); + local $self->{_pg_cursor_page_size}=$self->_get_pg_cursor_page_size($attrs); + + return $self->next::method(@_); +} + sub _dbh_sth { my ($self, $dbh, $sql) = @_; - if ($sql =~ /^SELECT\b/i) { - return DBIx::Class::Storage::DBI::Pg::Sth->new($self,$dbh,$sql); + if ($self->{_use_pg_cursors} && $sql =~ /^SELECT\b/i) { + return DBIx::Class::Storage::DBI::Pg::Sth + ->new($self,$dbh,$sql,$self->{_pg_cursor_page_size}); } else { # short-circuit - return $self->SUPER::_dbh_sth($dbh,$sql); + return $self->next::method($dbh,$sql); } } diff --git a/lib/DBIx/Class/Storage/DBI/Pg/Sth.pm b/lib/DBIx/Class/Storage/DBI/Pg/Sth.pm index 8145135..d34f7e0 100644 --- a/lib/DBIx/Class/Storage/DBI/Pg/Sth.pm +++ b/lib/DBIx/Class/Storage/DBI/Pg/Sth.pm @@ -7,10 +7,11 @@ __PACKAGE__->mk_group_accessors('simple' => 'storage', 'cursor_id', 'cursor_created', 'cursor_sth', 'fetch_sth', + 'page_size', ); sub new { - my ($class, $storage, $dbh, $sql) = @_; + my ($class, $storage, $dbh, $sql, $page_size) = @_; if ($sql =~ /^SELECT\b/i) { my $self=bless {},$class; @@ -24,6 +25,7 @@ sub new { $self->cursor_id($csr_id); $self->cursor_sth($storage->_dbh_sth($dbh,$sql)); $self->cursor_created(0); + $self->page_size($page_size); return $self; } else { @@ -105,7 +107,11 @@ sub _run_fetch_sth { } $self->fetch_sth->finish if $self->fetch_sth; - $self->fetch_sth($self->storage->sth("fetch 1000 from ".$self->cursor_id)); + $self->fetch_sth($self->storage->sth( + sprintf 'fetch %d from %s', + $self->page_size, + $self->cursor_id + )); $self->fetch_sth->execute; } diff --git a/t/72pg_cursors.t b/t/72pg_cursors.t index 17e01fd..1a933ea 100644 --- a/t/72pg_cursors.t +++ b/t/72pg_cursors.t @@ -5,14 +5,13 @@ use warnings; use Test::More; use lib qw(t/lib); use DBICTest; -use Time::HiRes qw(gettimeofday tv_interval); my ($dsn, $dbuser, $dbpass) = @ENV{map { "DBICTEST_PG_${_}" } qw/DSN USER PASS/}; plan skip_all => 'Set $ENV{DBICTEST_PG_DSN}, _USER and _PASS to run this test' unless ($dsn && $dbuser); -plan tests => 3; +plan tests => 10; sub create_test_schema { my ($schema)=@_; @@ -43,11 +42,16 @@ sub drop_test_schema { }); } -# copied from 100populate.t - -my $schema = DBICTest::Schema->connection($dsn, $dbuser, $dbpass, { AutoCommit => 1 }); +my $schema = DBICTest::Schema->connection($dsn, $dbuser, $dbpass, { AutoCommit => 1, use_pg_cursors => 1 }); drop_test_schema($schema);create_test_schema($schema); +my ($called,$page_size)=(0,0); +my $old_sth_new=\&DBIx::Class::Storage::DBI::Pg::Sth::new; +*DBIx::Class::Storage::DBI::Pg::Sth::new=sub { + ++$called;$page_size=$_[4]; + goto &$old_sth_new; +}; + END { return unless $schema; drop_test_schema($schema); @@ -57,7 +61,9 @@ my $start_id = 'populateXaaaaaa'; my $rows=1e4; my $offset = 3; +$called=0; $schema->populate('Artist', [ [ qw/artistid name/ ], map { [ ($_ + $offset) => $start_id++ ] } ( 1 .. $rows ) ] ); +is ($called,0,'Pg::Sth not created for insert'); is ( $schema->resultset ('Artist')->search ({ name => { -like => 'populateX%' } })->count, $rows, @@ -65,25 +71,29 @@ is ( ); { + $called=0; my $rs=$schema->resultset('Artist')->search({}); my $count=0; - my $t0=[gettimeofday]; $count++ while $rs->next; is($count,$rows,'get all the rows (loop)'); - diag('Time for all(loop): '.tv_interval($t0)); + is($called,1,'Pg::Sth called once per rs'); + is($page_size,$DBIx::Class::Storage::DBI::Pg::DEFAULT_PG_CURSORS_PAGE_SIZE,'default page size used'); } { - my $rs=$schema->resultset('Artist')->search({}); - my $t0=[gettimeofday]; + $called=0; + my $rs=$schema->resultset('Artist')->search({},{pg_cursors_page_size=>10}); $rs->first; - diag('Time for first: '.tv_interval($t0)); + is($called,1,'Pg::Sth called again per rs'); + is($page_size,10,'page size from attrs used'); } { + $called=0; my $rs=$schema->resultset('Artist')->search({}); - my $t0=[gettimeofday]; my @rows=$rs->all; is(scalar(@rows),$rows,'get all the rows (all)'); - diag('Time for all: '.tv_interval($t0)); + is($called,1,'Pg::Sth called again per rs'); + is($page_size,$DBIx::Class::Storage::DBI::Pg::DEFAULT_PG_CURSORS_PAGE_SIZE,'default page size used again'); } +