Commit a7495ada authored by Jason Cooper's avatar Jason Cooper

Added in additional logic to handle the situation where a result set doesn't...

Added in additional logic to handle the situation where a result set doesn't have field names defined until after the callback has been executed.
parent f55355f3
Pipeline #91977671 passed with stage
in 2 minutes and 48 seconds
......@@ -878,6 +878,75 @@ All functionality listed here is highly experimental and should be used with gre
$dbh->{mock_clear_table_info} = 1;
- Result Set Callbacks
If you need your result sets to be more dynamic (e.g. if they need to return different results based upon a bound parameter) then you can use a callback.
$dbh->{mock_add_resultset} = {
sql => 'SELECT a FROM b WHERE c = ?',
callback => sub {
my @bound_params = @_;
my %result = (
fields => [ "a" ],
rows => [[ 1] ]
);
if ($bound_params[0] == 1) {
$result{rows} = [ [32] ];
} elsif ($bound_params[0] == 2) {
$result{rows} = [ [43] ];
}
return %result;
},
};
my $sth = $dbh->prepare('SELECT a FROM b WHERE c = ?');
my $rows = $sth->execute(1);
my ($result) = $sth->fetchrow_array(); # $result will be 32
$rows = $sth->execute(2);
($result) = $sth->fetchrow_array(); # $result this time will be 43
$rows = $sth->execute(33); # $results this time will be 1
($result) = $sth->fetchrow_array();
The callback needs to return a hash with a `rows` key that is an array ref of arrays containing the values to return as the answer to the query. In addition a `fields` key can also be returned with an array ref of field names. If a `fields` key isn't present in the returned the hash then the fields will be taken from the `mock_add_resultset`'s `results` parameter.
$dbh->{mock_add_resultset} = {
sql => 'SELECT x FROM y WHERE z = ?',
results => [ ["x"] ],
callback => sub {
my @bound_params = @_;
my %result = ( rows => [[ 1] ] );
if ($bound_params[0] == 1) {
$result{rows} = [ [32] ];
} elsif ($bound_params[0] == 2) {
$result{rows} = [ [43] ];
}
return %result;
},
};
my $sth = $dbh->prepare('SELECT x FROM y WHERE z = ?');
my $rows = $sth->execute(1);
my ($result) = $sth->fetchrow_array(); # $result will be 32
$rows = $sth->execute(2);
($result) = $sth->fetchrow_array(); # $result will be 43
$rows = $sth->execute(33);
($result) = $sth->fetchrow_array(); # $result will be 1
By default result sets which only define their field names in their callback return values will have a `NUM_OF_FIELDS` property of 0 until after the statement has actually been executed. This is to make sure that DBD::Mock stays compatible with previous versions. If you need the `NUM_OF_FIELDS` property to be undef in this situation then set the `$DBD::Mock::DefaultFieldsToUndef` flag to 1.
# BUGS
- Odd $dbh attribute behavior
......@@ -894,8 +963,6 @@ All functionality listed here is highly experimental and should be used with gre
I would like to have the DBD::Mock::StatementTrack object handle more of the mock\_\* attributes. This would encapsulate much of the mock\_\* behavior in one place, which would be a good thing.
I would also like to add the ability to bind a subroutine (or possibly an object) to the result set, so that the results can be somewhat more dynamic and allow for a more realistic interaction.
# SEE ALSO
[DBI](https://metacpan.org/pod/DBI)
......
......@@ -36,6 +36,10 @@ our $drh = undef; # will hold driver handle
our $err = 0; # will hold any error codes
our $errstr = ''; # will hold any error messages
# Defaulting a result set's fields to undef changes the way DBD::Mock responds, so we default it to off
our $DefaultFieldsToUndef = 0;
sub driver {
return $drh if defined $drh;
my ( $class, $attributes ) = @_;
......@@ -1071,6 +1075,78 @@ To clear the current mocked table info set the database handle's C<mock_clear_ta
$dbh->{mock_clear_table_info} = 1;
=item Result Set Callbacks
If you need your result sets to be more dynamic (e.g. if they need to return different results based upon a bound parameter) then you can use a callback.
$dbh->{mock_add_resultset} = {
sql => 'SELECT a FROM b WHERE c = ?',
callback => sub {
my @bound_params = @_;
my %result = (
fields => [ "a" ],
rows => [[ 1] ]
);
if ($bound_params[0] == 1) {
$result{rows} = [ [32] ];
} elsif ($bound_params[0] == 2) {
$result{rows} = [ [43] ];
}
return %result;
},
};
my $sth = $dbh->prepare('SELECT a FROM b WHERE c = ?');
my $rows = $sth->execute(1);
my ($result) = $sth->fetchrow_array(); # $result will be 32
$rows = $sth->execute(2);
($result) = $sth->fetchrow_array(); # $result this time will be 43
$rows = $sth->execute(33); # $results this time will be 1
($result) = $sth->fetchrow_array();
The callback needs to return a hash with a C<rows> key that is an array ref of arrays containing the values to return as the answer to the query. In addition a C<fields> key can also be returned with an array ref of field names. If a C<fields> key isn't present in the returned the hash then the fields will be taken from the C<mock_add_resultset>'s C<results> parameter.
$dbh->{mock_add_resultset} = {
sql => 'SELECT x FROM y WHERE z = ?',
results => [ ["x"] ],
callback => sub {
my @bound_params = @_;
my %result = ( rows => [[ 1] ] );
if ($bound_params[0] == 1) {
$result{rows} = [ [32] ];
} elsif ($bound_params[0] == 2) {
$result{rows} = [ [43] ];
}
return %result;
},
};
my $sth = $dbh->prepare('SELECT x FROM y WHERE z = ?');
my $rows = $sth->execute(1);
my ($result) = $sth->fetchrow_array(); # $result will be 32
$rows = $sth->execute(2);
($result) = $sth->fetchrow_array(); # $result will be 43
$rows = $sth->execute(33);
($result) = $sth->fetchrow_array(); # $result will be 1
By default result sets which only define their field names in their callback return values will have a C<NUM_OF_FIELDS> property of 0 until after the statement has actually been executed. This is to make sure that DBD::Mock stays compatible with previous versions. If you need the C<NUM_OF_FIELDS> property to be undef in this situation then set the C<$DBD::Mock::DefaultFieldsToUndef> flag to 1.
=back
=head1 BUGS
......@@ -1095,8 +1171,6 @@ Each DBD has its own quirks and issues, it would be nice to be able to handle th
I would like to have the DBD::Mock::StatementTrack object handle more of the mock_* attributes. This would encapsulate much of the mock_* behavior in one place, which would be a good thing.
I would also like to add the ability to bind a subroutine (or possibly an object) to the result set, so that the results can be somewhat more dynamic and allow for a more realistic interaction.
=back
=head1 SEE ALSO
......
......@@ -9,7 +9,7 @@ sub new {
# these params have default values
# but can be overridden
$params{return_data} ||= [];
$params{fields} ||= [];
$params{fields} ||= $DBD::Mock::DefaultFieldsToUndef ? undef : [];
$params{bound_params} ||= [];
$params{bound_param_attrs} ||= [];
$params{statement} ||= "";
......@@ -44,7 +44,7 @@ sub get_failure {
sub num_fields {
my ($self) = @_;
return scalar @{ $self->{fields} };
return $self->{fields} ? scalar @{ $self->{fields} } : $self->{fields};
}
sub num_rows {
......@@ -135,7 +135,7 @@ sub mark_executed {
my %recordSet = $self->{callback}->(@{ $self->{bound_params} });
if (ref $recordSet{fields} eq "ARRAY") {
$self->{fileds} = $recordSet{fields};
$self->{fields} = $recordSet{fields};
}
if (ref $recordSet{rows} eq "ARRAY") {
......@@ -211,7 +211,11 @@ sub return_data {
sub fields {
my ( $self, @values ) = @_;
$self->{fields} ||= [];
push @{ $self->{fields} }, @values if scalar @values;
return $self->{fields};
}
......
......@@ -131,8 +131,12 @@ sub prepare {
$track_params{return_data} = $rs;
$track_params{fields} = $fields;
$track_params{callback} = $callback;
$sth->STORE( NAME => $fields );
$sth->STORE( NUM_OF_FIELDS => scalar @{$fields} );
if( $fields ) {
$sth->STORE( NAME => $fields );
$sth->STORE( NUM_OF_FIELDS => scalar @{$fields});
}
}
else {
$sth->trace_msg( "No return data set in DBH\n", 1 );
......
......@@ -120,6 +120,9 @@ sub execute {
$tracker->mark_executed;
my $fields = $tracker->fields;
$sth->STORE( NUM_OF_FIELDS => scalar @{ $fields ? $fields : [] } );
$sth->STORE( NAME => $fields );
$sth->STORE( NUM_OF_PARAMS => $tracker->num_params );
# handle INSERT statements and the mock_last_insert_ids
......
......@@ -30,12 +30,12 @@ $dbh->{mock_add_resultset} = {
my $rows = $sth->execute();
is($rows, '0E0', '... got back 0E0 for rows with a SELECT statement');
my ($result) = $sth->fetchrow_array();
is($result, 10, '... got the result we expected');
$sth->finish();
}
......@@ -50,11 +50,11 @@ $dbh->{mock_add_resultset} = {
my $rows = $sth->execute();
is($rows, '0E0', '... got back 0E0 for rows with a SELECT statement');
my ($result) = $sth->fetchrow_array();
is($result, 50, '... got the result we expected');
$sth->finish();
}
......@@ -65,11 +65,11 @@ $dbh->{mock_add_resultset} = {
my $rows = $sth->execute();
is($rows, '0E0', '... got back 0E0 for rows with a SELECT statement');
my ($result) = $sth->fetchrow_array();
is($result, 50, '... got the result we expected');
$sth->finish();
}
......@@ -84,7 +84,7 @@ $dbh->{mock_add_resultset} = {
my ($result) = $sth->fetchrow_array();
is($result, 50, '... got the result we expected');
$sth->finish();
}
......@@ -100,7 +100,7 @@ $dbh->{mock_add_resultset} = {
results => [ [ 'foo' ], [ 300 ] ],
};
{
{
my $sth = $dbh->prepare('SELECT foo FROM oof');
isa_ok($sth, 'DBI::st');
......@@ -186,25 +186,29 @@ $dbh->{mock_add_resultset} = {
my $sth = $dbh->prepare('SELECT x FROM y WHERE z = ?');
isa_ok($sth, 'DBI::st');
is($sth->{NUM_OF_FIELDS}, 1, "... When we specify the fields in the results parameter then we expect an answer from NUM_OF_FIELDS before we execute the statement");
my $rows = $sth->execute(1);
is($rows, '0E0', '... got back 0E0 for rows with a SELECT statement');
is($sth->{NUM_OF_FIELDS}, 1, "... When we specify the fields in the results parameter then we expect an answer from NUM_OF_FIELDS after we execute the statement");
my ($result) = $sth->fetchrow_array();
is($result, 32, '... got the result we expected');
$rows = $sth->execute(2);
is($rows, '0E0', '... got back 0E0 for rows with a SELECT statement');
($result) = $sth->fetchrow_array();
is($result, 43, '... got the result we expected');
$rows = $sth->execute(33);
is($rows, '0E0', '... got back 0E0 for rows with a SELECT statement');
($result) = $sth->fetchrow_array();
is($result, 1, '... got the result we expected');
$sth->finish();
}
......@@ -233,28 +237,64 @@ $dbh->{mock_add_resultset} = {
my $sth = $dbh->prepare('SELECT a FROM b WHERE c = ?');
isa_ok($sth, 'DBI::st');
is($sth->{NUM_OF_FIELDS}, 0 , "... When we don't specify the fields in the results parameter and we haven't activated the DefaultFieldsToUndef feature, then we expect the NUM_OF_FIELDS to be 0 before we execute the statement");
my $rows = $sth->execute(1);
is($rows, '0E0', '... got back 0E0 for rows with a SELECT statement');
is($sth->{NUM_OF_FIELDS}, 1, "... When we don't specify the fields in the results parameter then we still expect an answer from NUM_OF_FIELDS after we've execute the statement");
my ($result) = $sth->fetchrow_array();
is($result, 32, '... got the result we expected');
$rows = $sth->execute(2);
is($rows, '0E0', '... got back 0E0 for rows with a SELECT statement');
($result) = $sth->fetchrow_array();
is($result, 43, '... got the result we expected');
$rows = $sth->execute(33);
is($rows, '0E0', '... got back 0E0 for rows with a SELECT statement');
($result) = $sth->fetchrow_array();
is($result, 1, '... got the result we expected');
$sth->finish();
}
{
# Activate the DefaultFieldsToUndef feature
$DBD::Mock::DefaultFieldsToUndef = 1;
my $sth = $dbh->prepare('SELECT a FROM b WHERE c = ?');
isa_ok($sth, 'DBI::st');
is($sth->{NUM_OF_FIELDS}, undef , "... When we don't specify the fields in the results parameter then we expect the NUM_OF_FIELDS to be undef before we execute the statement");
my $rows = $sth->execute(1);
is($rows, '0E0', '... got back 0E0 for rows with a SELECT statement');
is($sth->{NUM_OF_FIELDS}, 1, "... When we don't specify the fields in the results parameter then we still expect an answer from NUM_OF_FIELDS after we've execute the statement");
my ($result) = $sth->fetchrow_array();
is($result, 32, '... got the result we expected');
$rows = $sth->execute(2);
is($rows, '0E0', '... got back 0E0 for rows with a SELECT statement');
($result) = $sth->fetchrow_array();
is($result, 43, '... got the result we expected');
$rows = $sth->execute(33);
is($rows, '0E0', '... got back 0E0 for rows with a SELECT statement');
($result) = $sth->fetchrow_array();
is($result, 1, '... got the result we expected');
$sth->finish();
}
done_testing();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment