views:

110

answers:

3

I'm very new to Perl and I'm confused with exactly how its variable scope works. I'm trying to create an array of Hashes from the result of a MySQL query.

The following code works, as intended, without use strict

  my %hash = ();

  while (my %hash = %{$qhand->fetchrow_hashref()} ) {
    push(@results, {%hash});
  }

but when strict is enabled it produces the following error:

Can't use an undefined value as a HASH reference at [filename] line XX (the line of the while statement).

Could someone tell me what I'm doing wrong and what the corresponding rule in strict is that I'm flaunting?

+3  A: 

That's a run time error (after the script has been working for a while), rather than a compile time error, isn't it?

You need to check that the hash ref returned from $qhand->fetchrow_hashref() (from Perl DBI) is valid before converting the hash ref into a hash. When there are no more rows to fetch, you get back an undef, and you can't convert the undef into %hash.

You also don't want two hashes called %hash - either in the loop or not in the loop, but not both.

  while (my $href = $qhand->fetchrow_hashref())
  {
      my %hash = %{$href};
      push(@results, {%hash});
  }
Jonathan Leffler
You're making two copies of the hashref. You want `push(@results, \%hash);` or `push(@results, {%$href});`. And as brian points out, it's not actually necessary to copy the hashrefs at all.
cjm
That part I copied from the question without really bothering to analyze what else was going on; the key point was that the code was dereferencing `undef` and triggering the message.
Jonathan Leffler
jamiei
I explained everything in my answer.
brian d foy
A: 

I'm not a Perl expert, but it's probably the cast to hash on the function call $qhand->fetchrow_hashref(). If this function already returns a hash, why not just skip the cast?

Dave
The language is "Perl", not "PERL", and it returns a hashref, not a hash.
David Dorward
"cast"? Perl is not C.
Ether
if you are not a Perl expert don't waste peoples time with "probably. ..."
justintime
+7  A: 

You're violating the refs portion of strict. When you try to use a non-reference value as a reference, Perl wants to create a "symbolic reference", which is usually not what you want although it silently continues the program (probably not "working", but just continuing). By enabling strictures, you catch those cases.

In your example and Jonathan's answer, it looks like you are doing a lot of acrobatics to undo hash references just to make them hash references again. Is there a reason you don't just leave it as a hash reference?

while( my $href = $qhand->fetchrow_hashref ) {
  push @results, $href;
  }

And, if you just want to get all the results as hash references, there's a DBI method for that so you can skip the while loop:

my $results_array_ref = $qhand->fetchall_arrayref( {} );
brian d foy
The reason for copying the hashrefs is probably that he`s confused `fetchrow_hashref` with `fetchrow_arrayref`. The latter keeps returning the _same_ arraryref, so you can't just push them onto an array, or you'll have N copies of the last row. But `fetchrow_hashref` is documented to return a new hashref each time, so that isn't an issue.
cjm
@brian d foy: You're probably correct, i'll fully admit to not being aware of the full extent of my reference folly but it became thus from the need to create a certain data structure for producing json from after this code. I will try your alternatives though, Thank-you.
jamiei