#!/usr/bin/perl
########################################################################
# (c) Copyright IBM Corp. 2007 All rights reserved.
# 
# The following sample of source code ("Sample") is owned by International 
# Business Machines Corporation or one of its subsidiaries ("IBM") and is 
# copyrighted and licensed, not sold. You may use, copy, modify, and 
# distribute the Sample in any form without payment to IBM, for the purpose of 
# assisting you in the development of your applications.
# 
# The Sample code is provided to you on an "AS IS" basis, without warranty of 
# any kind. IBM HEREBY EXPRESSLY DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR 
# IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. Some jurisdictions do 
# not allow for the exclusion or limitation of implied warranties, so the above 
# limitations or exclusions may not apply to you. IBM shall not be liable for 
# any damages you suffer as a result of using, copying, modifying or 
# distributing the Sample, even if IBM has been advised of the possibility of 
# such damages.
#########################################################################
#
# SOURCE FILE NAME: spclient.pl
#
# SAMPLE: Call various stored procedures
#
#         This file contains eleven functions that call stored procedures:
#
#  (1) callOutLanguage: Calls a stored procedure that returns the 
#      implementation language of the stored procedure library
#        Parameter types used: OUT CHAR(8)
#  (2) callOutParameter: Calls a stored procedure that returns median 
#      salary of employee salaries
#        Parameter types used: OUT DOUBLE                    
#  (3) callInParameters: Calls a stored procedure that accepts 3 salary 
#      values and updates employee salaries in the EMPLOYEE table based 
#      on these values for a given department.
#        Parameter types used: IN DOUBLE
#                              IN DOUBLE
#                              IN DOUBLE
#                              IN CHAR(3)
#  (4) callInoutParameter: Calls a stored procedure that accepts an input
#      value and returns the median salary of those employees in the
#      EMPLOYEE table who earn more than the input value. Demonstrates how 
#      to use null indicators in a client application. The stored procedure
#      has to be implemented in the following parameter styles for it to be
#      compatible with this client application.
#        Parameter style for a C stored procedure: SQL
#        Parameter style for a Java(JDBC/SQLJ) stored procedure: JAVA
#        Parameter types used: INOUT DOUBLE
#  (5) callClobExtract: Calls a stored procedure that extracts and returns a 
#      portion of a CLOB data type
#        Parameter types used: IN CHAR(6)
#                              OUT VARCHAR(1000)
#  (6) callDBINFO: Calls a stored procedure that receives a DBINFO
#      structure and returns elements of the structure to the client
#        Parameter types used: IN CHAR(8)
#                              OUT DOUBLE
#                              OUT CHAR(128)
#                              OUT CHAR(8)
#  (7) callProgramTypeMain: Calls a stored procedure implemented with
#       PROGRAM TYPE MAIN parameter style
#         Parameter types used: IN CHAR(8)
#                               OUT DOUBLE
#  (8) callAllDataTypes: Calls a stored procedure that uses a variety of 
#      common data types (not DECIMAL, GRAPHIC, VARGRAPHIC, BLOB, CLOB, DBCLOB).
#      This sample shows only a subset of DB2 supported data types. For a  
#      full listing of DB2 data types, please see the SQL Reference.
#        Parameter types used: INOUT SMALLINT
#                              INOUT INTEGER
#                              INOUT BIGINT
#                              INOUT REAL
#                              INOUT DOUBLE
#                              OUT CHAR(1)
#                              OUT CHAR(15)
#                              OUT VARCHAR(12)
#                              OUT DATE
#                              OUT TIME
#  (9) callOneResultSet: Calls a stored procedure that return a result set to
#      the client application
#        Parameter types used: IN DOUBLE
#  (10) callTwoResultSets: Calls a stored procedure that returns two result 
#       sets to the client application
#        Parameter types used: IN DOUBLE
#  (11) callGeneralExample: Call a stored procedure inplemented with 
#       PARAMETER STYLE GENERAL 
#        Parameter types used: IN INTEGER
#                              OUT INTEGER
#                              OUT CHAR(33) 
#
#         The file "DB2SampUtil.pm" contains functions for error-checking and
#         rolling back a transaction in case of error. 
#
# SQL STATEMENTS USED:
#         CALL
#         ROLLBACK
#         SELECT
#
# EXTERNAL DEPENDENCIES:
#      For successful precompilation, the sample database must exist 
#      (see DB2's db2sampl command).
#
#      The stored procedures called from this program must have been built
#      and cataloged in the database (see the instructions in spserver.sqc
#      or spserver.c).
#
#               
##########################################################################
#
# For more information on the sample programs, see the README file.
#
# For information on developing Perl applications, see the Application
# Development Guide.
#
# For information on using SQL statements, see the SQL Reference.
#
# For the latest information on programming, building, and running DB2
# applications, visit the DB2 Information Center:
#     http://publib.boulder.ibm.com/infocenter/db2luw/v9r7/index.jsp
###########################################################################

select STDERR; $|=1;
select STDOUT; $|=1;

use strict;
use warnings; 
use DBI;
use DBD::DB2;
use DBD::DB2::Constants;

# access the module for DB2 Sample Utility functions
use DB2SampUtil;

# check and parse the command line arguments
# call the subroutine CmdLineArgChk from DB2SampUtil.pm
my ($database, $user, $password) = CmdLineArgChk(@ARGV);

# declare return code, statement handler, database handler and local variables
my ($rc, $sth, $dbh);
my ($outLang, $testLangSql, $testLangC, $testLangJava, $median) = (0);

print "HOW TO CALL VARIOUS STORED PROCEDURES.\n";

# connect to the database
print "\n  Connecting to '$database' database...";
$dbh = DBI->connect($database, $user, $password, {AutoCommit => 0})
        || die "Can't connect to $database: $DBI::errstr";
print "\n  Connected to database.\n";


# call the subroutine CallOutLanguage
$rc = CallOutLanguage($outLang);
if ($rc != 0)
{
  print"\nRollback the transaction.\n";
  $dbh->rollback();
  print "\nError: Call to stored procedure OUT_LANGUAGE failed\n";
}

# we assume that all the remaining stored procedures are also written in
# the same language as $outLang and set the following variables accordingly.
# This would help us in invoking only those stored procedures that are
# supported in that particular language.   
# index function returns position of the first occurrence of second string 
# in the first string and -1 in case the second string is not a part of the
# first 
$testLangSql = index($outLang, "SQL");
$testLangC = index($outLang, "C");
$testLangJava = index($outLang, "JAVA");

# call the subroutine CallOutParameter
$rc = CallOutParameter($median);
if ($rc != 0)
{
  print"\nRollback the transaction.\n";
  $dbh->rollback();
  print "\nError: Call to stored procedure OUT_PARAM failed\n";
}

# call the subroutine CallInParameters
$rc = CallInParameters();
if ($rc != 0)
{
  print"\nRollback the transaction.\n";
  $dbh->rollback();
  print "\nError: Call to stored procedure IN_PARAM failed\n";
}

# call the subroutine CallInoutParameter
printf "\nCALL stored procedure named INOUT_PARAM \n";
printf "using the median returned by the call to OUT_PARAM \n";
$rc = CallInoutParameter($median); 
if ($rc != 0)
{
  print"\nRollback the transaction.\n";
  $dbh->rollback();
  print "\nError: Call to stored procedure INOUT_PARAM failed\n";
} 

# call INOUT_PARAM stored procedure two more times to intentionally show
# two different errors.  The first error depicts a NULL value condition that
# is returned when 'undef' is passed to the stored procedure.  The second  
# error shown is the result of a NOT FOUND error that is raised when no rows  
# are found to satisfy a query in the procedure.  No row is found because the 
# query depends on the procedure's input parameter value which is too high.
print "\nCALL stored procedure INOUT_PARAM again\n";
print "using a NULL input value\n";
print "\n**************** Expected Error ******************\n\n";
$rc = CallInoutParameter(undef);
printf "**************************************************\n";
  
print "\nCALL stored procedure INOUT_PARAM again \n";
print "using a value that returns a NOT FOUND error from the ";
print "stored procedure\n";
print "\n**************** Expected Error ******************\n\n";
$rc = CallInoutParameter(99999.99);
printf "**************************************************\n";

# call the subroutine CallClobExtract 
if ($testLangC == 0) 
{ 
  # warn the user that the CLI stored procedure requires a change 
  # to the UDF_MEM_SZ variable 
  print "\n  If the CLOB EXTRACT stored procedure is implemented\n";
  print "  using CLI, you must increase the value of the UDF_MEM_SZ\n";
  print "  database manager configuration variable to at least two\n";
  print "  pages larger than the size of the input arguments and\n";
  print "  the result of the stored procedure. To do this, issue\n";
  print "  the following command from the CLP:\n";
  print "    db2 UPDATE DBM CFG USING UDF_MEM_SZ 2048\n";
  print "  For the change to take effect, you must then stop and\n";
  print "  restart the DB2 server by issuing the following\n";
  print "  commands from the CLP:\n";
  print "    db2stop\n";
  print "    db2start\n";
} 
$rc = CallClobExtract(); 
if ($rc != 0)
{
  print"\nRollback the transaction.\n";
  $dbh->rollback();
  print "\nError: Call to stored procedure CLOB_EXTRACT failed\n";
} 

if ($testLangC != 0) 
{ 
  # stored procedures of PARAMETER STYLE DB2SQL, DBINFO, or PROGRAM TYPE  
  # MAIN can only be implemented by LANGUAGE C stored procedures. 
  # If language != "C", we know that those stored procedures are 
  # not implemented, and therefore do not call them. 
} 
else 
{ 
  # call the subroutine CallDBINFO 
  $rc = CallDBINFO(); 
  if ($rc != 0)
  {
    print"\nRollback the transaction.\n";
    $dbh->rollback();
    print "\nError: Call to stored procedure DBINFO_EXAMPLE failed\n";
  } 
   
  # call the subroutine CallProgramTypeMain 
  $rc = CallProgramTypeMain(); 
  if ($rc != 0)
  {
    print"\nRollback the transaction.\n";
    $dbh->rollback();
    print "\nError: Call to stored procedure MAIN_EXAMPLE failed\n";
  } 
}

########################################################################
# Perl applications do not provide direct support for the DECIMAL
# data type.
# The following programming languages can be used to directly manipulate
# the DECIMAL type:
#          - JDBC
#          - SQLJ
#          - SQL routines
#          - .NET common language runtime languages (C#, Visual Basic)
# Please see the SpClient implementation for one of the above languages
# to see this functionality.
########################################################################

# call the subroutine CallAllDataTypes
$rc = CallAllDataTypes();
if ($rc != 0)
{
  print"\nRollback the transaction.\n";
  $dbh->rollback();
  print "\nError: Call to stored procedure ALL_DATA_TYPES failed\n";
}

# call the subroutine CallOneResultSet
$rc = CallOneResultSet($median);
if ($rc != 0)
{
  print"\nRollback the transaction.\n";
  $dbh->rollback();
  print "\nError: Call to stored procedure ONE_RESULT_SET failed\n";
}

# call the subroutine CallTwoResultSets
$rc = CallTwoResultSets($median);
if ($rc != 0)
{
  print"\nRollback the transaction.\n";
  $dbh->rollback();
  print "\nError: Call to stored procedure TWO_RESULT_SETS failed\n";
}

# call the subroutine CallGeneralExample
$rc = CallGeneralExample(16);
if ($rc != 0)
{
  print"\nRollback the transaction.\n";
  $dbh->rollback();
  print "\nError: Call to stored procedure GENERAL_EXAMPLE failed\n";
}

# call the subroutine CallGeneralExample
$rc = CallGeneralWithNullsExample(2);
if ($rc != 0)
{
  print"\nRollback the transaction.\n";
  $dbh->rollback();
  print "\nError: Call to stored procedure GENERAL_EXAMPLE failed\n";
}
  
# call GENERAL_WITH_NULLS_EXAMPLE stored procedure again 
# GENERAL_WITH_NULLS_EXAMPLE to depict NULL value        
print "\nCALL stored procedure GENERAL_WITH_NULLS_EXAMPLE again\n";
print "using a NULL input value\n";
printf "\n**************** Expected Error ******************\n";
$rc = CallGeneralWithNullsExample(undef);
printf "\n**************************************************\n";
 
# no more data to be fetched from statement handle
$sth->finish;

print"\nRollback the transaction.\n";
$dbh->rollback();

print"\n  Disconnecting from sample...";
$dbh->disconnect();
print"\n  Disconnected from sample.\n";

##########################################################################
# Description: Call OUT_LANGUAGE stored procedure 
# Input      : string outLang which gets updated after the call to 
#              OUT_LANGUAGE 
# Output     : Returns 0 on success 
##########################################################################
sub CallOutLanguage
{ 
  # declare local variables
  my $callStmt = qq(CALL OUT_LANGUAGE (?)); 
  $outLang = $_[0];

  $rc = -1;
  printf("\nCALL stored procedure named OUT_LANGUAGE");
  
  eval    
  {
    # prepare call  statement
    $sth = $dbh->prepare($callStmt)
      || die $sth->errstr;

    # bind a value with a placeholder embedded in the prepared statement
    $sth->bind_param_inout(1, \$outLang, 8, { 'TYPE' => SQL_VARCHAR })
      || die $sth->errstr;
    
    # execute call statement
    $sth->execute()
      || die $sth->errstr;

    print "\nStored procedure returned successfully.";
    print "\nStored procedures are implemented in LANGUAGE $outLang\n";

    # no more data to be fetched from statement handle
    $sth->finish;

    $dbh->rollback();                
    $rc = 0;
  };
    
  return $rc; 
} # CallOutLanguage

##########################################################################
# Description: Call OUT_PARAM stored procedure
# Input      : Median
# Output     : Returns 0 on success 
##########################################################################
sub CallOutParameter
{
  # declare local variables
  my $outMedian;
  
  $rc = -1;
  $median = $_[0];
  
  printf("\nCALL stored procedure named OUT_PARAM\n");
 
  my $callStmt = qq(CALL OUT_PARAM (?));
  eval
  {
    # prepare call  statement
    my $sth = $dbh->prepare($callStmt)
      || die $sth->errstr;

    # bind a value with a placeholder embedded in the prepared statement
    $sth->bind_param_inout(1, \$outMedian, 31, { 'TYPE' => SQL_DOUBLE })
      || die $sth->errstr;

    # execute call statement
    $sth->execute()
      || die $sth->errstr; 
          
    print "Stored procedure returned successfully.\n";
    # display the median salary returned as an output parameter   
    printf("Median salary returned from OUT_PARAM = %8.2f\n", $outMedian);
  
    $median = $outMedian;
  
    # no more data to be fetched from statement handle
    $sth->finish;
   
    $dbh->rollback();
    $rc = 0;
   }; 
  
  return $rc;
} # CallOutParameter

##########################################################################
# Description: Call IN_PARAMS stored procedure
# Input      : None
# Output     : Returns 0 on success
##########################################################################
sub CallInParameters
{

  # declare local variables 
  my $inLowsal = 15000;
  my $inMedsal = 20000;
  my $inHighsal = 25000;
  my $inDept = 'E11';
  my ($selectStmt, $callStmt, $sumSalary);
  
  $rc = -1;
  printf("\nCALL stored procedure named IN_PARAMS");
  
  $selectStmt = qq(SELECT SUM(salary) 
                     FROM employee 
                     WHERE workdept = '$inDept');

  eval
  {
    # prepare and execute the SQL statement
    # call the subroutine PrepareExecuteSql() from DB2SampUtil.pm
    $sth = PrepareExecuteSql($dbh, $selectStmt);

    # fetch the sum into a variable for display
    my $sumSalary = $sth->fetchrow();
    printf("\nSum of salaries for dept %s = %8.2f before calling IN_PARAMS\n",
           $inDept, $sumSalary);

    my $callStmt = qq(CALL IN_PARAMS (?, ?, ?, ?));
  
    # prepare call  statement
    $sth = $dbh->prepare($callStmt)
      || die $sth->errstr;            
 
    # bind a value with a placeholder embedded in the prepared statement
    $sth->bind_param(1, $inLowsal, { 'TYPE' => SQL_DOUBLE })
       || die $sth->errstr;
    $sth->bind_param(2, $inMedsal, { 'TYPE' => SQL_DOUBLE })
       || die $sth->errstr;
    $sth->bind_param(3, $inHighsal, { 'TYPE' => SQL_DOUBLE })
       || die $sth->errstr;
    $sth->bind_param(4, $inDept, { 'TYPE' => SQL_VARCHAR })
       || die $sth->errstr;
  
    # execute call statement
    $sth->execute()
       || die $sth->errstr;

    print "Stored procedure returned successfully.\n";
    # display the sum salaries for the affected department   
 
    $selectStmt = qq(SELECT SUM(salary)
                       FROM employee 
                       WHERE workdept = '$inDept');
  
    # prepare and execute the SQL statement
    # call the subroutine PrepareExecuteSql() from DB2SampUtil.pm
    $sth = PrepareExecuteSql($dbh, $selectStmt);

    # fetch the sum into a variable for display
    $sumSalary = $sth->fetchrow();
    printf("Sum of salaries for dept %s = %9.2f after calling IN_PARAMS\n",
           $inDept, $sumSalary); 
    
    # no more data to be fetched from statement handle
    $sth->finish;

    $dbh->rollback();
    $rc = 0;
  };  
  
  return $rc;
} # CallInParameters

##########################################################################
# Description : Call INOUT_PARAM stored procedure
# Input       : Median value returned from CallOutParameter function
# Output      : Returns 0 on success
##########################################################################
sub CallInoutParameter
{
   
  # declare local variables
  my $inoutMedianSalary = $_[0];
  my $callStmt = qq(CALL INOUT_PARAM (?));

  $rc = -1;

  eval
  {
    # prepare call  statement
    $sth = $dbh->prepare($callStmt)
      || die $sth->errstr;

    # bind a value with a placeholder embedded in the prepared statement
    $sth->bind_param_inout(1, \$inoutMedianSalary, 31, { 'TYPE' => SQL_DOUBLE })
      || die $sth->errstr;
 
    # execute call statement
    $sth->execute()
      || die $sth->errstr;
   
    # check that the stored procedure executed successfully
    if (defined $inoutMedianSalary)
    {
      print "Stored procedure returned successfully.\n";
      printf("Median salary returned from INOUT_PARAM = %8.2f\n",
             $inoutMedianSalary);
    }
  
    # no more data to be fetched from statement handle
    $sth->finish;

    $dbh->rollback();
    $rc = 0;
  };  
    
  return $rc;
} # CallInoutParameter

##########################################################################
# Description: Call CLOB_EXTRACT stored procedure
# Input      : Median value returned from CallClobExtract function
# Output     : Returns 0 on success
##########################################################################
sub CallClobExtract
{
  
  # declare local variables
  my $inEmpno = '000140';  
  my $callStmt = qq(CALL CLOB_EXTRACT (?, ?));
  my $outResume;
  
  $rc = -1;  
  # call CLOB_EXTRACT stored procedure      
  print "\nCALL stored procedure named CLOB_EXTRACT\n"; 

  
  
  eval
  {
    # prepare call statement
    $sth = $dbh->prepare($callStmt)
       || die $sth->errstr;

    # bind a value with a placeholder embedded in the prepared statement
    $sth->bind_param(1, $inEmpno, { 'TYPE' => SQL_CHAR })
       || die $sth->errstr;
    $sth->bind_param_inout(2, \$outResume, 1000, { 'TYPE' => SQL_VARCHAR })
       || die $sth->errstr;
 
    # execute call statement
    $sth->execute()
      || die $sth->errstr;

    print "Stored procedure returned successfully.\n";
    print "Resume section returned from CLOB_EXTRACT =\n$outResume";

    # no more data to be fetched from statement handle
    $sth->finish;
  
    $dbh->rollback();
    $rc = 0;
  };
    
  return $rc;
} # CallClobExtract

##########################################################################
# Description: Call DBINFO_EXAMPLE stored procedure
# Input      : None
# Output     : Returns 0 on success
##########################################################################
sub CallDBINFO
{

  #declare local variables
  my $inJob = "CLERK";
  my $callStmt = qq(CALL DBINFO_EXAMPLE (?, ?, ?, ?));
  my ($outSalary, $outDbname, $outDbversion);
  $rc = -1;
  # call DBINFO_EXAMPLE stored procedure      
  print "\nCALL stored procedure named DBINFO_EXAMPLE\n"; 
   
  eval
  {
    # prepare call  statement
    $sth = $dbh->prepare($callStmt)
       || die $sth->errstr;

    # bind a value with a placeholder embedded in the prepared statement
    $sth->bind_param(1, $inJob, { 'TYPE' => SQL_CHAR })
       || die $sth->errstr;
    $sth->bind_param_inout(2, \$outSalary, 31, { 'TYPE' => SQL_DOUBLE })
       || die $sth->errstr;
    $sth->bind_param_inout(3, \$outDbname, 128, { 'TYPE' => SQL_CHAR })
       || die $sth->errstr;
    $sth->bind_param_inout(4, \$outDbversion, 8, { 'TYPE' => SQL_CHAR })
       || die $sth->errstr;
  
    # execute call statement
    $sth->execute()
      || die $sth->errstr;
  
    print "Stored procedure returned successfully.\n";
    printf("Average salary for job %s = %9.2f\n", $inJob, $outSalary);
    print "Database name from DBINFO structure = $outDbname\n";
    print "Database version from DBINFO structure = $outDbversion\n";

    # no more data to be fetched from statement handle
    $sth->finish;
  
    $dbh->rollback();
    $rc = 0;
  };
    
  return $rc;
} # CallDBINFO

##########################################################################
# Description: Call MAIN_EXAMPLE stored procedure
# Input      : None
# Output     : Returns 0 on success
##########################################################################
sub CallProgramTypeMain
{

  # declare local variables
  my $inJob = "DESIGNER";
  my $callStmt = qq(CALL MAIN_EXAMPLE (?, ?));
  my $outSalary;
  $rc = -1;
  
  # call MAIN_EXAMPLE stored procedure      
  print "\nCALL stored procedure named MAIN_EXAMPLE\n"; 

  eval
  {
    # prepare call  statement
    $sth = $dbh->prepare($callStmt)
       || die $sth->errstr;

    # bind a value with a placeholder embedded in the prepared statement
    $sth->bind_param(1, $inJob, { 'TYPE' => SQL_CHAR })
       || die $sth->errstr;
    $sth->bind_param_inout(2, \$outSalary, 31, { 'TYPE' => SQL_DOUBLE })
       || die $sth->errstr;

    # execute call statement
    $sth->execute()
      || die $sth->errstr;
  
    print "Stored procedure returned successfully.\n";
    printf("Average salary for job %s = %9.2f\n", 
            $inJob, $outSalary);

    # no more data to be fetched from statement handle
    $sth->finish;
    
    $dbh->rollback();
    $rc = 0;
  };
    
  return $rc;
} # CallProgramTypeMain

##########################################################################
# Description: Call ALL_DATA_TYPES stored procedure
# Input      : None
# Output     : Returns 0 on success
##########################################################################
sub CallAllDataTypes
{
  # declare local variables
  my $inoutSmallint = 32000;
  my $inoutInteger = 2147483000;
  my $inoutBigint = 2147480000;
  
  # maximum value of BIGINT is 9223372036854775807 
  # but some platforms only support 32-bit integers 
  my $inoutReal = 100000;
  my $inoutDouble = 2500000;  
  my ($outChar, $outChars, $outVarchar, $outDate, $outTime);

  my $callStmt = qq(CALL ALL_DATA_TYPES (?, ?, ?, ?, ?, 
                                         ?, ?, ?, ?, ?));
  
  $rc = -1;
  # call ALL_DATA_TYPES stored procedure      
  print "\nCALL stored procedure named ALL_DATA_TYPES\n"; 
    
  eval
  {
    # prepare call  statement
    $sth = $dbh->prepare($callStmt)
      || die $sth->errstr;

    # bind a value with a placeholder embedded in the prepared statement
    $sth->bind_param_inout(1, \$inoutSmallint, 10, { 'TYPE' => SQL_SMALLINT  })
       || die $sth->errstr;
    $sth->bind_param_inout(2, \$inoutInteger, 10, { 'TYPE' => SQL_INTEGER })
       || die $sth->errstr;
    $sth->bind_param_inout(3, \$inoutBigint, 10, { 'TYPE' => SQL_BIGINT })
       || die $sth->errstr;
    $sth->bind_param_inout(4, \$inoutReal, 15, { 'TYPE' => SQL_REAL })
       || die $sth->errstr;
    $sth->bind_param_inout(5, \$inoutDouble, 31, { 'TYPE' => SQL_DOUBLE })
       || die $sth->errstr;
    $sth->bind_param_inout(6, \$outChar, 1, { 'TYPE' => SQL_CHAR })
       || die $sth->errstr;
    $sth->bind_param_inout(7, \$outChars, 15, { 'TYPE' => SQL_VARCHAR })
       || die $sth->errstr;
    $sth->bind_param_inout(8, \$outVarchar, 12, { 'TYPE' => SQL_VARCHAR })
       || die $sth->errstr;
    $sth->bind_param_inout(9, \$outDate, 10, { 'TYPE' => SQL_TYPE_DATE })
       || die $sth->errstr;
    $sth->bind_param_inout(10, \$outTime, 8, { 'TYPE' => SQL_TYPE_TIME })
       || die $sth->errstr;

    # execute call statement
    $sth->execute()
      || die $sth->errstr;

    print "Stored procedure returned successfully.\n";
    # display the sum salaries for the affected department 
   
    printf("Value of SMALLINT = %d\n", $inoutSmallint);
    printf("Value of INTEGER = %d\n", $inoutInteger);
    printf("Value of BIGINT = %d\n", $inoutBigint);
    printf("Value of REAL = %.2f\n", $inoutReal);
    printf("Value of DOUBLE = %.2f\n", $inoutDouble);
    printf("Value of CHAR(1) = %s\n", $outChar);
    printf("Value of CHAR(15) = %s\n", $outChars);
    printf("Value of VARCHAR(12) = %s\n", $outVarchar);
    printf("Value of DATE = %s\n", $outDate);
    printf("Value of TIME = %s\n", $outTime);

    # no more data to be fetched from statement handle
    $sth->finish;
  
    $dbh->rollback();
    $rc = 0;
  };   
  
  return $rc;
} # CallAllDataTypes

##########################################################################
# Description: Call ONE_RESULT_SET stored procedure
# Input      : Median value returned from CallOutParameter function
# Output     : Returns 0 on success
##########################################################################
sub CallOneResultSet
{
  
  # declare local variables
  my $inSalary = $_[0];
  my ($numCols,  $outName, $outJob, $outSalary);
  my $callStmt = qq(CALL ONE_RESULT_SET (?));
  
  $rc = -1;
  
  # call ONE_RESULT_SET stored procedure      
  print "\nCALL stored procedure named ONE_RESULT_SET\n"; 

  eval
  {
    # prepare call  statement
    $sth = $dbh->prepare($callStmt)
       || die $sth->errstr;

    # bind a value with a placeholder embedded in the prepared statement
    $sth->bind_param(1, $inSalary, { 'TYPE' => SQL_DOUBLE  })
       || die $sth->errstr;
  
    # execute call statement
    $sth->execute()
      || die $sth->errstr;

    $numCols = $sth->{NUM_OF_FIELDS};  
    print "Result set returned $numCols columns\n";
  
    # bind column 1 to variable 
    $sth->bind_col(1, \$outName); 

    # bind column 2 to variable 
    $sth->bind_col(2, \$outJob); 

    # bind column 3 to variable 
    $sth->bind_col(3, \$outSalary); 
 
    print "Stored procedure returned successfully.\n";
    print "\nFirst result set returned from ONE_RESULT_SET";
    print "\n------Name------,  --JOB--, ---Salary--  \n";
    while ($sth->fetchrow())
    {
      printf("%16s,%9s,    %.2f\n", $outName, $outJob, $outSalary);
    }

    # no more data to be fetched from statement handle
    $sth->finish;
    
    $dbh->rollback();
    $rc = 0;
  };  
  
  return $rc;
} # CallOneResultSet

########################################################################## 
# Description: Call TWO_RESULT_SETS stored procedure 
# Input      : Median value returned from CallOutParameter function 
# Output     : Returns 0 on success 
########################################################################## 
sub CallTwoResultSets 
{ 

  # declare local variables
  my $inSalary = $_[0];  
  my ($numCols, $outName, $outJob, $outSalary);
  my $callStmt = qq(CALL TWO_RESULT_SETS (?)); 
  
  $rc = -1;
  
  # call TWO_RESULT_SETS stored procedure 
  print "\nCALL stored procedure named TWO_RESULT_SETS\n"; 
 
  eval
  {
    # prepare call  statement 
    $sth = $dbh->prepare($callStmt) 
       || die $sth->errstr;
 
    # bind a value with a placeholder embedded in the prepared statement 
    $sth->bind_param(1, $inSalary, { 'TYPE' => SQL_DOUBLE  }) 
       || die $sth->errstr; 
 
    # execute call statement 
    $sth->execute() 
      || die $sth->errstr; 
 
    $numCols = $sth->{NUM_OF_FIELDS}; 
    print "Result set returned $numCols columns\n"; 
 
    # bind column 1 to variable 
    $sth->bind_col(1, \$outName); 
 
    # bind column 2 to variable 
    $sth->bind_col(2, \$outJob); 

    # bind column 3 to variable 
    $sth->bind_col(3, \$outSalary); 
 
    print "Stored procedure returned successfully\n"; 
    
    # fetch the first result set 
    print "\nFirst result set returned from TWO_RESULT_SETS"; 
    print "\n------Name------,  --JOB--, ---Salary--  \n"; 
    while ($sth->fetchrow()) 
    { 
      printf("%16s, %9s,    %.2f\n", $outName, $outJob, $outSalary); 
    } 
 
    # fetch the remaining result sets 
    while ($sth->{db2_more_results}) 
    { 
      print "\nNext result set returned from TWO_RESULT_SETS"; 
      print "\n------Name------,  --JOB--, ---Salary--  \n"; 
      while ($sth->fetchrow()) 
      { 
        printf("%16s, %9s,    %.2f\n", $outName, $outJob, $outSalary); 
      } 
    } 
 
    # no more data to be fetched from statement handle
    $sth->finish; 
   
    $dbh->rollback();
    $rc = 0;
  };  
  
  return $rc; 
} # CallTwoResultSets 

########################################################################## 
# Description: Call GENERAL_EXAMPLE stored procedure 
# Input      : Education Level
# Output     : Returns 0 on success 
########################################################################## 
sub CallGeneralExample
{
  # declare local variables
  my $inEdLevel = $_[0];
  my $callStmt = qq(CALL GENERAL_EXAMPLE (?, ?, ?)); 
  my ($outSqlrc, $outMsg, $numCols, $firstnme, $lastname, $workdept); 
  $rc = -1;
    
  # call GENERAL_EXAMPLE stored procedure 
  print "\nCALL stored procedure named GENERAL_EXAMPLE\n"; 

  eval
  {  
    # prepare call  statement
    $sth = $dbh->prepare($callStmt)
       || die $sth->errstr;

    # bind a value with a placeholder embedded in the prepared statement
    $sth->bind_param(1, $inEdLevel, { 'TYPE' => SQL_INTEGER })
       || die $sth->errstr;
    $sth->bind_param_inout(2, \$outSqlrc, 10, { 'TYPE' => SQL_INTEGER })
       || die $sth->errstr;
    $sth->bind_param_inout(3, \$outMsg, 32, { 'TYPE' => SQL_CHAR })
      || die $sth->errstr;

    # execute call statement
    $sth->execute()
      || die $sth->errstr;
 
    if ($outSqlrc == 0)
    {
      $numCols = $sth->{NUM_OF_FIELDS};  
      print "Result set returned $numCols columns\n";
  
      # bind column 1 to variable 
      $sth->bind_col(1, \$firstnme); 

      # bind column 2 to variable 
      $sth->bind_col(2, \$lastname); 

      # bind column 3 to variable 
      $sth->bind_col(3, \$workdept); 
    
      print "Stored procedure returned successfully.\n";
      printf("\n-----FIRSTNME-------LASTNAME-----WORKDEPT--\n");

      while (($firstnme, $lastname, $workdept) = $sth->fetchrow())
      {
        printf("%12s,       %-10s, %3s\n", $firstnme, $lastname, $workdept);
      }
    }
    else
    {
      print "Stored procedure returned SQLCODE $outSqlrc";
      print "With Error: $outMsg \n";
    }

    # no more data to be fetched from statement handle
    $sth->finish;
  
    $dbh->rollback();
    $rc = 0;
  };  
  
  return $rc;
} # CallGeneralExample

########################################################################## 
# Description: Call GENERAL_WITH_NULLS_EXAMPLE stored procedure 
# Input      : Quarter
# Output     : Returns 0 on success 
########################################################################## 
sub CallGeneralWithNullsExample
{
  
  # declare local variables
  my $inQuarter = $_[0];
  my ($outSqlrc, $numCols, $salesPerson, $region, $sales, $outMsg);
  my $callStmt = qq(CALL GENERAL_WITH_NULLS_EXAMPLE (?, ?, ?)); 
  
  $rc = -1;
  # call GENERAL_WITH_NULLS_EXAMPLE stored procedure 
  print "\nCALL stored procedure named GENERAL_WITH_NULLS_EXAMPLE\n"; 

  eval
  {  
    # prepare call  statement
    $sth = $dbh->prepare($callStmt)
       || die $sth->errstr;

    # bind a value with a placeholder embedded in the prepared statement
    $sth->bind_param(1, $inQuarter, { 'TYPE' => SQL_INTEGER })
       || die $sth->errstr;
    $sth->bind_param_inout(2, \$outSqlrc, 10, { 'TYPE' => SQL_INTEGER })
       || die $sth->errstr;
    $sth->bind_param_inout(3, \$outMsg, 32, { 'TYPE' => SQL_CHAR })
      || die $sth->errstr;

    # execute call statement
    $sth->execute()
      || die $sth->errstr;
     
    if ($outSqlrc == 0)
    {
      $numCols = $sth->{NUM_OF_FIELDS};  
      print "Result set returned $numCols columns\n";
  
      # bind column 1 to variable 
      $sth->bind_col(1, \$salesPerson); 

      # bind column 2 to variable 
      $sth->bind_col(2, \$region); 

      # bind column 3 to variable 
      $sth->bind_col(3, \$sales); 
 
      print "Stored procedure returned successfully.\n";
      printf("\n---SALES_PERSON---REGION-----------SALES--\n");

      while (($salesPerson, $region, $sales) = $sth->fetchrow())
      {
        printf("  %-10s,    %-15s", $salesPerson, $region);
        if (defined $sales)
        {  
          printf(",  %-1d\n", $sales);
        }
        else
        {
          print ",  - \n";
        } 
      }
    }
    else
    {
      print "Stored procedure returned SQLCODE $outSqlrc";
      print "\nWith Error: $outMsg \n";
    }

    # no more data to be fetched from statement handle
    $sth->finish;
  
    $dbh->rollback();
    $rc = 0;
  };  
  
  return $rc;
} # CallGeneralWithNullsExample