Connecting Tech Pros Worldwide Help | Site Map

Problems with Prepare and Execute of a Query

Newbie
 
Join Date: Oct 2006
Posts: 7
#1: Nov 4 '06
I'm having problems with my Prepare and Execute statements.

I get a syntax error onmy prepare and of course the execute won't run on an undefined value.

I don't know what I am overlooking.

Expand|Select|Wrap|Line Numbers
  1. #!/Perl/bin/perl
  2. # Program to read information sent to the server
  3. # from the form in the contact_list.xhtml document.
  4.  
  5. use CGI qw( :standard );
  6. use DBI;
  7.  
  8. $dtd =
  9. "-//W3C//DTD XHTML 1.0 Transitional//EN\"
  10.    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd";
  11.  
  12. print( header() );
  13. print( start_html( { dtd    => $dtd,
  14.                      title  => "Order Response" } ) );
  15.  
  16. # generic variable to hold output message for user, with default success value
  17. my $message = "<b>Order Successful!  <br /> Thanks!</b>";
  18.  
  19. # try updating database
  20. # open connection to Access database
  21. # no passwords on our database
  22. $dbh = DBI->connect('dbi:ODBC:KTC')or die "Database connection failed.";
  23.  
  24. # server-side check of quantity
  25. $requestedQuantity = param( "quantity" );
  26. my $check=quantityValid();
  27. if ( ! $check ) {
  28.    # @TODO add not available message here
  29.   $message = "<b>Order NOT Successful!  <br /> Please Try Again!</b>";
  30. } else {
  31.    $customerID=param("customer_id");
  32.    $productID=param("product_id");
  33.    $quantity=param("quantity");
  34.    $deliveryDate=param("delivery_date");
  35.  
  36.    $query1 = "INSERT INTO Orders (CustomerID, ProductID, OrderQuantity, RequestedShipDate)
  37.                     VALUES ($customerID,$productID,$quantity,$deliveryDate);
  38.    ##                  VALUES (?,?,?,?)";
  39.  
  40.    my $insertHandle = $databaseHandle->prepare_cached($query1) 
  41.    or die "Couldn't prepare insert query; aborting "
  42.       unless defined $insertHandle;
  43.  
  44.    my $success = 1;
  45.    $success &&= $insertHandle->execute($customerID,$productID,$quantity,$deliveryDate)
  46.    or die "Execute failed.";
  47.  
  48.    # autocommit is on, so no need to commit
  49.    # my $result = ($success ? $databaseHandle->commit : $databaseHandle->rollback);
  50.    unless ($success) { 
  51.       die "Couldn't finish transaction: " . $databaseHandle->errstr 
  52.    }
  53.  
  54.    $insertHandle->finish();
  55. }
  56.  
  57. $databaseHandle->disconnect();
  58.  
  59. print( $message );
  60. print( end_html() );
  61.  
  62.  
  63. sub quantityValid {
  64.  
  65.    # use ProductID to find a product name
  66.  
  67.    $productID = param("product_id");
  68.    # @TODO get product ID from web page
  69.  
  70.    $query1 = "SELECT UnitsInStock FROM Products WHERE ProductID = $productID";
  71.  
  72.    #@TODO add query to product table
  73.  
  74.    # @TODO prepare query with existing database handle
  75.    $query_handle = $dbh->prepare($query1);
  76.  
  77.    # @TODO execute query
  78.    $query_handle->execute() or die $dbh->errstr;
  79.  
  80.    # @TODO loop through results put available amount into $availAmount
  81.    while ( @availAmount = $query_handle->fetchrow_array()){
  82.         $availAmount = $availAmount[0];
  83.    }
  84.  
  85.       # @TODO test quantity and return 1 (for true) is ok
  86.    #                         return 0 (for false) if not ok
  87.    if ($requestedQuantity <= $availAmount){
  88.         return 1;
  89.    } else {
  90.         return 0;
  91. }
  92. }
  93.  
Any help wopuld be greatly appreciated.
miller's Avatar
Moderator
 
Join Date: Oct 2006
Location: San Francisco, CA
Posts: 830
#2: Nov 6 '06

re: Problems with Prepare and Execute of a Query


Your error occurs because you created the database handle using the variable name "$dbh", but you atttempt to use the variable name "$databaseHandle" which does not exist.

Ultimately your biggest problem is the fact that you do not have the line "use strict;" included at the top of your file, therefore enforcing that you scope all of your variables. If you had done this it would have been able to throw an error when you attempted to access the $databaseHandle variable which would be undefined.

Before noticing that error through, I went through your script and added comments to all of the lines that I think you should change or at least think about. I hope that this helps, and good luck.

Expand|Select|Wrap|Line Numbers
  1. #!/Perl/bin/perl
  2. # Program to read information sent to the server
  3. # from the form in the contact_list.xhtml document.
  4.  
  5. use CGI qw( :standard );
  6. use DBI;
  7.  
  8. #WARNING: "use strict;"  It will help you avoid all sorts of simple typo and spelling errors.  Yes, it will require that you always scope your variables with either "my" or "our", but it is good to be thinking about such things anyway.
  9.  
  10. $dtd =
  11. "-//W3C//DTD XHTML 1.0 Transitional//EN\"
  12.    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd";
  13.  
  14. print( header() );
  15. print( start_html( { dtd    => $dtd,
  16.                      title  => "Order Response" } ) );
  17.  
  18. # generic variable to hold output message for user, with default success value
  19. my $message = "<b>Order Successful!  <br /> Thanks!</b>";
  20.  
  21. # try updating database
  22. # open connection to Access database
  23. # no passwords on our database
  24. $dbh = DBI->connect('dbi:ODBC:KTC')or die "Database connection failed.";
  25.  
  26. # server-side check of quantity
  27. $requestedQuantity = param( "quantity" );
  28. my $check=quantityValid();
  29. if ( ! $check ) {
  30.    # @TODO add not available message here
  31.   $message = "<b>Order NOT Successful!  <br /> Please Try Again!</b>";
  32. } else {
  33.    $customerID=param("customer_id");
  34.    $productID=param("product_id");
  35.    $quantity=param("quantity");
  36.    $deliveryDate=param("delivery_date");
  37.  
  38. #WARNING: When INSERTing a single record, it is better to use the SET syntax for the query instead of values as that way you can confirm that the key value pairs are matched properly.
  39. #WARNING: Stick with placeholders.  It appears that you commented out their use, but they take care of a lot of important security and functional concerns dealing with escaping characters, dealing with empty values, and avoiding security vulnerabilities.
  40.    $query1 = "INSERT INTO Orders (CustomerID, ProductID, OrderQuantity, RequestedShipDate)
  41.                     VALUES ($customerID,$productID,$quantity,$deliveryDate);
  42.    ##                  VALUES (?,?,?,?)";
  43.  
  44. #WARNING: your "unless defined" statement is never reached.  In this instance it doesn't do anything wrong since there is no way that $insertHandle is defined in this script.  But if there were an $insertHandle higher in scope, this statement could potentially lead to bad bad things.  Never a statement like this: "my $var = 'value' if $some_condition;"  This leads to really really wierd things in perl.  Instead if you truly mean optional assignment, do "my $var = $some_condition ? 'value' : undef;".  In this instance though, that test always passes so just remove it.
  45. #WARNING: Once your $dbh is created, always include the $dbh->errstr in your "or die" statement.  This will ensure the best changes of debugging hte problem
  46.    my $insertHandle = $databaseHandle->prepare_cached($query1) 
  47.    or die "Couldn't prepare insert query; aborting "
  48.       unless defined $insertHandle;
  49.  
  50. #WARNING 1: Your assignment of $success here is redundant.  Simply assign it the return value of the execute statement directly instead of the unnecessary boolean operator trick.
  51. #WARNING 2: Your assignment of $success is redundant for a second reason.  If $success is assigned a null value, than also means that the die statement would have been called, and so the assignment is meaningless.  Instead simply include the $dbh->errstr directly in your "or die" statement.
  52.    my $success = 1;
  53.    $success &&= $insertHandle->execute($customerID,$productID,$quantity,$delivery  Date)
  54.    or die "Execute failed.";
  55.  
  56.    # autocommit is on, so no need to commit
  57.    # my $result = ($success ? $databaseHandle->commit : $databaseHandle->rollback);
  58.    unless ($success) { 
  59.       die "Couldn't finish transaction: " . $databaseHandle->errstr 
  60.    }
  61.  
  62.    $insertHandle->finish();
  63. }
  64.  
  65. $databaseHandle->disconnect();
  66.  
  67. print( $message );
  68. print( end_html() );
  69.  
  70.  
  71. sub quantityValid {
  72.  
  73.    # use ProductID to find a product name
  74.  
  75.    $productID = param("product_id");
  76.    # @TODO get product ID from web page
  77.  
  78. #ERROR Possible: What if $productID has not value?  The below sql query will likely fail.  Instead use placeholders in this instance.
  79.    $query1 = "SELECT UnitsInStock FROM Products WHERE ProductID = $productID";
  80.  
  81.    #@TODO add query to product table
  82.  
  83.    # @TODO prepare query with existing database handle
  84.    $query_handle = $dbh->prepare($query1);
  85.  
  86. #NOTE: Very good use of "or die $dbh->errstr" here.  You could also add it to the prepare statement, but in truth most errors are through in the execute.
  87.    # @TODO execute query
  88.    $query_handle->execute() or die $dbh->errstr;
  89.  
  90. #WARNING: Is ProductID really a 1 to many relationship for Products?  This use of a while is very suspect.  If there really are more than one record, than there is no predicted which amount will be used for the test.  This is just messy. 
  91.    # @TODO loop through results put available amount into $availAmount
  92.    while ( @availAmount = $query_handle->fetchrow_array()){
  93.         $availAmount = $availAmount[0];
  94.    }
  95.  
  96. #WARNING: you use a global variable $requestedQuantity here.  Any subroutine should be passed all the variables that it requires.  This is just smarter programming style.
  97.       # @TODO test quantity and return 1 (for true) is ok
  98.    #                         return 0 (for false) if not ok
  99.    if ($requestedQuantity <= $availAmount){
  100.         return 1;
  101.    } else {
  102.         return 0;
  103. }
  104. }
  105.  
Reply