maderman has asked for the wisdom of the Perl Monks concerning the following question:

Hi all, just wondering if I can clean up (ie reduce) the following bit of code:
if (defined($allocated_to) && $allocated_to ne '') { push @where_clause, "UPPER(ALLOCATED_TO) LIKE UPPER('\%$allocated_to\% +')"; push @entries, 'ALLOCATED_TO'; } if (defined($contact_person) && $contact_person ne '') { push @where_clause, "UPPER(CONTACT_PERSON) LIKE UPPER('\%$contact_pers +on\%')"; push @entries, 'CONTACT_PERSON'; }
Many thanks, Stacy.

Replies are listed 'Best First'.
Re: Cleanup time
by Corion (Patriarch) on Nov 07, 2001 at 18:06 UTC

    If you know that $allocated_to will never contain "0" (a zero), then you can simplify the code to :

    if ($allocated_to) { push @where_clause, "UPPER(ALLOCATED_TO) LIKE UPPER('%$allocated_to% +')"; push @entries, 'ALLOCATED_TO'; }

    (the % don't need to be escaped in Perl strings) - possibly you might want to collapse both cases into a third subroutine like the following :

    sub add_if_defined { my ($columnname,$queryvar) = @_; die "add_if_defined: First parameter (columnname) may not be empty ( +maybe you supplied columnname and queryvar in the wrong order ?)" unless $columnname; if (defined($queryvar) && $queryvar ne '') { push @where_clause,"UPPER($columname) LIKE UPPER('%$queryvar%')"; push @entries, $columnname; } };
    and then call that routine like follows :
    add_if_defined("ALLOCATED_TO",$allocated_to); add_if_defined("CONTACT_PERSON",$contact_person);

    This might make your code more readable, but it will also hide the actual side effects in the subroutine.

    perl -MHTTP::Daemon -MHTTP::Response -MLWP::Simple -e ' ; # The $d = new HTTP::Daemon and fork and getprint $d->url and exit;#spider ($c = $d->accept())->get_request(); $c->send_response( new #in the HTTP::Response(200,$_,$_,qq(Just another Perl hacker\n))); ' # web
Re: Cleanup time
by Masem (Monsignor) on Nov 07, 2001 at 19:21 UTC
    Corion's got good ideas, but as it looks like you're playing with DBI, you'd really want to go with place holders if you can. Even if this is an intranet application, you should be thinking about security and taintness of your data at all times; DBI's placeholders do any automatic quoting of what goes in as to protect your database from harm.

    If I assume that you eventually have code like:

    # @where_clause has been fully defined my $query = "SELECT * FROM $table WHERE "; $query .= join " AND ", @where_clause; my $sth->prepare( $query ) or die #stuff; $sth->execute( ) or die #stuff;
    Then you can still use placeholders by pushing the data into another array, which is then passed to the execute() function:
    # From your code above: if (defined($allocated_to) && $allocated_to ne '') { push @where_clause, "UPPER(ALLOCATED_TO) LIKE UPPER(?)"; push @entries, 'ALLOCATED_TO'; push @values, '\%$allocated_to\%'; } # # yada yada yada # my $query = "SELECT * FROM $table WHERE "; $query .= join " AND ", @where_clause; my $sth->prepare( $query ) or die #stuff; $sth->execute( @values ) or die #stuff;

    -----------------------------------------------------
    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
    "I can see my house from here!"
    It's not what you know, but knowing how to find it if you don't know that's important

      Hi all, thanks for your replies. I've completed my task with a hash (%in) and used placeholders as follows:
      $contact_person = param("contact_person"); $allocated_to = param("allocated_to"); $status = param("status"); #Active or Closed die "At least one of the 'ALLOCATED_TO' or 'CONTACT_PERSON' fields " ."must be non-blank!" if ($contact_person eq '' && $allocated_to eq ''); $status =~ s/\s+//g; %in = ('allocated_to' => "$allocated_to", 'contact_person' => "$contact_person"); undef @where_clause; undef @entries; #one or both of allocated_to/contact_person undef @values; #entered name(s) from form #Identify if allocated_to and/or contact_person #have been selected - create some arrays while( my($key,$value) = each(%in) ) { if ($in{$key}) { push @where_clause, "UPPER($key) LIKE UPPER(?)"; push (@entries, $key); push (@values, "\%$value\%") } } #start composing the SQL: $sql = sprintf "SELECT FAULT_NO,FIX_STATUS,%s FROM TEST WHERE %s ", join(", ", @entries), join(" OR ", @where_clause); if ($status eq 'Active') { #Perform search for ACTIVE reports: $sql .= " AND (FIX_STATUS IN ('PENDING RESPONSE', 'AWAITING FIX', 'UNDER INVESTIGATION') )"; } elsif ($status eq 'Closed') {#Perform search for CLOSED reports: $sql .= " AND (FIX_STATUS IN ('FIXED', 'NO ACTION REQUIRED', 'FOR INFORMATION', 'UNRESOLVED') )"; } else { die "Value of \'$status\' unknown." } $sql .= " ORDER BY FAULT_NO DESC "; $sth = $dbh->prepare($sql); $sth->execute( @values );
      This works a charm! Again, thank to all for you contributions. Regards, Stacy
Re: Cleanup time
by Fastolfe (Vicar) on Nov 07, 2001 at 19:56 UTC
    As Masem noted, you're doing things that are horribly insecure here.

    Generally when doing CGI (especially where that CGI does stuff interesting with the filesystem, processes or a back-end piece like a database), you want to run with taint-checking enabled, and set the "Taint" flag on your DBI object so that it won't let you pass untainted data.

    #!/usr/bin/perl -wT use DBI; my $dbh = DBI->connect($dsn, $user, $password, { Taint => 1 }); ...