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

Hi all, I'm looking to create a table where I can pass alterations to an SQL statement. Say I have a form returned to me and at the top of each column is a button with the column name. If I click on a button, the SQL statement gets altered and the results are reverse-sorted. This is what I have so far:
use strict; my ($order_by,$sql,$asc_desc); $sql = qq{SELECT * FROM TABLE}; $order_by = param('order_by'); $asc_desc = param('asc_desc'); if (defined($order_by)) { if ($asc_desc eq 'ASC') { $sql .= " ORDER BY $order_by ASC "; } else { $sql .= " ORDER BY $order_by DESC "; } } print header, start_html(), start_form(), center( p($order_by), start_table(), Tr(td(submit('order_by','FAULT_NO',)), td(submit('order_by','ISSUE')), td(submit('order_by','RESPONSE'))), end_table(), hidden('order_by',$order_by), hidden('asc_desc',$asc_desc), p($sql), ), end_form(), end_html(); exit;
Don't worry about column name generation for now... Now the default SQL is 'SELECT * FROM TABLE' and when I press the 'FAULT_NO' button, the SQL statement becomes 'SELECT * FROM TABLE ORDER BY FAULT_NO DESC'. Similar results for the other buttons. Looks like I have the table column changing as it should, but I'm having a bugger of a time trying to get the 'ORDER BY' bit to change from DESC (take as default) to ASC... A little help please? Many thanks, Stacy.

Replies are listed 'Best First'.
Re: Using CGI params for SQL statement
by dws (Chancellor) on Feb 26, 2002 at 07:44 UTC
    I'm having a bugger of a time trying to get the 'ORDER BY' bit to change from DESC (take as default) to ASC.

    In other words, in the fragment

    if ($asc_desc eq 'ASC') { $sql .= " ORDER BY $order_by ASC "; } else { $sql .= " ORDER BY $order_by DESC "; }
    you're always ending up with DESC. This suggests that $asc_desc isn't 'ASC', which you should be able to verify pretty quickly be viewing source for the page you're emitting, since you're putting out $asc_desc in a hidden field.

    What RDBMS are you using? If you're using MySQL, 'ASC' is the default sort order, and you only add DESC to alter the order.

      I'm using Oracle, which also has ASC as the default sort order. I forgot about that - thanks for reminding me. I gues my next task is to determine if the same column has been pressed and if so, change the SQL statement. Regards, Stacy.
Re: Using CGI params for SQL statement
by IlyaM (Parson) on Feb 26, 2002 at 09:31 UTC
    This code contains BIG security hole. Values of CGI parameters must be validated before using them in SQL query. It is possible for 'bad person' to modify your query in very bad ways. There is good non-Perl specific paper about it: SQL Injection

    --
    Ilya Martynov (http://martynov.org/)

      The code here is basic and I would never use anything like it on a server. In reality I always check returned params.
      By keeping code simple for as long as possible alows me to work through the problem and hopefully let others help me out :)
      Stacy.
        IMHO in any event it is better to post code which follows good coding practices unless it requires significant efforts. There exist danger that some newbie will just copy your code blindly. Were you added just one line which checks value of CGI parameter there could be a chance to educate him/her about proper way.

        --
        Ilya Martynov (http://martynov.org/)

Re: Using CGI params for SQL statement
by George_Sherston (Vicar) on Feb 26, 2002 at 10:16 UTC
    This is probably obvious, in which case I apologise, but there doesn't seem to be any way for your form to tell perl what you want $asc_desc to be... so param('asc_desc') is always undefined and $asc_desc always ends up as DESC.

    You'll need a radio_group() or popup_menu() to select ASC or DESC. In fact, I'd be inclined to do the whole thing with radio_group() or popup_menu() rather than multiple submit buttons - not quite as ergonomic less mind-mangling.

    § George Sherston
      Hi all, thanks for your suggestions. My solution is appended below. Passing the hidden field $input solved my problem...
      Regards,
      Stacy.
      use CGI qw(:standard :netscape *table); use CGI::Carp qw(fatalsToBrowser); use CGI::Pretty qw( :html3 ); use strict; my ($order_by,$sql,$order_by_check,$order_by_input); $sql = qq{SELECT * FROM TABLE}; $order_by = param('order_by'); $order_by_check = param('order_by_check'); if ($order_by) { $sql .= " ORDER BY $order_by "; if ($order_by_check eq $order_by) { $sql .= ' DESC '; $order_by_check = $order_by; } else { $sql .= ' ASC '; $order_by_input = "<INPUT TYPE=hidden NAME=order_by_check VALUE=\" +$order_by\">"; } } print header, start_html(), start_form(), center( start_table(), Tr(td(submit('order_by','FAULT_NO',)), td(submit('order_by','ISSUE')), td(submit('order_by','RESPONSE'))), end_table(), p($sql), $order_by_input, ), end_form(), end_html(); exit;
        Instead of relying on user input for the values of the params 'order_by' and 'order_by_check', you should use a lookup table instead. Consider this: the user enters in their own values instead of using the buttons you supply:
        bar.cgi?order_by=foo&order_by_check=bar
        
        Now what? You get a database error. A lookup table is a hash that contains valid keys, the values can be any true value, :
        my %lookup = ( FAULT_NO => 'FAULT_NO', ISSUE => 'ISSUE', RESPONSE => 'RESPONSE', ); # task of automating the creation of the hash # is left as an excerise to the reader ;)
        Now, instead of just accepting the parameter as is from CGI.pm, you can 'filter' it:
        $order_by = $lookup{ param('order_by') }; $order_by_check = $lookup{ param('order_by_check') }; if ($order_by) { ... } ...
        When the user enters a value for a field that doesn't exist, the error is treated the same as if the user did not enter any value for a field at all. Works like a charm ... until you want to inform the user of that fact. :(

        jeffa

        L-LL-L--L-LL-L--L-LL-L--
        -R--R-RR-R--R-RR-R--R-RR
        B--B--B--B--B--B--B--B--
        H---H---H---H---H---H---
        (the triplet paradiddle with high-hat)