I forgot to add that drivers authors, for drivers that support multiple
result sets, should look at this patch and do something similar.
Specifically, delete any attributes that may be in the attribute cache,
and update NUM_OF_FIELDS in the specific manner shown.
Tim.
On Fri, Feb 23, 2007 at 04:32:56PM +0000, Tim Bunce wrote:
> This patch fixes the fact that DBD::mysql doesn't clear out the
> sth attribute cache when switching between result sets, and doesn't
> adjust NUM_OF_FIELDS.
>
> Other driver authors please note the new 'official way' to adjust
> NUM_OF_FIELDS, as per this patch. DBI >= 1.54 will now automatically
> adjust the row buffer width when you adjust NUM_OF_FIELDS in this way.
> (And it does it in a way that preserves column bindings.)
>
> The patch also adds some tests to the test suite.
>
> Tim.
> diff -ru DBD-mysql-4.001/dbdimp.c DBD-mysql-4.001.gofer1/dbdimp.c
> --- DBD-mysql-4.001/dbdimp.c 2007-01-08 00:39:05.000000000 +0000
> +++ DBD-mysql-4.001.gofer1/dbdimp.c 2007-02-23 15:14:40.000000000 +0000
> @@ -2785,6 +2785,7 @@
> }
> else
> {
> + AV *av;
> /* We have a new rowset */
> imp_sth->currow=0;
>
> @@ -2806,8 +2807,32 @@
> mysql_affected_rows(svsock));
> }
>
> - /* Store the result in the current statement handle */
> - DBIc_NUM_FIELDS(imp_sth)= mysql_num_fields(imp_sth->result);
> + /* delete cached handle attributes */
> + /* XXX should be driven by a list to ease maintenance */
> + hv_delete((HV*)SvRV(sth), "NAME", 4, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "NULLABLE", 8, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "NUM_OF_FIELDS", 13, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "PRECISION", 9, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "SCALE", 5, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "TYPE", 4, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "mysql_insertid", 14, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "mysql_is_auto_increment", 23, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "mysql_is_blob", 13, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "mysql_is_key", 12, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "mysql_is_num", 12, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "mysql_is_pri_key", 16, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "mysql_length", 12, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "mysql_max_length", 16, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "mysql_table", 11, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "mysql_type", 10, G_DISCARD);
> + hv_delete((HV*)SvRV(sth), "mysql_type_name", 15, G_DISCARD);
> +
> + /* Adjust NUM_OF_FIELDS - which also adjusts the row buffer size */
> + DBIc_NUM_FIELDS(imp_sth)= 0; /* for DBI <= 1.53 */
> + DBIS->set_attr_k(sth, sv_2mortal(newSVpvn("NUM_OF_FIELDS",13)), 0,
> + sv_2mortal(newSViv(mysql_num_fields(imp_sth->result)))
> + );
> +
> DBIc_ACTIVE_on(imp_sth);
>
> if (dbis->debug >= 5)
> diff -ru DBD-mysql-4.001/t/80procs.t DBD-mysql-4.001.gofer1/t/80procs.t
> --- DBD-mysql-4.001/t/80procs.t 2006-12-24 13:35:46.000000000 +0000
> +++ DBD-mysql-4.001.gofer1/t/80procs.t 2007-02-23 15:10:14.000000000
> +0000
> @@ -113,6 +113,8 @@
> Test($state or $sth->execute()) or
> DbiError($dbh->err, $dbh->errstr);
>
> + $sth->finish;
> +
> my $proc_select = 'SELECT @a';
> Test($state or $sth = $dbh->prepare($proc_select)) or
> DbiError($dbh->err, $dbh->errstr);
> @@ -120,6 +122,8 @@
> Test($state or $sth->execute()) or
> DbiError($dbh->err, $dbh->errstr);
>
> + $sth->finish;
> +
> Test($state or ($sth=$dbh->prepare("DROP PROCEDURE testproc"))) or
> DbiError($dbh->err, $dbh->errstr);
>
> @@ -148,6 +152,9 @@
>
> my $dataset;
>
> + Test($state or ($sth->{NUM_OF_FIELDS} == 1)) or
> + DbiError($dbh->err, $dbh->errstr);
> +
> Test($state or $dataset = $sth->fetchrow_arrayref()) or
> DbiError($dbh->err, $dbh->errstr);
>
> @@ -159,6 +166,9 @@
> Test($state or $more_results = $sth->more_results()) or
> DbiError($dbh->err, $dbh->errstr);
>
> + Test($state or ($sth->{NUM_OF_FIELDS} == 2)) or
> + DbiError($dbh->err, $dbh->errstr);
> +
> Test($state or $dataset = $sth->fetchrow_arrayref()) or
> DbiError($dbh->err, $dbh->errstr);
>
> @@ -168,6 +178,9 @@
> Test($state or $more_results = $sth->more_results()) or
> DbiError($dbh->err, $dbh->errstr);
>
> + Test($state or ($sth->{NUM_OF_FIELDS} == 3)) or
> + DbiError($dbh->err, $dbh->errstr);
> +
> Test($state or $dataset = $sth->fetchrow_arrayref()) or
> DbiError($dbh->err, $dbh->errstr);
>
> @@ -177,7 +190,7 @@
> Test($state or !($more_results = $sth->more_results())) or
> DbiError($dbh->err, $dbh->errstr);
>
> - $SIG{__WARN__} = sub { die @_ };
> + local $SIG{__WARN__} = sub { die @_ };
>
> Test($state or $dbh->disconnect()) or
> DbiError($dbh->err, $dbh->errstr);
> diff -ru DBD-mysql-4.001/t/lib.pl DBD-mysql-4.001.gofer1/t/lib.pl
> --- DBD-mysql-4.001/t/lib.pl 2006-12-23 18:03:30.000000000 +0000
> +++ DBD-mysql-4.001.gofer1/t/lib.pl 2007-02-23 15:06:52.000000000 +0000
> @@ -11,6 +11,7 @@
> use strict;
> use vars qw($mdriver $dbdriver $childPid $test_dsn $test_user
> $test_password);
>
> +$| = 1; # flush stdout asap to keep in sync with stderr
>
> #
> # Driver names; EDIT THIS!
> @@ -166,7 +167,8 @@
> print "ok $::numTests\n";
> return 1;
> } else {
> - printf("not ok $::numTests%s\n",
> + my ($pack, $file, $line) = caller();
> + printf("not ok $::numTests%s at line $line\n",
> (defined($error) ? " $error" : ""));
> return 0;
> }
> Only in DBD-mysql-4.001.gofer1: x
> Only in DBD-mysql-4.001.gofer1: {mysql_errno}