SheerSt
SheerSt

Reputation: 3337

perl - map not working, "Can't use string ... as a HASH ref"

I have the following code:

use strict;
use warnings;
use List::Util qw(max);
use DateTime;
use JSON;

use DBI;

...

my @names = @{ select_users_to_update('last_name') };

sub select_users_to_update {
    my ( $self, $column ) = @_;

    my $sql = qq{
        SELECT DISTINCT `$column`
        FROM `db_name`
        WHERE `first_name` IS NULL
    };
    my $rows = $self->{dbh}->selectall_arrayref( $sql, { Slice => {} } );
    my @fields = map { $_->{$column} } @$rows;

    return \@fields;
}

I am getting the following error:

Can't use string ("last_name") as a HASH ref while "strict refs" in use at update_hpcdb_people.pm line 51.

This code was taken from a different script, where it performed fine. I'm confused at perl's objection to the map command - what is wrong with the above code?

Upvotes: 0

Views: 424

Answers (1)

Mark Reed
Mark Reed

Reputation: 95267

TL;DR: Assuming the elided code includes something like this to connect to the database:

my $dbh = DBI->connect(....);

Then altering the call to select_users_to_update like this will do the trick:

my @names = @{ select_users_to_update( { dbh => $dbh }, 'last_name') };

The explanation follows.

The select_users_to_update subroutine expects its first argument ($self) to be a reference to a Hash containing a dbh field whose value is the handle to the database connection. You didn't pass any such thing to it; all you passed was the column name.

It's presumably from a program with a custom module, written as an object class, for handling the database stuff. The select_users_to_update subroutine is written as a method of the class, so I'm guessing that code in the original program to do what you're trying to do would look something like this:

my $customObj = CustomClass->new( database parameters ... );
my @names = @{ $customObj->select_users_to_update('last_name') };

Since calling a subroutine using the method syntax $someRef->subname is the same as passing $someRef as the first argument, the reference in $customObj would become $self inside the subroutine. As long as the constructor CustomClass::new populated dbh in the referenced Hash, it would work as designed.

But you don't have to replicate all that extra code if you don't need it and just want to use the one subroutine. You can reuse it as-is with the code in the TL;DR above.

Alternatively, you could modify the subroutine slightly, so it just takes the unadorned database handle directly instead of looking for it inside a hashref:

my @names = @{ select_users_to_update( $dbh, 'last_name') };

sub select_users_to_update {
    my ( $dbh, $column ) = @_;
    ...
    my $rows = $dbh->selectall_arrayref( $sql, { Slice => {} } );
    ...
}

And now instead of a method of a custom class, you have a standalone subroutine you can use with any old DBI object.

Upvotes: 10

Related Questions