| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | #line 1 | 
| 2 |  |  |  |  |  |  | package DBD::Mock; | 
| 3 |  |  |  |  |  |  |  | 
| 4 | 0 |  |  | 0 |  | 0 | sub import { | 
| 5 | 0 | 0 | 0 |  |  | 0 | shift; | 
| 6 |  |  |  |  |  |  | $DBI::connect_via = "DBD::Mock::Pool::connect" if (@_ && lc($_[0]) eq "pool"); | 
| 7 |  |  |  |  |  |  | } | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | # --------------------------------------------------------------------------- # | 
| 10 |  |  |  |  |  |  | #   Copyright (c) 2004-2007 Stevan Little, Chris Winters | 
| 11 |  |  |  |  |  |  | #   (spawned from original code Copyright (c) 1994 Tim Bunce) | 
| 12 |  |  |  |  |  |  | # --------------------------------------------------------------------------- # | 
| 13 |  |  |  |  |  |  | #   You may distribute under the terms of either the GNU General Public | 
| 14 |  |  |  |  |  |  | #   License or the Artistic License, as specified in the Perl README file. | 
| 15 |  |  |  |  |  |  | # --------------------------------------------------------------------------- # | 
| 16 | 1 |  |  | 1 |  | 5299 |  | 
|  | 1 |  |  |  |  | 5 |  | 
|  | 1 |  |  |  |  | 140 |  | 
| 17 |  |  |  |  |  |  | use 5.008001; | 
| 18 | 1 |  |  | 1 |  | 7 |  | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 93 |  | 
| 19 | 1 |  |  | 1 |  | 7 | use strict; | 
|  | 1 |  |  |  |  | 23 |  | 
|  | 1 |  |  |  |  | 602 |  | 
| 20 |  |  |  |  |  |  | use warnings; | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | require DBI; | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | our $VERSION = '1.39'; | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | our $drh    = undef;    # will hold driver handle | 
| 27 |  |  |  |  |  |  | our $err    = 0;        # will hold any error codes | 
| 28 |  |  |  |  |  |  | our $errstr = '';       # will hold any error messages | 
| 29 |  |  |  |  |  |  |  | 
| 30 | 1 | 50 |  | 1 | 0 | 254 | sub driver { | 
| 31 | 1 |  |  |  |  | 3 | return $drh if defined $drh; | 
| 32 | 1 | 50 | 33 |  |  | 12 | my ($class, $attributes) = @_; | 
| 33 | 1 |  |  |  |  | 16 | $attributes = {} unless (defined($attributes) && (ref($attributes) eq 'HASH')); | 
| 34 |  |  |  |  |  |  | $drh = DBI::_new_drh( "${class}::dr", { | 
| 35 |  |  |  |  |  |  | Name        => 'Mock', | 
| 36 |  |  |  |  |  |  | Version     => $DBD::Mock::VERSION, | 
| 37 |  |  |  |  |  |  | Attribution => 'DBD Mock driver by Chris Winters & Stevan Little (orig. from Tim Bunce)', | 
| 38 |  |  |  |  |  |  | Err         => \$DBD::Mock::err, | 
| 39 |  |  |  |  |  |  | Errstr      => \$DBD::Mock::errstr, | 
| 40 |  |  |  |  |  |  | # mock attributes | 
| 41 |  |  |  |  |  |  | mock_connect_fail => 0, | 
| 42 | 1 |  |  |  |  | 5 | # and pass in any extra attributes given | 
| 43 |  |  |  |  |  |  | %{$attributes} | 
| 44 | 1 |  |  |  |  | 68 | }); | 
| 45 |  |  |  |  |  |  | return $drh; | 
| 46 |  |  |  |  |  |  | } | 
| 47 | 0 |  |  | 0 |  | 0 |  | 
| 48 |  |  |  |  |  |  | sub CLONE { undef $drh } | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | # NOTE: | 
| 51 |  |  |  |  |  |  | # this feature is still quite experimental. It is defaulted to | 
| 52 |  |  |  |  |  |  | # be off, but it can be turned on by doing this: | 
| 53 |  |  |  |  |  |  | #    $DBD::Mock::AttributeAliasing++; | 
| 54 |  |  |  |  |  |  | # and then turned off by doing: | 
| 55 |  |  |  |  |  |  | #    $DBD::Mock::AttributeAliasing = 0; | 
| 56 |  |  |  |  |  |  | # we shall see how this feature works out. | 
| 57 |  |  |  |  |  |  |  | 
| 58 |  |  |  |  |  |  | our $AttributeAliasing = 0; | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  | my %AttributeAliases = ( | 
| 61 |  |  |  |  |  |  | mysql => { | 
| 62 |  |  |  |  |  |  | db => { | 
| 63 |  |  |  |  |  |  | # aliases can either be a string which is obvious | 
| 64 |  |  |  |  |  |  | mysql_insertid => 'mock_last_insert_id' | 
| 65 |  |  |  |  |  |  | }, | 
| 66 |  |  |  |  |  |  | st => { | 
| 67 |  |  |  |  |  |  | # but they can also be a subroutine reference whose | 
| 68 |  |  |  |  |  |  | # first argument will be either the $dbh or the $sth | 
| 69 |  |  |  |  |  |  | # depending upon which context it is aliased in. | 
| 70 |  |  |  |  |  |  | mysql_insertid => sub { (shift)->{Database}->{'mock_last_insert_id'} } | 
| 71 |  |  |  |  |  |  | } | 
| 72 |  |  |  |  |  |  | }, | 
| 73 |  |  |  |  |  |  | ); | 
| 74 |  |  |  |  |  |  |  | 
| 75 | 0 |  |  | 0 |  | 0 | sub _get_mock_attribute_aliases { | 
| 76 | 0 | 0 |  |  |  | 0 | my ($dbname) = @_; | 
| 77 |  |  |  |  |  |  | (exists $AttributeAliases{lc($dbname)}) | 
| 78 | 0 |  |  |  |  | 0 | || die "Attribute aliases not available for '$dbname'"; | 
| 79 |  |  |  |  |  |  | return $AttributeAliases{lc($dbname)}; | 
| 80 |  |  |  |  |  |  | } | 
| 81 |  |  |  |  |  |  |  | 
| 82 | 0 |  |  | 0 |  | 0 | sub _set_mock_attribute_aliases { | 
| 83 | 0 |  |  |  |  | 0 | my ($dbname, $dbh_or_sth, $key, $value) = @_; | 
| 84 |  |  |  |  |  |  | return $AttributeAliases{lc($dbname)}->{$dbh_or_sth}->{$key} = $value; | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | ## Some useful constants | 
| 88 | 1 |  |  | 1 |  | 9 |  | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 108 |  | 
| 89 |  |  |  |  |  |  | use constant NULL_RESULTSET => [[]]; | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | ######################################## | 
| 93 |  |  |  |  |  |  | # DRIVER | 
| 94 |  |  |  |  |  |  |  | 
| 95 |  |  |  |  |  |  | package | 
| 96 |  |  |  |  |  |  | DBD::Mock::dr; | 
| 97 | 1 |  |  | 1 |  | 6 |  | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 35 |  | 
| 98 | 1 |  |  | 1 |  | 6 | use strict; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 677 |  | 
| 99 |  |  |  |  |  |  | use warnings; | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | $DBD::Mock::dr::imp_data_size = 0; | 
| 102 |  |  |  |  |  |  |  | 
| 103 | 1 |  |  | 1 | 0 | 61 | sub connect { | 
| 104 | 1 | 50 |  |  |  | 5 | my ($drh, $dbname, $user, $auth, $attributes) = @_; | 
| 105 | 0 |  |  |  |  | 0 | if ($drh->{'mock_connect_fail'} == 1) { | 
| 106 | 0 |  |  |  |  | 0 | $drh->DBI::set_err(1, "Could not connect to mock database"); | 
| 107 |  |  |  |  |  |  | return; | 
| 108 | 1 |  | 50 |  |  | 6 | } | 
| 109 |  |  |  |  |  |  | $attributes ||= {}; | 
| 110 | 1 | 0 | 33 |  |  | 6 |  | 
| 111 |  |  |  |  |  |  | if ($dbname && $DBD::Mock::AttributeAliasing) { | 
| 112 | 0 |  |  |  |  | 0 | # this is the DB we are mocking | 
| 113 | 0 |  |  |  |  | 0 | $attributes->{mock_attribute_aliases} = DBD::Mock::_get_mock_attribute_aliases($dbname); | 
| 114 |  |  |  |  |  |  | $attributes->{mock_database_name} = $dbname; | 
| 115 |  |  |  |  |  |  | } | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 1 |  |  |  |  | 3 | # holds statement parsing coderefs/objects | 
| 118 |  |  |  |  |  |  | $attributes->{mock_parser} = []; | 
| 119 | 1 |  |  |  |  | 3 | # holds all statements applied to handle until manually cleared | 
| 120 |  |  |  |  |  |  | $attributes->{mock_statement_history} = []; | 
| 121 | 1 |  |  |  |  | 3 | # ability to fake a failed DB connection | 
| 122 |  |  |  |  |  |  | $attributes->{mock_can_connect} = 1; | 
| 123 | 1 |  |  |  |  | 3 | # ability to make other things fail :) | 
| 124 | 1 |  |  |  |  | 3 | $attributes->{mock_can_prepare} = 1; | 
| 125 | 1 |  |  |  |  | 3 | $attributes->{mock_can_execute} = 1; | 
| 126 |  |  |  |  |  |  | $attributes->{mock_can_fetch}   = 1; | 
| 127 | 1 |  | 50 |  |  | 7 |  | 
| 128 |  |  |  |  |  |  | my $dbh = DBI::_new_dbh($drh, {Name => $dbname}) | 
| 129 |  |  |  |  |  |  | || return; | 
| 130 | 1 |  |  |  |  | 46 |  | 
| 131 |  |  |  |  |  |  | return $dbh; | 
| 132 |  |  |  |  |  |  | } | 
| 133 |  |  |  |  |  |  |  | 
| 134 | 0 |  |  | 0 |  | 0 | sub FETCH { | 
| 135 | 0 | 0 |  |  |  | 0 | my ($drh, $attr) = @_; | 
| 136 | 0 | 0 |  |  |  | 0 | if ($attr =~ /^mock_/) { | 
|  |  | 0 |  |  |  |  |  | 
| 137 | 0 |  |  |  |  | 0 | if ($attr eq 'mock_connect_fail') { | 
| 138 |  |  |  |  |  |  | return $drh->{'mock_connect_fail'}; | 
| 139 |  |  |  |  |  |  | } | 
| 140 | 0 | 0 |  |  |  | 0 | elsif ($attr eq 'mock_data_sources') { | 
| 141 | 0 |  |  |  |  | 0 | unless (defined $drh->{'mock_data_sources'}) { | 
| 142 |  |  |  |  |  |  | $drh->{'mock_data_sources'} = [ 'DBI:Mock:' ]; | 
| 143 | 0 |  |  |  |  | 0 | } | 
| 144 |  |  |  |  |  |  | return $drh->{'mock_data_sources'}; | 
| 145 |  |  |  |  |  |  | } | 
| 146 | 0 |  |  |  |  | 0 | else { | 
| 147 |  |  |  |  |  |  | return $drh->SUPER::FETCH($attr); | 
| 148 |  |  |  |  |  |  | } | 
| 149 |  |  |  |  |  |  | } | 
| 150 | 0 |  |  |  |  | 0 | else { | 
| 151 |  |  |  |  |  |  | return $drh->SUPER::FETCH($attr); | 
| 152 |  |  |  |  |  |  | } | 
| 153 |  |  |  |  |  |  | } | 
| 154 |  |  |  |  |  |  |  | 
| 155 | 0 |  |  | 0 |  | 0 | sub STORE { | 
| 156 | 0 | 0 |  |  |  | 0 | my ($drh, $attr, $value) = @_; | 
| 157 | 0 | 0 |  |  |  | 0 | if ($attr =~ /^mock_/) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 158 | 0 | 0 |  |  |  | 0 | if ($attr eq 'mock_connect_fail') { | 
| 159 |  |  |  |  |  |  | return $drh->{'mock_connect_fail'} = $value ? 1 : 0; | 
| 160 |  |  |  |  |  |  | } | 
| 161 | 0 | 0 |  |  |  | 0 | elsif ($attr eq 'mock_data_sources') { | 
| 162 | 0 |  |  |  |  | 0 | if (ref($value) ne 'ARRAY') { | 
| 163 | 0 |  |  |  |  | 0 | $drh->DBI::set_err(1, "You must pass an array ref of data sources"); | 
| 164 |  |  |  |  |  |  | return; | 
| 165 | 0 |  |  |  |  | 0 | } | 
| 166 |  |  |  |  |  |  | return $drh->{'mock_data_sources'} = $value; | 
| 167 |  |  |  |  |  |  | } | 
| 168 | 0 |  |  |  |  | 0 | elsif ($attr eq 'mock_add_data_sources') { | 
|  | 0 |  |  |  |  | 0 |  | 
| 169 |  |  |  |  |  |  | return push @{$drh->{'mock_data_sources'}} => $value; | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  | } | 
| 172 | 0 |  |  |  |  | 0 | else { | 
| 173 |  |  |  |  |  |  | return $drh->SUPER::STORE($attr, $value); | 
| 174 |  |  |  |  |  |  | } | 
| 175 |  |  |  |  |  |  | } | 
| 176 |  |  |  |  |  |  |  | 
| 177 | 0 |  |  | 0 | 0 | 0 | sub data_sources { | 
| 178 | 0 | 0 |  |  |  | 0 | my $drh = shift; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 179 |  |  |  |  |  |  | return map { (/^DBI\:Mock\:/i) ? $_ : "DBI:Mock:$_" } @{$drh->FETCH('mock_data_sources')}; | 
| 180 |  |  |  |  |  |  | } | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | # Necessary to support DBI < 1.34 | 
| 183 |  |  |  |  |  |  | # from CPAN RT bug #7057 | 
| 184 | 1 |  |  | 1 | 0 | 53 |  | 
| 185 |  |  |  |  |  |  | sub disconnect_all { | 
| 186 |  |  |  |  |  |  | # no-op | 
| 187 |  |  |  |  |  |  | } | 
| 188 | 0 |  |  | 0 |  | 0 |  | 
| 189 |  |  |  |  |  |  | sub DESTROY { undef } | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | ######################################## | 
| 192 |  |  |  |  |  |  | # DATABASE | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | package | 
| 195 |  |  |  |  |  |  | DBD::Mock::db; | 
| 196 | 1 |  |  | 1 |  | 7 |  | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 129 |  | 
| 197 | 1 |  |  | 1 |  | 7 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 2906 |  | 
| 198 |  |  |  |  |  |  | use warnings; | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | $DBD::Mock::db::imp_data_size = 0; | 
| 201 |  |  |  |  |  |  |  | 
| 202 | 0 |  |  | 0 | 0 | 0 | sub ping { | 
| 203 | 0 |  |  |  |  | 0 | my ( $dbh ) = @_; | 
| 204 |  |  |  |  |  |  | return $dbh->{mock_can_connect}; | 
| 205 |  |  |  |  |  |  | } | 
| 206 |  |  |  |  |  |  |  | 
| 207 | 0 |  |  | 0 | 0 | 0 | sub last_insert_id { | 
| 208 | 0 |  |  |  |  | 0 | my ( $dbh ) = @_; | 
| 209 |  |  |  |  |  |  | return $dbh->{mock_last_insert_id}; | 
| 210 |  |  |  |  |  |  | } | 
| 211 |  |  |  |  |  |  |  | 
| 212 | 0 |  |  | 0 | 0 | 0 | sub get_info { | 
| 213 | 0 |  | 0 |  |  | 0 | my ( $dbh, $attr ) = @_; | 
| 214 | 0 |  |  |  |  | 0 | $dbh->{mock_get_info} ||= {}; | 
| 215 |  |  |  |  |  |  | return $dbh->{mock_get_info}{ $attr }; | 
| 216 |  |  |  |  |  |  | } | 
| 217 |  |  |  |  |  |  |  | 
| 218 | 0 |  |  | 0 | 0 | 0 | sub prepare { | 
| 219 |  |  |  |  |  |  | my($dbh, $statement) = @_; | 
| 220 | 0 | 0 |  |  |  | 0 |  | 
| 221 | 0 |  |  |  |  | 0 | unless ($dbh->{mock_can_connect}) { | 
| 222 | 0 |  |  |  |  | 0 | $dbh->DBI::set_err(1, "No connection present"); | 
| 223 |  |  |  |  |  |  | return; | 
| 224 | 0 | 0 |  |  |  | 0 | } | 
| 225 | 0 |  |  |  |  | 0 | unless ($dbh->{mock_can_prepare}) { | 
| 226 | 0 |  |  |  |  | 0 | $dbh->DBI::set_err(1, "Cannot prepare"); | 
| 227 |  |  |  |  |  |  | return; | 
| 228 | 0 | 0 |  |  |  | 0 | } | 
| 229 |  |  |  |  |  |  | $dbh->{mock_can_prepare}++ if $dbh->{mock_can_prepare} < 0; | 
| 230 |  |  |  |  |  |  |  | 
| 231 | 0 |  |  |  |  | 0 |  | 
| 232 | 0 |  |  |  |  | 0 | eval { | 
|  | 0 |  |  |  |  | 0 |  | 
| 233 | 0 | 0 |  |  |  | 0 | foreach my $parser ( @{ $dbh->{mock_parser} } ) { | 
| 234 | 0 |  |  |  |  | 0 | if (ref($parser) eq 'CODE') { | 
| 235 |  |  |  |  |  |  | $parser->($statement); | 
| 236 |  |  |  |  |  |  | } | 
| 237 | 0 |  |  |  |  | 0 | else { | 
| 238 |  |  |  |  |  |  | $parser->parse($statement); | 
| 239 |  |  |  |  |  |  | } | 
| 240 |  |  |  |  |  |  | } | 
| 241 | 0 | 0 |  |  |  | 0 | }; | 
| 242 | 0 |  |  |  |  | 0 | if ($@) { | 
| 243 | 0 |  |  |  |  | 0 | my $parser_error = $@; | 
| 244 | 0 |  |  |  |  | 0 | chomp $parser_error; | 
| 245 | 0 |  |  |  |  | 0 | $dbh->DBI::set_err(1, "Failed to parse statement. Error: ${parser_error}. Statement: ${statement}"); | 
| 246 |  |  |  |  |  |  | return; | 
| 247 |  |  |  |  |  |  | } | 
| 248 | 0 | 0 |  |  |  | 0 |  | 
| 249 | 0 |  |  |  |  | 0 | if (my $session = $dbh->FETCH('mock_session')) { | 
| 250 | 0 |  |  |  |  | 0 | eval { | 
| 251 |  |  |  |  |  |  | $session->verify_statement($dbh, $statement); | 
| 252 | 0 | 0 |  |  |  | 0 | }; | 
| 253 | 0 |  |  |  |  | 0 | if ($@) { | 
| 254 | 0 |  |  |  |  | 0 | my $session_error = $@; | 
| 255 | 0 |  |  |  |  | 0 | chomp $session_error; | 
| 256 | 0 |  |  |  |  | 0 | $dbh->DBI::set_err(1, "Session Error: ${session_error}. Statement: ${statement}"); | 
| 257 |  |  |  |  |  |  | return; | 
| 258 |  |  |  |  |  |  | } | 
| 259 |  |  |  |  |  |  | } | 
| 260 | 0 |  |  |  |  | 0 |  | 
| 261 |  |  |  |  |  |  | my $sth = DBI::_new_sth($dbh, { Statement => $statement }); | 
| 262 | 0 |  |  |  |  | 0 |  | 
| 263 |  |  |  |  |  |  | $sth->trace_msg("Preparing statement '${statement}'\n", 1); | 
| 264 | 0 |  |  |  |  | 0 |  | 
| 265 |  |  |  |  |  |  | my %track_params = (statement => $statement); | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | # If we have available resultsets seed the tracker with one | 
| 268 | 0 |  |  |  |  | 0 |  | 
| 269 | 0 | 0 |  |  |  | 0 | my $rs; | 
| 270 | 0 | 0 |  |  |  | 0 | if ( my $all_rs = $dbh->{mock_rs} ) { | 
| 271 |  |  |  |  |  |  | if ( my $by_name = $all_rs->{named}{$statement} ) { | 
| 272 | 0 |  |  |  |  | 0 | # We want to copy this, because it is meant to be reusable | 
|  | 0 |  |  |  |  | 0 |  | 
| 273 | 0 | 0 |  |  |  | 0 | $rs = [ @{$by_name->{results}} ]; | 
| 274 | 0 |  |  |  |  | 0 | if (exists $by_name->{failure}) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 275 |  |  |  |  |  |  | $track_params{failure} = [ @{$by_name->{failure}} ]; | 
| 276 |  |  |  |  |  |  | } | 
| 277 |  |  |  |  |  |  | } | 
| 278 | 0 |  |  |  |  | 0 | else { | 
|  | 0 |  |  |  |  | 0 |  | 
| 279 |  |  |  |  |  |  | $rs = shift @{$all_rs->{ordered}}; | 
| 280 |  |  |  |  |  |  | } | 
| 281 |  |  |  |  |  |  | } | 
| 282 | 0 | 0 | 0 |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 283 | 0 |  |  |  |  | 0 | if (ref($rs) eq 'ARRAY' && scalar(@{$rs}) > 0 ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 284 | 0 |  |  |  |  | 0 | my $fields = shift @{$rs}; | 
| 285 | 0 |  |  |  |  | 0 | $track_params{return_data} = $rs; | 
| 286 | 0 |  |  |  |  | 0 | $track_params{fields}      = $fields; | 
| 287 | 0 |  |  |  |  | 0 | $sth->STORE(NAME           => $fields); | 
|  | 0 |  |  |  |  | 0 |  | 
| 288 |  |  |  |  |  |  | $sth->STORE(NUM_OF_FIELDS  => scalar @{$fields}); | 
| 289 |  |  |  |  |  |  | } | 
| 290 | 0 |  |  |  |  | 0 | else { | 
| 291 |  |  |  |  |  |  | $sth->trace_msg("No return data set in DBH\n", 1); | 
| 292 |  |  |  |  |  |  | } | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | # do not allow a statement handle to be created if there is no | 
| 295 |  |  |  |  |  |  | # connection present. | 
| 296 | 0 | 0 |  |  |  | 0 |  | 
| 297 | 0 |  |  |  |  | 0 | unless ($dbh->FETCH('Active')) { | 
| 298 | 0 |  |  |  |  | 0 | $dbh->DBI::set_err(1, "No connection present"); | 
| 299 |  |  |  |  |  |  | return; | 
| 300 |  |  |  |  |  |  | } | 
| 301 |  |  |  |  |  |  |  | 
| 302 |  |  |  |  |  |  | # This history object will track everything done to the statement | 
| 303 | 0 |  |  |  |  | 0 |  | 
| 304 | 0 |  |  |  |  | 0 | my $history = DBD::Mock::StatementTrack->new(%track_params); | 
| 305 |  |  |  |  |  |  | $sth->STORE(mock_my_history => $history); | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | # ...now associate the history object with the database handle so | 
| 308 |  |  |  |  |  |  | # people can browse the entire history at once, even for | 
| 309 |  |  |  |  |  |  | # statements opened and closed in a black box | 
| 310 | 0 |  |  |  |  | 0 |  | 
| 311 | 0 |  |  |  |  | 0 | my $all_history = $dbh->FETCH('mock_statement_history'); | 
|  | 0 |  |  |  |  | 0 |  | 
| 312 |  |  |  |  |  |  | push @{$all_history}, $history; | 
| 313 | 0 |  |  |  |  | 0 |  | 
| 314 |  |  |  |  |  |  | return $sth; | 
| 315 |  |  |  |  |  |  | } | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | *prepare_cached = \&prepare; | 
| 318 |  |  |  |  |  |  |  | 
| 319 |  |  |  |  |  |  | { | 
| 320 |  |  |  |  |  |  | my $begin_work_commit; | 
| 321 | 0 |  |  | 0 | 0 | 0 | sub begin_work { | 
| 322 | 0 | 0 |  |  |  | 0 | my $dbh = shift; | 
| 323 | 0 |  |  |  |  | 0 | if ($dbh->FETCH('AutoCommit')) { | 
| 324 | 0 |  |  |  |  | 0 | $dbh->STORE('AutoCommit', 0); | 
| 325 | 0 |  |  |  |  | 0 | $begin_work_commit = 1; | 
| 326 | 0 |  |  |  |  | 0 | my $sth = $dbh->prepare( 'BEGIN WORK' ); | 
| 327 | 0 |  |  |  |  | 0 | my $rc = $sth->execute(); | 
| 328 | 0 |  |  |  |  | 0 | $sth->finish(); | 
| 329 |  |  |  |  |  |  | return $rc; | 
| 330 |  |  |  |  |  |  | } | 
| 331 | 0 |  |  |  |  | 0 | else { | 
| 332 |  |  |  |  |  |  | return $dbh->set_err(1, 'AutoCommit is off, you are already within a transaction'); | 
| 333 |  |  |  |  |  |  | } | 
| 334 |  |  |  |  |  |  | } | 
| 335 |  |  |  |  |  |  |  | 
| 336 | 0 |  |  | 0 | 0 | 0 | sub commit { | 
| 337 | 0 | 0 | 0 |  |  | 0 | my $dbh = shift; | 
| 338 | 0 |  |  |  |  | 0 | if ($dbh->FETCH('AutoCommit') && $dbh->FETCH('Warn')) { | 
| 339 |  |  |  |  |  |  | return $dbh->set_err(1, "commit ineffective with AutoCommit" ); | 
| 340 |  |  |  |  |  |  | } | 
| 341 | 0 |  |  |  |  | 0 |  | 
| 342 | 0 |  |  |  |  | 0 | my $sth = $dbh->prepare( 'COMMIT' ); | 
| 343 | 0 |  |  |  |  | 0 | my $rc = $sth->execute(); | 
| 344 |  |  |  |  |  |  | $sth->finish(); | 
| 345 | 0 | 0 |  |  |  | 0 |  | 
| 346 | 0 |  |  |  |  | 0 | if ($begin_work_commit) { | 
| 347 | 0 |  |  |  |  | 0 | $dbh->STORE('AutoCommit', 1); | 
| 348 |  |  |  |  |  |  | $begin_work_commit = 0; | 
| 349 |  |  |  |  |  |  | } | 
| 350 | 0 |  |  |  |  | 0 |  | 
| 351 |  |  |  |  |  |  | return $rc; | 
| 352 |  |  |  |  |  |  | } | 
| 353 |  |  |  |  |  |  |  | 
| 354 | 0 |  |  | 0 | 0 | 0 | sub rollback { | 
| 355 | 0 | 0 | 0 |  |  | 0 | my $dbh = shift; | 
| 356 | 0 |  |  |  |  | 0 | if ($dbh->FETCH('AutoCommit') && $dbh->FETCH('Warn')) { | 
| 357 |  |  |  |  |  |  | return $dbh->set_err(1, "rollback ineffective with AutoCommit" ); | 
| 358 |  |  |  |  |  |  | } | 
| 359 | 0 |  |  |  |  | 0 |  | 
| 360 | 0 |  |  |  |  | 0 | my $sth = $dbh->prepare( 'ROLLBACK' ); | 
| 361 | 0 |  |  |  |  | 0 | my $rc = $sth->execute(); | 
| 362 |  |  |  |  |  |  | $sth->finish(); | 
| 363 | 0 | 0 |  |  |  | 0 |  | 
| 364 | 0 |  |  |  |  | 0 | if ($begin_work_commit) { | 
| 365 | 0 |  |  |  |  | 0 | $dbh->STORE('AutoCommit', 1); | 
| 366 |  |  |  |  |  |  | $begin_work_commit = 0; | 
| 367 |  |  |  |  |  |  | } | 
| 368 | 0 |  |  |  |  | 0 |  | 
| 369 |  |  |  |  |  |  | return $rc; | 
| 370 |  |  |  |  |  |  | } | 
| 371 |  |  |  |  |  |  | } | 
| 372 |  |  |  |  |  |  |  | 
| 373 |  |  |  |  |  |  | # NOTE: | 
| 374 |  |  |  |  |  |  | # this method should work in most cases, however it does | 
| 375 |  |  |  |  |  |  | # not exactly follow the DBI spec in the case of error | 
| 376 |  |  |  |  |  |  | # handling. I am not sure if that level of detail is | 
| 377 |  |  |  |  |  |  | # really nessecary since it is a weird error conditon | 
| 378 |  |  |  |  |  |  | # which causes it to fail anyway. However if you find you do need it, | 
| 379 |  |  |  |  |  |  | # then please email me about it. I think it would be possible | 
| 380 |  |  |  |  |  |  | # to mimic it by accessing the DBD::Mock::StatementTrack | 
| 381 |  |  |  |  |  |  | # object directly. | 
| 382 | 0 |  |  | 0 | 0 | 0 | sub selectcol_arrayref { | 
| 383 |  |  |  |  |  |  | my ($dbh, $query, $attrib, @bindvalues) = @_; | 
| 384 | 0 |  |  |  |  | 0 | # get all the columns ... | 
| 385 |  |  |  |  |  |  | my $a_ref = $dbh->selectall_arrayref($query, $attrib, @bindvalues); | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | # if we get nothing back, or dont get an | 
| 388 |  |  |  |  |  |  | # ARRAY ref back, then we can assume | 
| 389 | 0 | 0 | 0 |  |  | 0 | # something went wrong, and so return undef. | 
| 390 |  |  |  |  |  |  | return undef unless defined $a_ref || ref($a_ref) ne 'ARRAY'; | 
| 391 | 0 |  |  |  |  | 0 |  | 
| 392 | 0 | 0 |  |  |  | 0 | my @cols = 0; | 
| 393 | 0 |  |  |  |  | 0 | if (ref $attrib->{Columns} eq 'ARRAY') { | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 394 |  |  |  |  |  |  | @cols = map { $_ - 1 } @{$attrib->{Columns}}; | 
| 395 |  |  |  |  |  |  | } | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | # if we do get something then we | 
| 398 | 0 |  |  |  |  | 0 | # grab all the columns out of it. | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 399 |  |  |  |  |  |  | return [ map { @$_[@cols] } @{$a_ref} ] | 
| 400 |  |  |  |  |  |  | } | 
| 401 |  |  |  |  |  |  |  | 
| 402 | 0 |  |  | 0 |  | 0 | sub FETCH { | 
| 403 | 0 |  |  |  |  | 0 | my ( $dbh, $attrib, $value ) = @_; | 
| 404 |  |  |  |  |  |  | $dbh->trace_msg( "Fetching DB attrib '$attrib'\n" ); | 
| 405 | 0 | 0 |  |  |  | 0 |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 406 | 0 |  |  |  |  | 0 | if ($attrib eq 'Active') { | 
| 407 |  |  |  |  |  |  | return $dbh->{mock_can_connect}; | 
| 408 |  |  |  |  |  |  | } | 
| 409 | 0 |  |  |  |  | 0 | elsif ($attrib eq 'mock_all_history') { | 
| 410 |  |  |  |  |  |  | return $dbh->{mock_statement_history}; | 
| 411 |  |  |  |  |  |  | } | 
| 412 | 0 |  |  |  |  | 0 | elsif ($attrib eq 'mock_all_history_iterator') { | 
| 413 |  |  |  |  |  |  | return DBD::Mock::StatementTrack::Iterator->new($dbh->{mock_statement_history}); | 
| 414 |  |  |  |  |  |  | } | 
| 415 | 0 |  |  |  |  | 0 | elsif ($attrib =~ /^mock/) { | 
| 416 |  |  |  |  |  |  | return $dbh->{$attrib}; | 
| 417 |  |  |  |  |  |  | } | 
| 418 | 0 |  |  |  |  | 0 | elsif ($attrib =~ /^(private_|dbi_|dbd_|[A-Z])/ ) { | 
| 419 | 0 |  |  |  |  | 0 | $dbh->trace_msg("... fetching non-driver attribute ($attrib) that DBI handles\n"); | 
| 420 |  |  |  |  |  |  | return $dbh->SUPER::FETCH($attrib); | 
| 421 |  |  |  |  |  |  | } | 
| 422 | 0 | 0 |  |  |  | 0 | else { | 
| 423 | 0 | 0 |  |  |  | 0 | if ($dbh->{mock_attribute_aliases}) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 424 | 0 |  |  |  |  | 0 | if (exists ${$dbh->{mock_attribute_aliases}->{db}}{$attrib}) { | 
| 425 | 0 | 0 |  |  |  | 0 | my $mock_attrib = $dbh->{mock_attribute_aliases}->{db}->{$attrib}; | 
| 426 | 0 |  |  |  |  | 0 | if (ref($mock_attrib) eq 'CODE') { | 
| 427 |  |  |  |  |  |  | return $mock_attrib->($dbh); | 
| 428 |  |  |  |  |  |  | } | 
| 429 | 0 |  |  |  |  | 0 | else { | 
| 430 |  |  |  |  |  |  | return $dbh->FETCH($mock_attrib); | 
| 431 |  |  |  |  |  |  | } | 
| 432 |  |  |  |  |  |  | } | 
| 433 | 0 |  |  |  |  | 0 | } | 
| 434 | 0 |  |  |  |  | 0 | $dbh->trace_msg( "... fetching non-driver attribute ($attrib) that DBI doesn't handle\n"); | 
| 435 |  |  |  |  |  |  | return $dbh->{$attrib}; | 
| 436 |  |  |  |  |  |  | } | 
| 437 |  |  |  |  |  |  | } | 
| 438 |  |  |  |  |  |  |  | 
| 439 | 10 |  |  | 10 |  | 172 | sub STORE { | 
| 440 | 10 | 50 |  |  |  | 143 | my ( $dbh, $attrib, $value ) = @_; | 
| 441 |  |  |  |  |  |  | $dbh->trace_msg( "Storing DB attribute '$attrib' with '" . (defined($value) ? $value : 'undef') . "'\n" ); | 
| 442 | 10 | 100 |  |  |  | 26 |  | 
| 443 |  |  |  |  |  |  | if ($attrib eq 'AutoCommit') { | 
| 444 |  |  |  |  |  |  | # These are magic DBI values that say we can handle AutoCommit | 
| 445 | 1 | 50 |  |  |  | 84 | # internally as well | 
| 446 |  |  |  |  |  |  | $value = ($value) ? -901 : -900; | 
| 447 |  |  |  |  |  |  | } | 
| 448 | 10 | 50 |  |  |  | 86 |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 449 | 0 | 0 |  |  |  | 0 | if ( $attrib eq 'mock_clear_history' ) { | 
| 450 | 0 |  |  |  |  | 0 | if ( $value ) { | 
| 451 |  |  |  |  |  |  | $dbh->{mock_statement_history} = []; | 
| 452 | 0 |  |  |  |  | 0 | } | 
| 453 |  |  |  |  |  |  | return []; | 
| 454 |  |  |  |  |  |  | } | 
| 455 | 0 |  |  |  |  | 0 | elsif ( $attrib eq 'mock_add_parser' ) { | 
| 456 | 0 |  |  |  |  | 0 | my $parser_type = ref($value); | 
| 457 |  |  |  |  |  |  | my $is_valid_parser; | 
| 458 | 0 | 0 | 0 |  |  | 0 |  | 
|  |  | 0 |  |  |  |  |  | 
| 459 | 0 |  |  |  |  | 0 | if ($parser_type eq 'CODE') { | 
| 460 |  |  |  |  |  |  | $is_valid_parser++; | 
| 461 |  |  |  |  |  |  | } | 
| 462 | 0 |  |  |  |  | 0 | elsif ($parser_type && $parser_type !~ /^(ARRAY|HASH|SCALAR)$/) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 463 |  |  |  |  |  |  | $is_valid_parser = eval { $parser_type->can( 'parse' ) }; | 
| 464 |  |  |  |  |  |  | } | 
| 465 | 0 | 0 |  |  |  | 0 |  | 
| 466 | 0 |  |  |  |  | 0 | unless ($is_valid_parser) { | 
| 467 |  |  |  |  |  |  | my $error = "Parser must be a code reference or object with 'parse()' " . | 
| 468 | 0 |  |  |  |  | 0 | "method (Given type: '$parser_type')"; | 
| 469 | 0 |  |  |  |  | 0 | $dbh->DBI::set_err(1, $error); | 
| 470 |  |  |  |  |  |  | return; | 
| 471 | 0 |  |  |  |  | 0 | } | 
|  | 0 |  |  |  |  | 0 |  | 
| 472 | 0 |  |  |  |  | 0 | push @{$dbh->{mock_parser}}, $value; | 
| 473 |  |  |  |  |  |  | return $value; | 
| 474 |  |  |  |  |  |  | } | 
| 475 | 0 |  | 0 |  |  | 0 | elsif ( $attrib eq 'mock_add_resultset' ) { | 
| 476 |  |  |  |  |  |  | $dbh->{mock_rs} ||= { named   => {}, | 
| 477 | 0 | 0 |  |  |  | 0 | ordered => [] }; | 
|  |  | 0 |  |  |  |  |  | 
| 478 | 0 |  |  |  |  | 0 | if ( ref $value eq 'ARRAY' ) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 479 | 0 |  |  |  |  | 0 | my @copied_values = @{$value}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 480 | 0 |  |  |  |  | 0 | push @{$dbh->{mock_rs}{ordered}}, \@copied_values; | 
| 481 |  |  |  |  |  |  | return \@copied_values; | 
| 482 |  |  |  |  |  |  | } | 
| 483 | 0 |  |  |  |  | 0 | elsif ( ref $value eq 'HASH' ) { | 
| 484 | 0 | 0 |  |  |  | 0 | my $name = $value->{sql}; | 
| 485 | 0 |  |  |  |  | 0 | unless ($name) { | 
| 486 |  |  |  |  |  |  | die "Indexing resultset by name requires passing in 'sql' ", | 
| 487 |  |  |  |  |  |  | "as hashref key to 'mock_add_resultset'.\n"; | 
| 488 | 0 |  |  |  |  | 0 | } | 
|  | 0 |  |  |  |  | 0 |  | 
| 489 | 0 |  |  |  |  | 0 | my @copied_values = @{$value->{results}}; | 
| 490 |  |  |  |  |  |  | $dbh->{mock_rs}{named}{$name} = { | 
| 491 |  |  |  |  |  |  | results => \@copied_values, | 
| 492 | 0 | 0 |  |  |  | 0 | }; | 
| 493 | 0 |  |  |  |  | 0 | if ( exists $value->{failure} ) { | 
| 494 | 0 |  |  |  |  | 0 | $dbh->{mock_rs}{named}{$name}{failure} = [ | 
| 495 |  |  |  |  |  |  | @{$value->{failure}}, | 
| 496 |  |  |  |  |  |  | ]; | 
| 497 | 0 |  |  |  |  | 0 | } | 
| 498 |  |  |  |  |  |  | return \@copied_values; | 
| 499 |  |  |  |  |  |  | } | 
| 500 | 0 |  |  |  |  | 0 | else { | 
| 501 |  |  |  |  |  |  | die "Must provide an arrayref or hashref when adding ", | 
| 502 |  |  |  |  |  |  | "resultset via 'mock_add_resultset'.\n"; | 
| 503 |  |  |  |  |  |  | } | 
| 504 |  |  |  |  |  |  | } | 
| 505 | 0 | 0 |  |  |  | 0 | elsif ($attrib eq 'mock_start_insert_id') { | 
| 506 | 0 | 0 |  |  |  | 0 | if ( ref $value eq 'ARRAY' ) { | 
| 507 | 0 |  |  |  |  | 0 | $dbh->{mock_last_insert_ids} = {} unless $dbh->{mock_last_insert_ids}; | 
| 508 |  |  |  |  |  |  | $dbh->{mock_last_insert_ids}{$value->[0]} = $value->[1]; | 
| 509 |  |  |  |  |  |  | } | 
| 510 |  |  |  |  |  |  | else { | 
| 511 |  |  |  |  |  |  | # we start at one minus the start id | 
| 512 | 0 |  |  |  |  | 0 | # so that the increment works | 
| 513 |  |  |  |  |  |  | $dbh->{mock_last_insert_id} = $value - 1; | 
| 514 |  |  |  |  |  |  | } | 
| 515 |  |  |  |  |  |  |  | 
| 516 |  |  |  |  |  |  | } | 
| 517 | 0 | 0 | 0 |  |  | 0 | elsif ($attrib eq 'mock_session') { | 
|  |  |  | 0 |  |  |  |  | 
| 518 |  |  |  |  |  |  | (ref($value) && UNIVERSAL::isa($value, 'DBD::Mock::Session')) | 
| 519 |  |  |  |  |  |  | || die "Only DBD::Mock::Session objects can be placed into the 'mock_session' slot\n" | 
| 520 | 0 |  |  |  |  | 0 | if defined $value; | 
| 521 |  |  |  |  |  |  | $dbh->{mock_session} = $value; | 
| 522 |  |  |  |  |  |  | } | 
| 523 | 0 |  |  |  |  | 0 | elsif ($attrib =~ /^mock_(add_)?data_sources/) { | 
| 524 |  |  |  |  |  |  | $dbh->{Driver}->STORE($attrib, $value); | 
| 525 |  |  |  |  |  |  | } | 
| 526 | 6 |  |  |  |  | 68 | elsif ($attrib =~ /^mock/) { | 
| 527 |  |  |  |  |  |  | return $dbh->{$attrib} = $value; | 
| 528 |  |  |  |  |  |  | } | 
| 529 | 4 |  |  |  |  | 20 | elsif ($attrib =~ /^(private_|dbi_|dbd_|[A-Z])/ ) { | 
| 530 | 4 |  |  |  |  | 51 | $dbh->trace_msg("... storing non-driver attribute ($attrib) with value ($value) that DBI handles\n"); | 
| 531 |  |  |  |  |  |  | return $dbh->SUPER::STORE($attrib, $value); | 
| 532 |  |  |  |  |  |  | } | 
| 533 | 0 |  |  |  |  | 0 | else { | 
| 534 | 0 |  |  |  |  | 0 | $dbh->trace_msg("... storing non-driver attribute ($attrib) with value ($value) that DBI won't handle\n"); | 
| 535 |  |  |  |  |  |  | return $dbh->{$attrib} = $value; | 
| 536 |  |  |  |  |  |  | } | 
| 537 |  |  |  |  |  |  | } | 
| 538 |  |  |  |  |  |  |  | 
| 539 | 1 |  |  | 1 |  | 703 | sub DESTROY { | 
| 540 | 1 | 50 |  |  |  | 262 | my ($dbh) = @_; | 
| 541 | 0 | 0 |  |  |  |  | if ( my $session = $dbh->{mock_session} ) { | 
| 542 | 0 |  |  |  |  |  | if ( $session->has_states_left ) { | 
| 543 |  |  |  |  |  |  | die "DBH->finish called when session still has states left\n"; | 
| 544 |  |  |  |  |  |  | } | 
| 545 |  |  |  |  |  |  | } | 
| 546 |  |  |  |  |  |  | } | 
| 547 |  |  |  |  |  |  |  | 
| 548 | 0 |  |  | 0 | 0 |  | sub disconnect { | 
| 549 | 0 | 0 |  |  |  |  | my ($dbh) = @_; | 
| 550 | 0 | 0 |  |  |  |  | if ( my $session = $dbh->{mock_session} ) { | 
| 551 | 0 |  |  |  |  |  | if ( $session->has_states_left ) { | 
| 552 |  |  |  |  |  |  | die "DBH->finish called when session still has states left\n"; | 
| 553 |  |  |  |  |  |  | } | 
| 554 |  |  |  |  |  |  | } | 
| 555 |  |  |  |  |  |  | } | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | ######################################## | 
| 558 |  |  |  |  |  |  | # STATEMENT | 
| 559 |  |  |  |  |  |  |  | 
| 560 |  |  |  |  |  |  | package | 
| 561 |  |  |  |  |  |  | DBD::Mock::st; | 
| 562 | 1 |  |  | 1 |  | 10 |  | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 136 |  | 
| 563 | 1 |  |  | 1 |  | 7 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 3696 |  | 
| 564 |  |  |  |  |  |  | use warnings; | 
| 565 |  |  |  |  |  |  |  | 
| 566 |  |  |  |  |  |  | $DBD::Mock::st::imp_data_size = 0; | 
| 567 |  |  |  |  |  |  |  | 
| 568 | 0 |  |  | 0 | 0 |  | sub bind_col { | 
| 569 |  |  |  |  |  |  | my ($sth, $param_num, $ref, $attr) = @_; | 
| 570 | 0 |  |  |  |  |  |  | 
| 571 | 0 |  |  |  |  |  | my $tracker = $sth->FETCH( 'mock_my_history' ); | 
| 572 | 0 |  |  |  |  |  | $tracker->bind_col( $param_num, $ref ); | 
| 573 |  |  |  |  |  |  | return 1; | 
| 574 |  |  |  |  |  |  | } | 
| 575 |  |  |  |  |  |  |  | 
| 576 | 0 |  |  | 0 | 0 |  | sub bind_param { | 
| 577 | 0 |  |  |  |  |  | my ($sth, $param_num, $val, $attr) = @_; | 
| 578 | 0 |  |  |  |  |  | my $tracker = $sth->FETCH( 'mock_my_history' ); | 
| 579 | 0 |  |  |  |  |  | $tracker->bound_param( $param_num, $val ); | 
| 580 |  |  |  |  |  |  | return 1; | 
| 581 |  |  |  |  |  |  | } | 
| 582 |  |  |  |  |  |  |  | 
| 583 | 0 |  |  | 0 | 0 |  | sub bind_param_inout { | 
| 584 |  |  |  |  |  |  | my ($sth, $param_num, $val, $max_len) = @_; | 
| 585 | 0 | 0 |  |  |  |  | # check that $val is a scalar ref | 
| 586 |  |  |  |  |  |  | (UNIVERSAL::isa($val, 'SCALAR')) | 
| 587 |  |  |  |  |  |  | || $sth->{Database}->DBI::set_err(1, "need a scalar ref to bind_param_inout, not $val"); | 
| 588 | 0 | 0 |  |  |  |  | # check for positive $max_len | 
| 589 |  |  |  |  |  |  | ($max_len > 0) | 
| 590 | 0 |  |  |  |  |  | || $sth->{Database}->DBI::set_err(1, "need to specify a maximum length to bind_param_inout"); | 
| 591 | 0 |  |  |  |  |  | my $tracker = $sth->FETCH( 'mock_my_history' ); | 
| 592 | 0 |  |  |  |  |  | $tracker->bound_param( $param_num, $val ); | 
| 593 |  |  |  |  |  |  | return 1; | 
| 594 |  |  |  |  |  |  | } | 
| 595 |  |  |  |  |  |  |  | 
| 596 | 0 |  |  | 0 | 0 |  | sub execute { | 
| 597 | 0 |  |  |  |  |  | my ($sth, @params) = @_; | 
| 598 |  |  |  |  |  |  | my $dbh = $sth->{Database}; | 
| 599 | 0 | 0 |  |  |  |  |  | 
| 600 | 0 |  |  |  |  |  | unless ($dbh->{mock_can_connect}) { | 
| 601 | 0 |  |  |  |  |  | $dbh->DBI::set_err(1, "No connection present"); | 
| 602 |  |  |  |  |  |  | return 0; | 
| 603 | 0 | 0 |  |  |  |  | } | 
| 604 | 0 |  |  |  |  |  | unless ($dbh->{mock_can_execute}) { | 
| 605 | 0 |  |  |  |  |  | $dbh->DBI::set_err(1, "Cannot execute"); | 
| 606 |  |  |  |  |  |  | return 0; | 
| 607 | 0 | 0 |  |  |  |  | } | 
| 608 |  |  |  |  |  |  | $dbh->{mock_can_execute}++ if $dbh->{mock_can_execute} < 0; | 
| 609 | 0 |  |  |  |  |  |  | 
| 610 |  |  |  |  |  |  | my $tracker = $sth->FETCH( 'mock_my_history' ); | 
| 611 | 0 | 0 |  |  |  |  |  | 
| 612 | 0 |  |  |  |  |  | if ($tracker->has_failure()) { | 
| 613 | 0 |  |  |  |  |  | $dbh->DBI::set_err($tracker->get_failure()); | 
| 614 |  |  |  |  |  |  | return 0; | 
| 615 |  |  |  |  |  |  | } | 
| 616 | 0 | 0 |  |  |  |  |  | 
| 617 | 0 |  |  |  |  |  | if ( @params ) { | 
| 618 |  |  |  |  |  |  | $tracker->bind_params( @params ); | 
| 619 |  |  |  |  |  |  | } | 
| 620 | 0 | 0 |  |  |  |  |  | 
| 621 | 0 |  |  |  |  |  | if (my $session = $dbh->{mock_session}) { | 
| 622 | 0 |  |  |  |  |  | eval { | 
| 623 | 0 |  |  |  |  |  | $session->verify_bound_params($dbh, $tracker->bound_params()); | 
| 624 | 0 |  |  |  |  |  | my $idx = $session->{state_index} - 1; | 
|  | 0 |  |  |  |  |  |  | 
| 625 | 0 |  |  |  |  |  | my @results = @{$session->{states}->[$idx]->{results}}; | 
| 626 | 0 |  |  |  |  |  | shift @results; | 
| 627 |  |  |  |  |  |  | $tracker->{return_data} = \@results; | 
| 628 | 0 | 0 |  |  |  |  | }; | 
| 629 | 0 |  |  |  |  |  | if ($@) { | 
| 630 | 0 |  |  |  |  |  | my $session_error = $@; | 
| 631 | 0 |  |  |  |  |  | chomp $session_error; | 
| 632 | 0 |  |  |  |  |  | $dbh->DBI::set_err(1, "Session Error: ${session_error}"); | 
| 633 |  |  |  |  |  |  | return; | 
| 634 |  |  |  |  |  |  | } | 
| 635 |  |  |  |  |  |  | } | 
| 636 | 0 |  |  |  |  |  |  | 
| 637 | 0 |  |  |  |  |  | $tracker->mark_executed; | 
| 638 | 0 |  |  |  |  |  | my $fields = $tracker->fields; | 
| 639 |  |  |  |  |  |  | $sth->STORE( NUM_OF_PARAMS => $tracker->num_params ); | 
| 640 |  |  |  |  |  |  |  | 
| 641 |  |  |  |  |  |  | # handle INSERT statements and the mock_last_insert_ids | 
| 642 |  |  |  |  |  |  | # We should only increment these things after the last successful INSERT. | 
| 643 |  |  |  |  |  |  | # -RobK, 2007-10-12 | 
| 644 | 0 | 0 |  |  |  |  | #use Data::Dumper;warn Dumper $dbh->{mock_last_insert_ids}; | 
| 645 | 0 | 0 | 0 |  |  |  | if ($dbh->{Statement} =~ /^\s*?insert\s+into\s+(\S+)/i) { | 
| 646 | 0 |  |  |  |  |  | if ( $dbh->{mock_last_insert_ids} && exists $dbh->{mock_last_insert_ids}{$1} ) { | 
| 647 |  |  |  |  |  |  | $dbh->{mock_last_insert_id} = $dbh->{mock_last_insert_ids}{$1}++; | 
| 648 |  |  |  |  |  |  | } | 
| 649 | 0 |  |  |  |  |  | else { | 
| 650 |  |  |  |  |  |  | $dbh->{mock_last_insert_id}++; | 
| 651 |  |  |  |  |  |  | } | 
| 652 |  |  |  |  |  |  | } | 
| 653 |  |  |  |  |  |  | #warn "$dbh->{mock_last_insert_id}\n"; | 
| 654 |  |  |  |  |  |  |  | 
| 655 | 0 | 0 |  |  |  |  | # always return 0E0 for Selects | 
| 656 | 0 |  |  |  |  |  | if ($dbh->{Statement} =~ /^\s*?select/i) { | 
| 657 |  |  |  |  |  |  | return '0E0'; | 
| 658 | 0 |  | 0 |  |  |  | } | 
| 659 |  |  |  |  |  |  | return ($sth->rows() || '0E0'); | 
| 660 |  |  |  |  |  |  | } | 
| 661 |  |  |  |  |  |  |  | 
| 662 | 0 |  |  | 0 | 0 |  | sub fetch { | 
| 663 | 0 |  |  |  |  |  | my ($sth) = @_; | 
| 664 | 0 | 0 |  |  |  |  | my $dbh = $sth->{Database}; | 
| 665 | 0 |  |  |  |  |  | unless ($dbh->{mock_can_connect}) { | 
| 666 | 0 |  |  |  |  |  | $dbh->DBI::set_err(1, "No connection present"); | 
| 667 |  |  |  |  |  |  | return; | 
| 668 | 0 | 0 |  |  |  |  | } | 
| 669 | 0 |  |  |  |  |  | unless ($dbh->{mock_can_fetch}) { | 
| 670 | 0 |  |  |  |  |  | $dbh->DBI::set_err(1, "Cannot fetch"); | 
| 671 |  |  |  |  |  |  | return; | 
| 672 | 0 | 0 |  |  |  |  | } | 
| 673 |  |  |  |  |  |  | $dbh->{mock_can_fetch}++ if $dbh->{mock_can_fetch} < 0; | 
| 674 | 0 |  |  |  |  |  |  | 
| 675 |  |  |  |  |  |  | my $tracker = $sth->FETCH( 'mock_my_history' ); | 
| 676 | 0 | 0 |  |  |  |  |  | 
| 677 |  |  |  |  |  |  | my $record = $tracker->next_record | 
| 678 |  |  |  |  |  |  | or return; | 
| 679 | 0 | 0 |  |  |  |  |  | 
| 680 | 0 |  |  |  |  |  | if ( my @cols = $tracker->bind_cols() ) { | 
|  | 0 |  |  |  |  |  |  | 
| 681 | 0 |  |  |  |  |  | for my $i ( grep { ref $cols[$_] } 0..$#cols ) { | 
|  | 0 |  |  |  |  |  |  | 
| 682 |  |  |  |  |  |  | ${ $cols[$i] } = $record->[$i]; | 
| 683 |  |  |  |  |  |  | } | 
| 684 |  |  |  |  |  |  | } | 
| 685 | 0 |  |  |  |  |  |  | 
| 686 |  |  |  |  |  |  | return $record; | 
| 687 |  |  |  |  |  |  | } | 
| 688 |  |  |  |  |  |  |  | 
| 689 | 0 |  |  | 0 | 0 |  | sub fetchrow_array { | 
| 690 | 0 |  |  |  |  |  | my ($sth) = @_; | 
| 691 | 0 | 0 |  |  |  |  | my $row = $sth->DBD::Mock::st::fetch(); | 
| 692 | 0 |  |  |  |  |  | return unless ref($row) eq 'ARRAY'; | 
|  | 0 |  |  |  |  |  |  | 
| 693 |  |  |  |  |  |  | return @{$row}; | 
| 694 |  |  |  |  |  |  | } | 
| 695 |  |  |  |  |  |  |  | 
| 696 | 0 |  |  | 0 | 0 |  | sub fetchrow_arrayref { | 
| 697 | 0 |  |  |  |  |  | my ($sth) = @_; | 
| 698 |  |  |  |  |  |  | return $sth->DBD::Mock::st::fetch(); | 
| 699 |  |  |  |  |  |  | } | 
| 700 |  |  |  |  |  |  |  | 
| 701 | 0 |  |  | 0 | 0 |  | sub fetchrow_hashref { | 
| 702 | 0 |  |  |  |  |  | my ($sth, $name) = @_; | 
| 703 |  |  |  |  |  |  | my $dbh = $sth->{Database}; | 
| 704 |  |  |  |  |  |  | # handle any errors since we are grabbing | 
| 705 | 0 | 0 |  |  |  |  | # from the tracker directly | 
| 706 | 0 |  |  |  |  |  | unless ($dbh->{mock_can_connect}) { | 
| 707 | 0 |  |  |  |  |  | $dbh->DBI::set_err(1, "No connection present"); | 
| 708 |  |  |  |  |  |  | return; | 
| 709 | 0 | 0 |  |  |  |  | } | 
| 710 | 0 |  |  |  |  |  | unless ($dbh->{mock_can_fetch}) { | 
| 711 | 0 |  |  |  |  |  | $dbh->DBI::set_err(1, "Cannot fetch"); | 
| 712 |  |  |  |  |  |  | return; | 
| 713 | 0 | 0 |  |  |  |  | } | 
| 714 |  |  |  |  |  |  | $dbh->{mock_can_fetch}++ if $dbh->{mock_can_fetch} < 0; | 
| 715 |  |  |  |  |  |  |  | 
| 716 | 0 |  | 0 |  |  |  | # first handle the $name, it will default to NAME | 
| 717 |  |  |  |  |  |  | $name ||= 'NAME'; | 
| 718 | 0 |  |  |  |  |  | # then fetch the names from the $sth (per DBI spec) | 
| 719 |  |  |  |  |  |  | my $fields = $sth->FETCH($name); | 
| 720 |  |  |  |  |  |  |  | 
| 721 | 0 |  |  |  |  |  | # now check the tracker ... | 
| 722 |  |  |  |  |  |  | my $tracker = $sth->FETCH( 'mock_my_history' ); | 
| 723 | 0 | 0 |  |  |  |  | # and collect the results | 
| 724 | 0 |  |  |  |  |  | if (my $record = $tracker->next_record()) { | 
|  | 0 |  |  |  |  |  |  | 
| 725 |  |  |  |  |  |  | my @values = @{$record}; | 
| 726 | 0 |  |  |  |  |  | return { | 
| 727 | 0 |  |  |  |  |  | map { | 
| 728 | 0 |  |  |  |  |  | $_ => shift(@values) | 
| 729 |  |  |  |  |  |  | } @{$fields} | 
| 730 |  |  |  |  |  |  | }; | 
| 731 |  |  |  |  |  |  | } | 
| 732 | 0 |  |  |  |  |  |  | 
| 733 |  |  |  |  |  |  | return undef; | 
| 734 |  |  |  |  |  |  | } | 
| 735 |  |  |  |  |  |  |  | 
| 736 |  |  |  |  |  |  | #XXX Isn't this supposed to return an array of hashrefs? -RobK, 2007-10-15 | 
| 737 | 0 |  |  | 0 | 0 |  | sub fetchall_hashref { | 
| 738 | 0 |  |  |  |  |  | my ($sth, $keyfield) = @_; | 
| 739 |  |  |  |  |  |  | my $dbh = $sth->{Database}; | 
| 740 |  |  |  |  |  |  | # handle any errors since we are grabbing | 
| 741 | 0 | 0 |  |  |  |  | # from the tracker directly | 
| 742 | 0 |  |  |  |  |  | unless ($dbh->{mock_can_connect}) { | 
| 743 | 0 |  |  |  |  |  | $dbh->DBI::set_err(1, "No connection present"); | 
| 744 |  |  |  |  |  |  | return; | 
| 745 | 0 | 0 |  |  |  |  | } | 
| 746 | 0 |  |  |  |  |  | unless ($dbh->{mock_can_fetch}) { | 
| 747 | 0 |  |  |  |  |  | $dbh->DBI::set_err(1, "Cannot fetch"); | 
| 748 |  |  |  |  |  |  | return; | 
| 749 | 0 | 0 |  |  |  |  | } | 
| 750 |  |  |  |  |  |  | $dbh->{mock_can_fetch}++ if $dbh->{mock_can_fetch} < 0; | 
| 751 | 0 |  |  |  |  |  |  | 
| 752 | 0 |  |  |  |  |  | my $tracker = $sth->FETCH( 'mock_my_history' ); | 
| 753 |  |  |  |  |  |  | my $rethash = {}; | 
| 754 |  |  |  |  |  |  |  | 
| 755 | 0 |  | 0 |  |  |  | # get the name set by | 
| 756 | 0 |  |  |  |  |  | my $name = $sth->{Database}->FETCH('FetchHashKeyName') || 'NAME'; | 
| 757 |  |  |  |  |  |  | my $fields = $sth->FETCH($name); | 
| 758 |  |  |  |  |  |  |  | 
| 759 | 0 | 0 |  |  |  |  | # check if $keyfield is not an integer | 
| 760 | 0 |  |  |  |  |  | if (!($keyfield =~ /^-?\d+$/)) { | 
| 761 |  |  |  |  |  |  | my $found = 0; | 
| 762 | 0 |  |  |  |  |  | # search for index of item that matches $keyfield | 
|  | 0 |  |  |  |  |  |  | 
| 763 | 0 | 0 |  |  |  |  | foreach my $index (0 .. scalar(@{$fields})) { | 
| 764 | 0 |  |  |  |  |  | if ($fields->[$index] eq $keyfield) { | 
| 765 |  |  |  |  |  |  | $found++; | 
| 766 | 0 |  |  |  |  |  | # now make the keyfield the index | 
| 767 |  |  |  |  |  |  | $keyfield = $index; | 
| 768 | 0 |  |  |  |  |  | # and jump out of the loop :) | 
| 769 |  |  |  |  |  |  | last; | 
| 770 |  |  |  |  |  |  | } | 
| 771 | 0 | 0 |  |  |  |  | } | 
| 772 | 0 |  |  |  |  |  | unless ($found) { | 
| 773 | 0 |  |  |  |  |  | $dbh->DBI::set_err(1, "Could not find key field '$keyfield'"); | 
| 774 |  |  |  |  |  |  | return; | 
| 775 |  |  |  |  |  |  | } | 
| 776 |  |  |  |  |  |  | } | 
| 777 |  |  |  |  |  |  |  | 
| 778 | 0 |  |  |  |  |  | # now loop through all the records ... | 
| 779 |  |  |  |  |  |  | while (my $record = $tracker->next_record()) { | 
| 780 |  |  |  |  |  |  | # copy the values so as to preserve | 
| 781 | 0 |  |  |  |  |  | # the original record... | 
|  | 0 |  |  |  |  |  |  | 
| 782 |  |  |  |  |  |  | my @values = @{$record}; | 
| 783 | 0 |  |  |  |  |  | # populate the hash | 
| 784 |  |  |  |  |  |  | $rethash->{$record->[$keyfield]} = { | 
| 785 | 0 |  |  |  |  |  | map { | 
| 786 | 0 |  |  |  |  |  | $_ => shift(@values) | 
| 787 |  |  |  |  |  |  | } @{$fields} | 
| 788 |  |  |  |  |  |  | }; | 
| 789 |  |  |  |  |  |  | } | 
| 790 | 0 |  |  |  |  |  |  | 
| 791 |  |  |  |  |  |  | return $rethash; | 
| 792 |  |  |  |  |  |  | } | 
| 793 |  |  |  |  |  |  |  | 
| 794 | 0 |  |  | 0 | 0 |  | sub finish { | 
| 795 | 0 |  |  |  |  |  | my ($sth) = @_; | 
| 796 |  |  |  |  |  |  | $sth->FETCH( 'mock_my_history' )->is_finished( 'yes' ); | 
| 797 |  |  |  |  |  |  | } | 
| 798 |  |  |  |  |  |  |  | 
| 799 | 0 |  |  | 0 | 0 |  | sub rows { | 
| 800 | 0 |  |  |  |  |  | my ($sth) = @_; | 
| 801 |  |  |  |  |  |  | $sth->FETCH('mock_num_rows'); | 
| 802 |  |  |  |  |  |  | } | 
| 803 |  |  |  |  |  |  |  | 
| 804 | 0 |  |  | 0 |  |  | sub FETCH { | 
| 805 | 0 |  |  |  |  |  | my ( $sth, $attrib ) = @_; | 
| 806 | 0 |  |  |  |  |  | $sth->trace_msg( "Fetching ST attribute '$attrib'\n" ); | 
| 807 | 0 |  |  |  |  |  | my $tracker = $sth->{mock_my_history}; | 
| 808 |  |  |  |  |  |  | $sth->trace_msg( "Retrieved tracker: " . ref( $tracker ) . "\n" ); | 
| 809 | 0 | 0 |  |  |  |  | # NAME attributes | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 810 | 0 |  |  |  |  |  | if ( $attrib eq 'NAME' ) { | 
|  | 0 |  |  |  |  |  |  | 
| 811 |  |  |  |  |  |  | return [ @{$tracker->fields} ]; | 
| 812 |  |  |  |  |  |  | } | 
| 813 | 0 |  |  |  |  |  | elsif ( $attrib eq 'NAME_lc' ) { | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 814 |  |  |  |  |  |  | return [ map { lc($_) } @{$tracker->fields} ]; | 
| 815 |  |  |  |  |  |  | } | 
| 816 | 0 |  |  |  |  |  | elsif ( $attrib eq 'NAME_uc' ) { | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 817 |  |  |  |  |  |  | return [ map { uc($_) } @{$tracker->fields} ]; | 
| 818 |  |  |  |  |  |  | } | 
| 819 |  |  |  |  |  |  | # NAME_hash attributes | 
| 820 | 0 |  |  |  |  |  | elsif ( $attrib eq 'NAME_hash' ) { | 
| 821 | 0 |  |  |  |  |  | my $i = 0; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 822 |  |  |  |  |  |  | return { map { $_ => $i++ } @{$tracker->fields} }; | 
| 823 |  |  |  |  |  |  | } | 
| 824 | 0 |  |  |  |  |  | elsif ( $attrib eq 'NAME_hash_lc' ) { | 
| 825 | 0 |  |  |  |  |  | my $i = 0; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 826 |  |  |  |  |  |  | return { map { lc($_) => $i++ } @{$tracker->fields} }; | 
| 827 |  |  |  |  |  |  | } | 
| 828 | 0 |  |  |  |  |  | elsif ( $attrib eq 'NAME_hash_uc' ) { | 
| 829 | 0 |  |  |  |  |  | my $i = 0; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 830 |  |  |  |  |  |  | return { map { uc($_) => $i++ } @{$tracker->fields} }; | 
| 831 |  |  |  |  |  |  | } | 
| 832 |  |  |  |  |  |  | # others | 
| 833 | 0 |  |  |  |  |  | elsif ( $attrib eq 'NUM_OF_FIELDS' ) { | 
| 834 |  |  |  |  |  |  | return $tracker->num_fields; | 
| 835 |  |  |  |  |  |  | } | 
| 836 | 0 |  |  |  |  |  | elsif ( $attrib eq 'NUM_OF_PARAMS' ) { | 
| 837 |  |  |  |  |  |  | return $tracker->num_params; | 
| 838 |  |  |  |  |  |  | } | 
| 839 | 0 |  |  |  |  |  | elsif ( $attrib eq 'TYPE' ) { | 
| 840 | 0 |  |  |  |  |  | my $num_fields = $tracker->num_fields; | 
|  | 0 |  |  |  |  |  |  | 
| 841 |  |  |  |  |  |  | return [ map { $DBI::SQL_VARCHAR } ( 0 .. $num_fields ) ]; | 
| 842 |  |  |  |  |  |  | } | 
| 843 | 0 |  |  |  |  |  | elsif ( $attrib eq 'Active' ) { | 
| 844 |  |  |  |  |  |  | return $tracker->is_active; | 
| 845 |  |  |  |  |  |  | } | 
| 846 | 0 | 0 |  |  |  |  | elsif ( $attrib !~ /^mock/ ) { | 
| 847 | 0 | 0 |  |  |  |  | if ($sth->{Database}->{mock_attribute_aliases}) { | 
|  | 0 |  |  |  |  |  |  | 
| 848 | 0 |  |  |  |  |  | if (exists ${$sth->{Database}->{mock_attribute_aliases}->{st}}{$attrib}) { | 
| 849 | 0 | 0 |  |  |  |  | my $mock_attrib = $sth->{Database}->{mock_attribute_aliases}->{st}->{$attrib}; | 
| 850 | 0 |  |  |  |  |  | if (ref($mock_attrib) eq 'CODE') { | 
| 851 |  |  |  |  |  |  | return $mock_attrib->($sth); | 
| 852 |  |  |  |  |  |  | } | 
| 853 | 0 |  |  |  |  |  | else { | 
| 854 |  |  |  |  |  |  | return $sth->FETCH($mock_attrib); | 
| 855 |  |  |  |  |  |  | } | 
| 856 |  |  |  |  |  |  | } | 
| 857 | 0 |  |  |  |  |  | } | 
| 858 |  |  |  |  |  |  | return $sth->SUPER::FETCH( $attrib ); | 
| 859 |  |  |  |  |  |  | } | 
| 860 |  |  |  |  |  |  |  | 
| 861 |  |  |  |  |  |  | # now do our stuff... | 
| 862 | 0 | 0 |  |  |  |  |  | 
| 863 | 0 |  |  |  |  |  | if ( $attrib eq 'mock_my_history' ) { | 
| 864 |  |  |  |  |  |  | return $tracker; | 
| 865 | 0 | 0 | 0 |  |  |  | } | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 866 | 0 |  |  |  |  |  | if ( $attrib eq 'mock_statement' ) { | 
| 867 |  |  |  |  |  |  | return $tracker->statement; | 
| 868 |  |  |  |  |  |  | } | 
| 869 | 0 |  |  |  |  |  | elsif ( $attrib eq 'mock_params' ) { | 
| 870 |  |  |  |  |  |  | return $tracker->bound_params; | 
| 871 |  |  |  |  |  |  | } | 
| 872 | 0 |  |  |  |  |  | elsif ( $attrib eq 'mock_records' ) { | 
| 873 |  |  |  |  |  |  | return $tracker->return_data; | 
| 874 |  |  |  |  |  |  | } | 
| 875 | 0 |  |  |  |  |  | elsif ( $attrib eq 'mock_num_records' || $attrib eq 'mock_num_rows' ) { | 
| 876 |  |  |  |  |  |  | return $tracker->num_rows; | 
| 877 |  |  |  |  |  |  | } | 
| 878 | 0 |  |  |  |  |  | elsif ( $attrib eq 'mock_current_record_num' ) { | 
| 879 |  |  |  |  |  |  | return $tracker->current_record_num; | 
| 880 |  |  |  |  |  |  | } | 
| 881 | 0 |  |  |  |  |  | elsif ( $attrib eq 'mock_fields' ) { | 
| 882 |  |  |  |  |  |  | return $tracker->fields; | 
| 883 |  |  |  |  |  |  | } | 
| 884 | 0 |  |  |  |  |  | elsif ( $attrib eq 'mock_is_executed' ) { | 
| 885 |  |  |  |  |  |  | return $tracker->is_executed; | 
| 886 |  |  |  |  |  |  | } | 
| 887 | 0 |  |  |  |  |  | elsif ( $attrib eq 'mock_is_finished' ) { | 
| 888 |  |  |  |  |  |  | return $tracker->is_finished; | 
| 889 |  |  |  |  |  |  | } | 
| 890 | 0 |  |  |  |  |  | elsif ( $attrib eq 'mock_is_depleted' ) { | 
| 891 |  |  |  |  |  |  | return $tracker->is_depleted; | 
| 892 |  |  |  |  |  |  | } | 
| 893 | 0 |  |  |  |  |  | else { | 
| 894 |  |  |  |  |  |  | die "I don't know how to retrieve statement attribute '$attrib'\n"; | 
| 895 |  |  |  |  |  |  | } | 
| 896 |  |  |  |  |  |  | } | 
| 897 |  |  |  |  |  |  |  | 
| 898 | 0 |  |  | 0 |  |  | sub STORE { | 
| 899 | 0 |  |  |  |  |  | my ($sth, $attrib, $value) = @_; | 
| 900 | 0 | 0 |  |  |  |  | $sth->trace_msg( "Storing ST attribute '$attrib'\n" ); | 
|  |  | 0 |  |  |  |  |  | 
| 901 | 0 |  |  |  |  |  | if ($attrib =~ /^mock/) { | 
| 902 |  |  |  |  |  |  | return $sth->{$attrib} = $value; | 
| 903 |  |  |  |  |  |  | } | 
| 904 |  |  |  |  |  |  | elsif ($attrib =~ /^NAME/) { | 
| 905 | 0 |  |  |  |  |  | # no-op... | 
| 906 |  |  |  |  |  |  | return; | 
| 907 |  |  |  |  |  |  | } | 
| 908 | 0 |  | 0 |  |  |  | else { | 
| 909 | 0 |  |  |  |  |  | $value ||= 0; | 
| 910 |  |  |  |  |  |  | return $sth->SUPER::STORE( $attrib, $value ); | 
| 911 |  |  |  |  |  |  | } | 
| 912 |  |  |  |  |  |  | } | 
| 913 | 0 |  |  | 0 |  |  |  | 
| 914 |  |  |  |  |  |  | sub DESTROY { undef } | 
| 915 |  |  |  |  |  |  |  | 
| 916 |  |  |  |  |  |  | ########################## | 
| 917 |  |  |  |  |  |  | # Database Pooling | 
| 918 |  |  |  |  |  |  | # (Apache::DBI emulation) | 
| 919 |  |  |  |  |  |  |  | 
| 920 |  |  |  |  |  |  | package | 
| 921 |  |  |  |  |  |  | DBD::Mock::Pool; | 
| 922 | 1 |  |  | 1 |  | 13 |  | 
|  | 1 |  |  |  |  | 4 |  | 
|  | 1 |  |  |  |  | 44 |  | 
| 923 | 1 |  |  | 1 |  | 6 | use strict; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 116 |  | 
| 924 |  |  |  |  |  |  | use warnings; | 
| 925 |  |  |  |  |  |  |  | 
| 926 |  |  |  |  |  |  | my $connection; | 
| 927 |  |  |  |  |  |  |  | 
| 928 | 0 | 0 |  | 0 | 0 |  | sub connect { | 
| 929 |  |  |  |  |  |  | return $connection if $connection; | 
| 930 |  |  |  |  |  |  |  | 
| 931 |  |  |  |  |  |  | # according to the code before my tweaks, this could be a class | 
| 932 | 0 | 0 |  |  |  |  | # name, but it was never used - DR, 2008-11-08 | 
| 933 |  |  |  |  |  |  | shift unless ref $_[0]; | 
| 934 | 0 |  |  |  |  |  |  | 
| 935 | 0 |  |  |  |  |  | my $drh = shift; | 
| 936 |  |  |  |  |  |  | return $connection = bless $drh->connect(@_), 'DBD::Mock::Pool::db'; | 
| 937 |  |  |  |  |  |  | } | 
| 938 |  |  |  |  |  |  |  | 
| 939 |  |  |  |  |  |  | package | 
| 940 |  |  |  |  |  |  | DBD::Mock::Pool::db; | 
| 941 | 1 |  |  | 1 |  | 5 |  | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 26 |  | 
| 942 | 1 |  |  | 1 |  | 4 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 82 |  | 
| 943 |  |  |  |  |  |  | use warnings; | 
| 944 |  |  |  |  |  |  |  | 
| 945 |  |  |  |  |  |  | our @ISA = qw(DBI::db); | 
| 946 | 0 |  |  | 0 | 0 |  |  | 
| 947 |  |  |  |  |  |  | sub disconnect { 1 } | 
| 948 |  |  |  |  |  |  |  | 
| 949 |  |  |  |  |  |  | ######################################## | 
| 950 |  |  |  |  |  |  | # TRACKER | 
| 951 |  |  |  |  |  |  |  | 
| 952 |  |  |  |  |  |  | package | 
| 953 |  |  |  |  |  |  | DBD::Mock::StatementTrack; | 
| 954 | 1 |  |  | 1 |  | 5 |  | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 38 |  | 
| 955 | 1 |  |  | 1 |  | 5 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 1918 |  | 
| 956 |  |  |  |  |  |  | use warnings; | 
| 957 |  |  |  |  |  |  |  | 
| 958 | 0 |  |  | 0 | 0 |  | sub new { | 
| 959 |  |  |  |  |  |  | my ($class, %params) = @_; | 
| 960 |  |  |  |  |  |  | # these params have default values | 
| 961 | 0 |  | 0 |  |  |  | # but can be overridden | 
| 962 | 0 |  | 0 |  |  |  | $params{return_data}  ||= []; | 
| 963 | 0 |  | 0 |  |  |  | $params{fields}       ||= []; | 
| 964 | 0 |  | 0 |  |  |  | $params{bound_params} ||= []; | 
| 965 | 0 |  | 0 |  |  |  | $params{statement}    ||= ""; | 
| 966 |  |  |  |  |  |  | $params{failure}      ||= undef; | 
| 967 |  |  |  |  |  |  | # these params should never be overridden | 
| 968 |  |  |  |  |  |  | # and should always start out in a default | 
| 969 | 0 |  |  |  |  |  | # state to assure the sanity of this class | 
| 970 | 0 |  |  |  |  |  | $params{is_executed}        = 'no'; | 
| 971 | 0 |  |  |  |  |  | $params{is_finished}        = 'no'; | 
| 972 |  |  |  |  |  |  | $params{current_record_num} = 0; | 
| 973 |  |  |  |  |  |  | # NOTE: | 
| 974 |  |  |  |  |  |  | # changed from \%params here because that | 
| 975 |  |  |  |  |  |  | # would bind the hash sent in so that it | 
| 976 |  |  |  |  |  |  | # would reflect alterations in the object | 
| 977 | 0 |  |  |  |  |  | # this violates encapsulation | 
| 978 | 0 |  |  |  |  |  | my $self = bless { %params }, $class; | 
| 979 |  |  |  |  |  |  | return $self; | 
| 980 |  |  |  |  |  |  | } | 
| 981 |  |  |  |  |  |  |  | 
| 982 | 0 |  |  | 0 | 0 |  | sub has_failure { | 
| 983 | 0 | 0 |  |  |  |  | my ($self) = @_; | 
| 984 |  |  |  |  |  |  | $self->{failure} ? 1 : 0; | 
| 985 |  |  |  |  |  |  | } | 
| 986 |  |  |  |  |  |  |  | 
| 987 | 0 |  |  | 0 | 0 |  | sub get_failure { | 
| 988 | 0 |  |  |  |  |  | my ($self) = @_; | 
|  | 0 |  |  |  |  |  |  | 
| 989 |  |  |  |  |  |  | @{$self->{failure}}; | 
| 990 |  |  |  |  |  |  | } | 
| 991 |  |  |  |  |  |  |  | 
| 992 | 0 |  |  | 0 | 0 |  | sub num_fields { | 
| 993 | 0 |  |  |  |  |  | my ($self) = @_; | 
|  | 0 |  |  |  |  |  |  | 
| 994 |  |  |  |  |  |  | return scalar @{$self->{fields}}; | 
| 995 |  |  |  |  |  |  | } | 
| 996 |  |  |  |  |  |  |  | 
| 997 | 0 |  |  | 0 | 0 |  | sub num_rows { | 
| 998 | 0 |  |  |  |  |  | my ($self) = @_; | 
|  | 0 |  |  |  |  |  |  | 
| 999 |  |  |  |  |  |  | return scalar @{$self->{return_data}}; | 
| 1000 |  |  |  |  |  |  | } | 
| 1001 |  |  |  |  |  |  |  | 
| 1002 | 0 |  |  | 0 | 0 |  | sub num_params { | 
| 1003 | 0 |  |  |  |  |  | my ($self) = @_; | 
|  | 0 |  |  |  |  |  |  | 
| 1004 |  |  |  |  |  |  | return scalar @{$self->{bound_params}}; | 
| 1005 |  |  |  |  |  |  | } | 
| 1006 |  |  |  |  |  |  |  | 
| 1007 | 0 |  |  | 0 | 0 |  | sub bind_col { | 
| 1008 | 0 |  |  |  |  |  | my ($self, $param_num, $ref) = @_; | 
| 1009 |  |  |  |  |  |  | $self->{bind_cols}->[$param_num - 1] = $ref; | 
| 1010 |  |  |  |  |  |  | } | 
| 1011 |  |  |  |  |  |  |  | 
| 1012 | 0 |  |  | 0 | 0 |  | sub bound_param { | 
| 1013 | 0 |  |  |  |  |  | my ($self, $param_num, $value) = @_; | 
| 1014 | 0 |  |  |  |  |  | $self->{bound_params}->[$param_num - 1] = $value; | 
| 1015 |  |  |  |  |  |  | return $self->bound_params; | 
| 1016 |  |  |  |  |  |  | } | 
| 1017 |  |  |  |  |  |  |  | 
| 1018 | 0 |  |  | 0 | 0 |  | sub bound_param_trailing { | 
| 1019 | 0 |  |  |  |  |  | my ($self, @values) = @_; | 
|  | 0 |  |  |  |  |  |  | 
| 1020 |  |  |  |  |  |  | push @{$self->{bound_params}}, @values; | 
| 1021 |  |  |  |  |  |  | } | 
| 1022 |  |  |  |  |  |  |  | 
| 1023 | 0 |  |  | 0 | 0 |  | sub bind_cols { | 
| 1024 | 0 | 0 |  |  |  |  | my $self = shift; | 
|  | 0 |  |  |  |  |  |  | 
| 1025 |  |  |  |  |  |  | return @{$self->{bind_cols} || []}; | 
| 1026 |  |  |  |  |  |  | } | 
| 1027 |  |  |  |  |  |  |  | 
| 1028 | 0 |  |  | 0 | 0 |  | sub bind_params { | 
| 1029 | 0 |  |  |  |  |  | my ($self, @values) = @_; | 
|  | 0 |  |  |  |  |  |  | 
| 1030 |  |  |  |  |  |  | @{$self->{bound_params}} = @values; | 
| 1031 |  |  |  |  |  |  | } | 
| 1032 |  |  |  |  |  |  |  | 
| 1033 |  |  |  |  |  |  | # Rely on the DBI's notion of Active: a statement is active if it's | 
| 1034 |  |  |  |  |  |  | # currently in a SELECT and has more records to fetch | 
| 1035 |  |  |  |  |  |  |  | 
| 1036 | 0 |  |  | 0 | 0 |  | sub is_active { | 
| 1037 | 0 | 0 |  |  |  |  | my ($self) = @_; | 
| 1038 | 0 | 0 |  |  |  |  | return 0 unless $self->statement =~ /^\s*select/ism; | 
| 1039 | 0 | 0 |  |  |  |  | return 0 unless $self->is_executed eq 'yes'; | 
| 1040 | 0 |  |  |  |  |  | return 0 if     $self->is_depleted; | 
| 1041 |  |  |  |  |  |  | return 1; | 
| 1042 |  |  |  |  |  |  | } | 
| 1043 |  |  |  |  |  |  |  | 
| 1044 | 0 |  |  | 0 | 0 |  | sub is_finished { | 
| 1045 | 0 | 0 | 0 |  |  |  | my ($self, $value) = @_; | 
|  |  | 0 |  |  |  |  |  | 
| 1046 | 0 |  |  |  |  |  | if (defined $value && $value eq 'yes' ) { | 
| 1047 | 0 |  |  |  |  |  | $self->{is_finished} = 'yes'; | 
| 1048 | 0 |  |  |  |  |  | $self->current_record_num(0); | 
| 1049 |  |  |  |  |  |  | $self->{return_data} = []; | 
| 1050 |  |  |  |  |  |  | } | 
| 1051 | 0 |  |  |  |  |  | elsif (defined $value) { | 
| 1052 |  |  |  |  |  |  | $self->{is_finished} = 'no'; | 
| 1053 | 0 |  |  |  |  |  | } | 
| 1054 |  |  |  |  |  |  | return $self->{is_finished}; | 
| 1055 |  |  |  |  |  |  | } | 
| 1056 |  |  |  |  |  |  |  | 
| 1057 |  |  |  |  |  |  | #################### | 
| 1058 |  |  |  |  |  |  | # RETURN VALUES | 
| 1059 |  |  |  |  |  |  |  | 
| 1060 | 0 |  |  | 0 | 0 |  | sub mark_executed { | 
| 1061 | 0 |  |  |  |  |  | my ($self) = @_; | 
| 1062 | 0 |  |  |  |  |  | $self->is_executed('yes'); | 
| 1063 |  |  |  |  |  |  | $self->current_record_num(0); | 
| 1064 |  |  |  |  |  |  | } | 
| 1065 |  |  |  |  |  |  |  | 
| 1066 | 0 |  |  | 0 | 0 |  | sub next_record { | 
| 1067 | 0 | 0 |  |  |  |  | my ($self) = @_; | 
| 1068 | 0 |  |  |  |  |  | return if $self->is_depleted; | 
| 1069 | 0 |  |  |  |  |  | my $rec_num = $self->current_record_num; | 
| 1070 | 0 |  |  |  |  |  | my $rec = $self->return_data->[$rec_num]; | 
| 1071 | 0 |  |  |  |  |  | $self->current_record_num($rec_num + 1); | 
| 1072 |  |  |  |  |  |  | return $rec; | 
| 1073 |  |  |  |  |  |  | } | 
| 1074 |  |  |  |  |  |  |  | 
| 1075 | 0 |  |  | 0 | 0 |  | sub is_depleted { | 
| 1076 | 0 |  |  |  |  |  | my ($self) = @_; | 
|  | 0 |  |  |  |  |  |  | 
| 1077 |  |  |  |  |  |  | return ($self->current_record_num >= scalar @{$self->return_data}); | 
| 1078 |  |  |  |  |  |  | } | 
| 1079 |  |  |  |  |  |  |  | 
| 1080 |  |  |  |  |  |  | # DEBUGGING AID | 
| 1081 |  |  |  |  |  |  |  | 
| 1082 | 0 |  |  | 0 | 0 |  | sub to_string { | 
| 1083 | 0 |  |  |  |  |  | my ($self) = @_; | 
| 1084 |  |  |  |  |  |  | return join "\n" => ( | 
| 1085 | 0 |  |  |  |  |  | $self->{statement}, | 
| 1086 | 0 |  |  |  |  |  | "Values: [" . join( '] [', @{ $self->{bound_params} } ) . "]", | 
| 1087 |  |  |  |  |  |  | "Records: on $self->{current_record_num} of " . scalar(@{$self->return_data}) . "\n", | 
| 1088 |  |  |  |  |  |  | "Executed? $self->{is_executed}; Finished? $self->{is_finished}" | 
| 1089 |  |  |  |  |  |  | ); | 
| 1090 |  |  |  |  |  |  | } | 
| 1091 |  |  |  |  |  |  |  | 
| 1092 |  |  |  |  |  |  | # PROPERTIES | 
| 1093 |  |  |  |  |  |  |  | 
| 1094 |  |  |  |  |  |  | # boolean | 
| 1095 |  |  |  |  |  |  |  | 
| 1096 | 0 |  |  | 0 | 0 |  | sub is_executed { | 
| 1097 | 0 | 0 |  |  |  |  | my ($self, $yes_no) = @_; | 
| 1098 | 0 | 0 |  |  |  |  | $self->{is_executed} = $yes_no if defined $yes_no; | 
| 1099 |  |  |  |  |  |  | return ($self->{is_executed} eq 'yes') ? 'yes' : 'no'; | 
| 1100 |  |  |  |  |  |  | } | 
| 1101 |  |  |  |  |  |  |  | 
| 1102 |  |  |  |  |  |  | # single-element fields | 
| 1103 |  |  |  |  |  |  |  | 
| 1104 | 0 |  |  | 0 | 0 |  | sub statement { | 
| 1105 | 0 | 0 |  |  |  |  | my ($self, $value) = @_; | 
| 1106 | 0 |  |  |  |  |  | $self->{statement} = $value if defined $value; | 
| 1107 |  |  |  |  |  |  | return $self->{statement}; | 
| 1108 |  |  |  |  |  |  | } | 
| 1109 |  |  |  |  |  |  |  | 
| 1110 | 0 |  |  | 0 | 0 |  | sub current_record_num { | 
| 1111 | 0 | 0 |  |  |  |  | my ($self, $value) = @_; | 
| 1112 | 0 |  |  |  |  |  | $self->{current_record_num} = $value if defined $value; | 
| 1113 |  |  |  |  |  |  | return $self->{current_record_num}; | 
| 1114 |  |  |  |  |  |  | } | 
| 1115 |  |  |  |  |  |  |  | 
| 1116 |  |  |  |  |  |  | # multi-element fields | 
| 1117 |  |  |  |  |  |  |  | 
| 1118 | 0 |  |  | 0 | 0 |  | sub return_data { | 
| 1119 | 0 | 0 |  |  |  |  | my ($self, @values) = @_; | 
|  | 0 |  |  |  |  |  |  | 
| 1120 | 0 |  |  |  |  |  | push @{$self->{return_data}}, @values if scalar @values; | 
| 1121 |  |  |  |  |  |  | return $self->{return_data}; | 
| 1122 |  |  |  |  |  |  | } | 
| 1123 |  |  |  |  |  |  |  | 
| 1124 | 0 |  |  | 0 | 0 |  | sub fields { | 
| 1125 | 0 | 0 |  |  |  |  | my ($self, @values) = @_; | 
|  | 0 |  |  |  |  |  |  | 
| 1126 | 0 |  |  |  |  |  | push @{$self->{fields}}, @values if scalar @values; | 
| 1127 |  |  |  |  |  |  | return $self->{fields}; | 
| 1128 |  |  |  |  |  |  | } | 
| 1129 |  |  |  |  |  |  |  | 
| 1130 | 0 |  |  | 0 | 0 |  | sub bound_params { | 
| 1131 | 0 | 0 |  |  |  |  | my ($self, @values) = @_; | 
|  | 0 |  |  |  |  |  |  | 
| 1132 | 0 |  |  |  |  |  | push @{$self->{bound_params}}, @values if scalar @values; | 
| 1133 |  |  |  |  |  |  | return $self->{bound_params}; | 
| 1134 |  |  |  |  |  |  | } | 
| 1135 |  |  |  |  |  |  |  | 
| 1136 |  |  |  |  |  |  | package | 
| 1137 |  |  |  |  |  |  | DBD::Mock::StatementTrack::Iterator; | 
| 1138 | 1 |  |  | 1 |  | 10 |  | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 100 |  | 
| 1139 | 1 |  |  | 1 |  | 6 | use strict; | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 387 |  | 
| 1140 |  |  |  |  |  |  | use warnings; | 
| 1141 |  |  |  |  |  |  |  | 
| 1142 | 0 |  |  | 0 | 0 |  | sub new { | 
| 1143 | 0 |  | 0 |  |  |  | my ($class, $history) = @_; | 
| 1144 |  |  |  |  |  |  | return bless { | 
| 1145 |  |  |  |  |  |  | pointer => 0, | 
| 1146 |  |  |  |  |  |  | history => $history || [] | 
| 1147 |  |  |  |  |  |  | } => $class; | 
| 1148 |  |  |  |  |  |  | } | 
| 1149 |  |  |  |  |  |  |  | 
| 1150 | 0 |  |  | 0 | 0 |  | sub next { | 
| 1151 | 0 | 0 |  |  |  |  | my ($self) = @_; | 
|  | 0 |  |  |  |  |  |  | 
| 1152 | 0 |  |  |  |  |  | return unless $self->{pointer} < scalar(@{$self->{history}}); | 
| 1153 |  |  |  |  |  |  | return $self->{history}->[$self->{pointer}++]; | 
| 1154 |  |  |  |  |  |  | } | 
| 1155 | 0 |  |  | 0 | 0 |  |  | 
| 1156 |  |  |  |  |  |  | sub reset { (shift)->{pointer} = 0 } | 
| 1157 |  |  |  |  |  |  |  | 
| 1158 |  |  |  |  |  |  | package | 
| 1159 |  |  |  |  |  |  | DBD::Mock::Session; | 
| 1160 | 1 |  |  | 1 |  | 7 |  | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 29 |  | 
| 1161 | 1 |  |  | 1 |  | 5 | use strict; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 978 |  | 
| 1162 |  |  |  |  |  |  | use warnings; | 
| 1163 |  |  |  |  |  |  |  | 
| 1164 |  |  |  |  |  |  | my $INSTANCE_COUNT = 1; | 
| 1165 |  |  |  |  |  |  |  | 
| 1166 | 0 |  |  | 0 | 0 |  | sub new { | 
| 1167 | 0 | 0 |  |  |  |  | my $class = shift; | 
| 1168 | 0 |  |  |  |  |  | (@_) || die "You must specify at least one session state"; | 
| 1169 | 0 | 0 |  |  |  |  | my $session_name; | 
| 1170 | 0 |  |  |  |  |  | if (ref($_[0])) { | 
| 1171 |  |  |  |  |  |  | $session_name = 'Session ' . $INSTANCE_COUNT; | 
| 1172 |  |  |  |  |  |  | } | 
| 1173 | 0 |  |  |  |  |  | else { | 
| 1174 |  |  |  |  |  |  | $session_name = shift; | 
| 1175 | 0 |  |  |  |  |  | } | 
| 1176 | 0 | 0 |  |  |  |  | my @session_states = @_; | 
| 1177 |  |  |  |  |  |  | (@session_states) | 
| 1178 |  |  |  |  |  |  | || die "You must specify at least one session state"; | 
| 1179 |  |  |  |  |  |  | (ref($_) eq 'HASH') | 
| 1180 | 0 |  | 0 |  |  |  | || die "You must specify session states as HASH refs" | 
| 1181 | 0 |  |  |  |  |  | foreach @session_states; | 
| 1182 | 0 |  |  |  |  |  | $INSTANCE_COUNT++; | 
| 1183 |  |  |  |  |  |  | return bless { | 
| 1184 |  |  |  |  |  |  | name        => $session_name, | 
| 1185 |  |  |  |  |  |  | states      => \@session_states, | 
| 1186 |  |  |  |  |  |  | state_index => 0 | 
| 1187 |  |  |  |  |  |  | } => $class; | 
| 1188 |  |  |  |  |  |  | } | 
| 1189 | 0 |  |  | 0 | 0 |  |  | 
| 1190 | 0 |  |  | 0 | 0 |  | sub name  { (shift)->{name} } | 
| 1191 | 0 |  |  | 0 | 0 |  | sub reset { (shift)->{state_index} = 0 } | 
|  | 0 |  |  |  |  |  |  | 
| 1192 |  |  |  |  |  |  | sub num_states { scalar( @{ (shift)->{states} } ) } | 
| 1193 |  |  |  |  |  |  |  | 
| 1194 | 0 |  |  | 0 | 0 |  | sub has_states_left { | 
| 1195 | 0 |  |  |  |  |  | my $self = shift; | 
|  | 0 |  |  |  |  |  |  | 
| 1196 |  |  |  |  |  |  | return $self->{state_index} < scalar(@{$self->{states}}); | 
| 1197 |  |  |  |  |  |  | } | 
| 1198 |  |  |  |  |  |  |  | 
| 1199 | 0 |  |  | 0 | 0 |  | sub verify_statement { | 
| 1200 |  |  |  |  |  |  | my ($self, $dbh, $statement) = @_; | 
| 1201 | 0 |  |  |  |  |  |  | 
| 1202 | 0 | 0 |  |  |  |  | ($self->has_states_left) | 
| 1203 |  |  |  |  |  |  | || die "Session states exhausted, only '" . scalar(@{$self->{states}}) . "' in DBD::Mock::Session (" . $self->{name} . ")"; | 
| 1204 | 0 |  |  |  |  |  |  | 
| 1205 |  |  |  |  |  |  | my $current_state = $self->{states}->[$self->{state_index}]; | 
| 1206 | 0 | 0 | 0 |  |  |  | # make sure our state is good | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 1207 |  |  |  |  |  |  | (exists ${$current_state}{statement} && exists ${$current_state}{results}) | 
| 1208 |  |  |  |  |  |  | || die "Bad state '" . $self->{state_index} .  "' in DBD::Mock::Session (" . $self->{name} . ")"; | 
| 1209 | 0 |  |  |  |  |  | # try the SQL | 
| 1210 | 0 | 0 |  |  |  |  | my $SQL = $current_state->{statement}; | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 1211 | 0 | 0 |  |  |  |  | unless (ref($SQL)) { | 
| 1212 |  |  |  |  |  |  | ($SQL eq $statement) | 
| 1213 |  |  |  |  |  |  | || die "Statement does not match current state in DBD::Mock::Session (" . $self->{name} . ")\n" . | 
| 1214 |  |  |  |  |  |  | "      got: $statement\n" . | 
| 1215 |  |  |  |  |  |  | " expected: $SQL"; | 
| 1216 |  |  |  |  |  |  | } | 
| 1217 | 0 | 0 |  |  |  |  | elsif (ref($SQL) eq 'Regexp') { | 
| 1218 |  |  |  |  |  |  | ($statement =~ /$SQL/) | 
| 1219 |  |  |  |  |  |  | || die "Statement does not match current state (with Regexp) in DBD::Mock::Session (" . $self->{name} . ")\n" . | 
| 1220 |  |  |  |  |  |  | "      got: $statement\n" . | 
| 1221 |  |  |  |  |  |  | " expected: $SQL"; | 
| 1222 |  |  |  |  |  |  | } | 
| 1223 | 0 | 0 |  |  |  |  | elsif (ref($SQL) eq 'CODE') { | 
| 1224 |  |  |  |  |  |  | ($SQL->($statement, $current_state)) | 
| 1225 |  |  |  |  |  |  | || die "Statement does not match current state (with CODE ref) in DBD::Mock::Session (" . $self->{name} . ")"; | 
| 1226 |  |  |  |  |  |  | } | 
| 1227 | 0 |  |  |  |  |  | else { | 
| 1228 |  |  |  |  |  |  | die "Bad 'statement' value '$SQL' in current state in DBD::Mock::Session (" . $self->{name} . ")"; | 
| 1229 |  |  |  |  |  |  | } | 
| 1230 |  |  |  |  |  |  | # copy the result sets so that | 
| 1231 | 0 |  |  |  |  |  | # we can re-use the session | 
|  | 0 |  |  |  |  |  |  | 
| 1232 |  |  |  |  |  |  | $dbh->STORE('mock_add_resultset' => [ @{$current_state->{results}} ]); | 
| 1233 |  |  |  |  |  |  | } | 
| 1234 |  |  |  |  |  |  |  | 
| 1235 | 0 |  |  | 0 | 0 |  | sub verify_bound_params { | 
| 1236 | 0 |  |  |  |  |  | my ($self, $dbh, $params) = @_; | 
| 1237 | 0 | 0 |  |  |  |  | my $current_state = $self->{states}->[$self->{state_index}]; | 
|  | 0 |  |  |  |  |  |  | 
| 1238 | 0 |  |  |  |  |  | if (exists ${$current_state}{bound_params}) { | 
| 1239 | 0 |  |  |  |  |  | my $expected = $current_state->{bound_params}; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 1240 |  |  |  |  |  |  | (scalar(@{$expected}) == scalar(@{$params})) | 
| 1241 | 0 |  |  |  |  |  | || die "Not the same number of bound params in current state in DBD::Mock::Session (" . $self->{name} . ")\n" . | 
| 1242 | 0 | 0 |  |  |  |  | "      got: " . scalar(@{$params}) . "\n" . | 
| 1243 | 0 |  |  |  |  |  | " expected: " . scalar(@{$expected}); | 
|  | 0 |  |  |  |  |  |  | 
| 1244 | 1 |  |  | 1 |  | 7 | for (my $i = 0; $i < scalar(@{$params}); $i++) { | 
|  | 1 |  |  |  |  | 1 |  | 
|  | 1 |  |  |  |  | 245 |  | 
| 1245 | 0 | 0 |  |  |  |  | no warnings; | 
| 1246 | 0 | 0 |  |  |  |  | if (ref($expected->[$i]) eq 'Regexp') { | 
| 1247 |  |  |  |  |  |  | ($params->[$i] =~ /$expected->[$i]/) | 
| 1248 |  |  |  |  |  |  | || die "Bound param $i do not match (using regexp) in current state in DBD::Mock::Session (" . $self->{name} . ")\n" . | 
| 1249 |  |  |  |  |  |  | "      got: " . $params->[$i] . "\n" . | 
| 1250 |  |  |  |  |  |  | " expected: " . $expected->[$i]; | 
| 1251 |  |  |  |  |  |  | } | 
| 1252 | 0 | 0 |  |  |  |  | else { | 
| 1253 |  |  |  |  |  |  | ($params->[$i] eq $expected->[$i]) | 
| 1254 |  |  |  |  |  |  | || die "Bound param $i do not match in current state in DBD::Mock::Session (" . $self->{name} . ")\n" . | 
| 1255 |  |  |  |  |  |  | "      got: " . $params->[$i] . "\n" . | 
| 1256 |  |  |  |  |  |  | " expected: " . $expected->[$i]; | 
| 1257 |  |  |  |  |  |  | } | 
| 1258 |  |  |  |  |  |  | } | 
| 1259 |  |  |  |  |  |  | } | 
| 1260 |  |  |  |  |  |  | # and make sure we go to | 
| 1261 | 0 |  |  |  |  |  | # the next statement | 
| 1262 |  |  |  |  |  |  | $self->{state_index}++; | 
| 1263 |  |  |  |  |  |  | } | 
| 1264 |  |  |  |  |  |  |  | 
| 1265 |  |  |  |  |  |  | 1; | 
| 1266 |  |  |  |  |  |  |  | 
| 1267 |  |  |  |  |  |  | __END__ |