2

A newborn Perl developer here. I've been wracking my brain and searching all over online trying to figure this out... exasperated, I am come to you seeking clarity.

I have the following code (only relevant parts left in) as rest is working):

my @arrMissingTids;
@arrMissingTids = %hshTids;

my $missingtid;
foreach $missingtid (@arrMissingTids) {
    print "$missingtid\n";
}

This works well, returning the values I want to be in the array:

500000246,500000235,500000185,500000237,500000227,500000252

However, when I pass this to a subroutine and include it in a variable name, it does not provide the list as written above, but rather just the number 1. Code for that is below:

myqry(@arrMissingTids);

sub myqry($) {

    my $missingtids = @_;

    $sql = "select 
        i.tid i_tid, i.name i_name
        from 
        instrument i
        where i.tid in ($missingtids)";

    print "$sql/n";
}

Print $sql returns the following:

Select i.tid i_tid, i.name i_name from instrument i where i.tid in (1)

When I want it to return the following:

Select i.tid i_tid, i.name i_name from instrument i where i.tid in (500000246,500000235,500000185,500000237,500000227,500000252)

Thanks in advance for any pointers in the right direction!

Greg Bacon
  • 134,834
  • 32
  • 188
  • 245
Scott Holtzman
  • 27,099
  • 5
  • 37
  • 72

6 Answers6

7

There are three problems here. The first is using a function prototype, just leave it away, see Why are Perl 5's function prototypes bad?.

The second is a mismatch of types in the function call and at the recipient side of the function itself. Either use arrays both times, or array references both times.

The third is that treating data as part of a SQL query, possibly opening the gate for a SQL injection attack. This is safely mitigated by assembling the query string with placeholders for use with DBI.

myqry(@arrMissingTids);
sub myqry {
    my @missingtids = @_;
    $sql = "select
        i.tid i_tid, i.name i_name
        from
        instrument i
        where i.tid in (" . join(',', ('?') x @missingtids) . ")";
    print "$sql\n";
    # $dbh->selectall_arrayref($sql, {}, @missingtids)
}

myqry(\@arrMissingTids);
sub myqry {
    my @missingtids = @{ shift() };
    $sql = "select
        i.tid i_tid, i.name i_name
        from
        instrument i
        where i.tid in (" . join(',', ('?') x @missingtids) . ")";
    print "$sql\n";
    # $dbh->selectall_arrayref($sql, {}, @missingtids)
}
Community
  • 1
  • 1
daxim
  • 39,270
  • 4
  • 65
  • 132
  • Changed the code to deal with database placeholders. Well reminded, [Joel](http://stackoverflow.com/users/468327/joel-berger)! – daxim May 09 '12 at 22:14
  • Thanks Daxim. I appreciate this little nook about passing the SQL. On a steep learning curve, so appreciate all the help. – Scott Holtzman May 10 '12 at 20:40
5

If anyone hasn't mention it yet, the prototype here is an issue:

sub myqry($) {

Consider this:

sub test1($) {
    print "$_\n" foreach @_;
}

sub test2 {
    print "$_\n" foreach @_;
}

my @args = ('a', 'b', 'c');

test1(@args);
test2(@args);      

and the output:

3
a
b
c

By now you've realized that an array in scalar context is just the number of elements, eg:

my $n = @args;

$n is 3. By passing an array into a subroutine which reduces it to a scalar, you end up with one arg, the number of elements in the array. Then you do this:

my $missingtids = @_;

which will always just be one because of the ($) in the sub definition (the array has already been reduced to one element). Hence, you get 1.

$0.02: IMO prototypes in perl are a bad idea ;)

CodeClown42
  • 11,194
  • 1
  • 32
  • 67
1

The problem is right here:

my $missingtids = @_;

You're calling the array @_ in scalar context. That means that you're assigning to $missingtids the number of elements in @_. One way to work around this would be to pass an array reference:

sub myqry {

    my $missingtids_ref = shift;
    my @missingtids=@$missingtids_ref;

    $sql = "select 
        i.tid i_tid, i.name i_name
        from 
        instrument i
        where i.tid in (" . join(",",@missingtids) . ")";

    print "$sql/n";
}

For more information, take a look at perldoc perlref and perldoc perldata.

  • 1
    accepting this as answer as it was first and solved the problem... great, great help all over though. i very much appreciate all the feedback! – Scott Holtzman May 09 '12 at 18:25
0

So you want to create the string

... in (1,2,3,4)

Use join

myqry(join(',', @arrMissingTids))

But that's rather inside-out. This would be better:

sub myqry {
    my $missingtids = join(',', @_);

    return "select 
        i.tid i_tid, i.name i_name
        from 
        instrument i
        where i.tid in ($missingtids)
    ";
}

myqry(@arrMissingTids);
ikegami
  • 367,544
  • 15
  • 269
  • 518
0

Thanks for all the help. After all is said and done, I ended up getting rid of the prototype and using this code, which worked perfectly and was pieced together from all the help above:

myqry(@arrTids);

sub myqry {
   $missingtids = join(",",@_);

   .rest of code...
}
Scott Holtzman
  • 27,099
  • 5
  • 37
  • 72
-1

Untested, but probably right:

myqry(\@arrMissingTids);

sub myqry($) {

    my $missingtids = shift; # or $_[0]

    $sql = "select 
        i.tid i_tid, i.name i_name
        from 
        instrument i
        where i.tid in (" . join(',', @{$missingtids}) . ")";

    print "$sql/n";
}

Of course you could pass the array itself instead of a reference, but then you would need to change the prototype and rewrite the array references. But the stuff above should get you going.

Marius Kjeldahl
  • 6,830
  • 3
  • 33
  • 37
  • 4
    Best not to use the prototype on the function. In almost all cases, breaks far more things than it solves. Just avoid them. – LeoNerd May 09 '12 at 16:19
  • 1
    or at least use the prototype `(\@)` to force arrays to arrayrefs rather than to force scalar context which is almost certainly not what's wanted here. – Joel Berger May 09 '12 at 16:27
  • @goldilocks, I don't follow, do you mean that `myqry(\@array)` wont work if `myqry` is prototyped `($)`? It should. – Joel Berger May 09 '12 at 17:47
  • Whoops! My bad. I didn't notice the reference or the subsequent `@{$dereference}`. Sorry. – CodeClown42 May 09 '12 at 18:02