//   Source File Name: Spclient.java % 
//  
//   Licensed Materials -- Property of IBM 
//  
//   (c) Copyright International Business Machines Corporation, 1998, 2000. 
//       All Rights Reserved. 
//  
//   US Government Users Restricted Rights - 
//   Use, duplication or disclosure restricted by 
//   GSA ADP Schedule Contract with IBM Corp. 

//   Sample Program Spclient - Client Application for Stored Procedure 

//   To run this sample, perform the following steps: 
//   (1) create and populate the SAMPLE database (db2sampl) 
//   (2) compile Spserver (javac Spserver.java) 
//   (3) copy Spserver.class to the sqllib/function directory 
//   (4) register the stored procedures using the Spcreate.db2 CLP script: 
//       (i) db2 connect to sample 
//      (ii) db2 -td@ -vf Spcreate.db2 
//   (3) compile Spclient (javac Spclient.java) 
//   (4) run Spclient (java Spclient) 

//   NOTES: (1) The jdk11_path database manager configuration parameter must 
//              be set on the server 
//          (2) The CLASSPATH and shared library path environment variables 
//              must be set, as for any JDBC application 
//          (3) Visit http://www.software.ibm.com/data/db2/java 
//              for current DB2 Java information 

//  For more information about this sample, refer to the README file. 

//  For more information on Programming in Java, refer to the  
//  "Programming in Java" section of the Application Development Guide. 

//  For more information on creating stored procedures, refer to the  
//  "Writing Stored Procedures" section of the Application Development Guide. 

//  For more information on building and running Java programs for DB2, 
//  refer to the "Building Java Applets and Applications" section of the  
//  Application Building Guide. 

//  For more information on the SQL language, refer to the SQL Reference. 

//   Spclient calls twelve methods that call stored procedures: 
//   (1) callOutLanguage: returns language of the stored procedure library 
//         Parameter types used: OUT CHAR(8) 
//   (2) callOutParameter: return median salary of EMPLOYEE table 
//         Parameter types used: OUT DOUBLE 
//                               OUT INTEGER 
//                               OUT CHAR(32) 
//   (3) callInParameters: accept 3 values and update salaries in EMPLOYEE table 
//       accordingly 
//         Parameter types used: IN DOUBLE 
//                               IN CHAR(3) 
//                               OUT INTEGER 
//                               OUT CHAR(32) 
//   (4) callIncallOutParameter: accept an input value and return the median 
//       salary of the EMPLOYEE table for employees who make more 
//       than the input value. Demonstrates how to use null indicators 
//       in a client application. 
//         Parameter style (C):    GENERAL WITH NULLS 
//         Parameter style (Java): JAVA 
//         Parameter style (SQL):  SQL 
//         Parameter types used: INOUT DOUBLE 
//                               OUT INTEGER 
//                               OUT CHAR(32) 
//   (5) callOneResultSet: return a result set to the client application 
//         Parameter types used: IN DOUBLE 
//                               OUT INTEGER 
//   (6) callTwoResultSets: return two result sets to the client application 
//         Parameter types used: IN DOUBLE 
//                               OUT INTEGER 
//   (7) callClobExtract: return a sub-section of a CLOB data type 
//         Parameter types used: IN CHAR(6) 
//                               OUT VARCHAR(1000) 
//                               OUT INTEGER 
//   (8) callDecimalType: pass and receive a DECIMAL data type from a 
//       stored procedure 
//         Parameter types used: INOUT DECIMAL 
//   (9) callAllDataTypes: use all of the common data types in a stored procedure 
//         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 
//   (10) callDB2SQL: calls a DB2SQL parameter style stored procedure 
//         Parameter style: DB2SQL 
//         Parameter types used: IN CHAR(8) 
//                               OUT DOUBLE 
//   (11) 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) 
//                               OUT INTEGER 
//   (12) callProgramTypeMain: calls a stored procedure implemented with 
//       PROGRAM TYPE MAIN parameter passing style 
//         Parameter types used: IN CHAR(8) 
//                               OUT DOUBLE 

import java.sql.*;              //  JDBC classes                           /* :rk.1:erk. */  
import java.math.BigDecimal;    //  BigDecimal support for packed decimal type 

class Spclient
{
  static double outMedian = 0;

  static
  {
    try
    {
      System.out.println();
      System.out.println("Java Stored Procedure Sample");
      Class.forName("COM.ibm.db2.jdbc.app.DB2Driver").newInstance();
    }
    catch (Exception e)
    {
      System.out.println("\nError loading DB2 Driver...\n");
      e.printStackTrace();
    }
  }

  public static void main(String argv[])
  {
    Connection con = null;
    //  URL is jdbc:db2:dbname 
    String url = "jdbc:db2:sample";
    String language = "";

    try
    {
      if (argv.length == 0) {
        //  connect to sample database 
        //  connect with default id/password 
        con = DriverManager.getConnection(url);                           /* :rk.2:erk. */
      }
      else if (argv.length == 2) {
        //  connect to sample database 
        //  connect with user-provided username and password 
        String userid = argv[0];
        String passwd = argv[1];

        con = DriverManager.getConnection(url, userid, passwd);
      }
      else if (argv.length == 3) {
        //  connect to user-provided database 
        url = "jdbc:db2:" + argv[2];

        String userid = argv[0];
        String passwd = argv[1];

        //  connect with user-provided username and password 
        con = DriverManager.getConnection(url, userid, passwd);
      }

      else {
        System.out.println("\nUsage: java Spclient "
          + "[username password [database]]\n");
        System.exit(0);
      }
      //  turn off autocommit  
      con.setAutoCommit(false);                                           /* :rk.3:erk. */

      language = callOutLanguage(con);
      callOutParameter(con);
      callInParameters(con);

      callInoutParameter(con, outMedian);
      callInoutParameter(con, 99999.99);
      callOneResultSet(con);
      callTwoResultSets(con);

      //  SQL procedures cannot handle LOB data types 
      if (language.trim().equals("SQL"))
      {
        System.out.println("\nStored procedures are implemented in SQL,\n" +
               "which cannot handle LOB datatypes.\n" +
               "Skipping the call to CLOB_EXTRACT.\n");
      }
      else if (language.trim().equals("C"))
      {
        System.out.println(
            "\n\n   If the CLOB EXTRACT stored procedure is implemented\n" +
            "   using CLI, you must increase the value of the UDF_MEM_SZ\n" +
            "   database manager configuration variable to at least two\n" +
            "   pages larger than the size of the input arguments and\n" +
            "   the result of the stored procedure. To do this, issue\n" +
            "   the following command from the CLP:\n" +
            "      db2 UPDATE DBM CFG USING UDF_MEM_SZ 2048\n" +
            "   For the change to take effect, you must then stop and\n" +
            "   restart the DB2 server by issuing the following\n" +
            "   commands from the CLP:\n" +
            "      db2stop\n" +
            "      db2start");

        callClobExtract("000140", con);
      }
      else if (language.trim().equals("JAVA"))
      {
        callClobExtract("000140", con);
      }

      //  Java and SQL procedures can handle DECIMAL data types 
      if (language.trim().equals("SQL") || language.trim().equals("JAVA"))
      {
        callDecimalType(con);
      }
      else if (language.trim().equals("C"))
      {
        //  C does not provide a native data type for decimal 
        //  values, so neither a C client application nor a 
        //  C stored procedure can pass DECIMAL SQL data types. 
        //  
        //  Do not call the DECIMAL_TYPE stored procedure, 
        //  because the stored procedure is written in C. 
      }

      callAllDataTypes(con);

      //  the following stored procedures can only be implemented in C/C++ 
      if (language.trim().equals("C"))
      {
        callDB2SQL("CLERK", con);
        callDBINFO("MANAGER", con);
        callProgramTypeMain("DESIGNER", con);
      }

      //  roll back any changes to the database 
      con.rollback();                                                     /* :rk.8:erk. */
      con.close();
    }
    catch (Exception e)
    {
      try
      { 
        con.rollback();
        con.close();
      } 
      catch (Exception x) 
      { } 
      e.printStackTrace ();
    }
  } //  end main 

  public static String callOutLanguage(Connection con)
  {
    String outLang = "";
    try {
      //  prepare the CALL statement for OUT_LANGUAGE 
      String procName = "OUT_LANGUAGE";
      String sql = "CALL " + procName + "(?)";
      CallableStatement callStmt = con.prepareCall(sql);

      //  register the output parameter 
      callStmt.registerOutParameter (1, Types.CHAR);

      //  call the stored procedure 
      System.out.println ("\nCall stored procedure named " + procName);
      callStmt.execute();

      //  retrieve output parameters 
      outLang = callStmt.getString(1);
      System.out.println ("Stored procedures are implemented in language "
        + outLang);
    }
    catch (SQLException e)
    {
       System.out.println(e);
    }
    return(outLang);
  }

  public static void callOutParameter(Connection con) 
  {
    try
    {
      //  prepare the CALL statement for OUT_PARAM 
      String procName = "OUT_PARAM";
      String sql = "CALL " + procName + "(?, ?, ?)";
      CallableStatement callStmt = con.prepareCall(sql);

      //  register the output parameter                                      /* :rk.4:erk. */ 
      callStmt.registerOutParameter (1, Types.DOUBLE);
      callStmt.registerOutParameter (2, Types.INTEGER);
      callStmt.registerOutParameter (3, Types.CHAR);

      //  call the stored procedure                                          /* :rk.5:erk. */ 
      System.out.println ("\nCall stored procedure named " + procName);
      callStmt.execute();

      //  retrieve output parameters                                         /* :rk.6:erk. */ 
      outMedian = callStmt.getDouble(1);
      int outErrorCode = callStmt.getInt(2);
      String outErrorLabel = callStmt.getString(3);

      if (outErrorCode == 0)                                                /* :rk.7:erk. */
      {
        System.out.println(procName + " completed successfully");
        System.out.println ("Median salary returned from " + procName + " = "
          + outMedian);
      }
      else  
      { //  stored procedure failed 
        System.out.println(procName + " failed with SQLCODE " 
          + outErrorCode);
        System.out.println(procName + " failed at " + outErrorLabel);
      }
    }
    catch (SQLException e)
    {
       System.out.println(e);
    }
  }


  public static void callInParameters(Connection con) throws SQLException
  {
    try
    {
      //  prepare the CALL statement for IN_PARAMS 
      String procName = "IN_PARAMS";
      String sql = "CALL " + procName + "(?, ?, ?, ?, ?, ?)";
      CallableStatement callStmt = con.prepareCall(sql);

      //  Display total salary before calling IN_PARAMS 
      String query = "SELECT SUM(salary) FROM employee WHERE workdept = ?";
      PreparedStatement queryStmt = con.prepareStatement(query);
      queryStmt.setString(1, "E11");
      ResultSet queryRS = queryStmt.executeQuery();
      queryRS.next();
      double sumSalary = queryRS.getDouble(1);
      queryRS.close();
      System.out.println("\nSum of salaries for dept. E11 = "
          + sumSalary + " before " + procName);

      //  register the output parameters 
      callStmt.registerOutParameter (5, Types.INTEGER);
      callStmt.registerOutParameter (6, Types.CHAR);

      //  set input parameters 
      callStmt.setDouble (1, 15000);
      callStmt.setDouble (2, 20000);
      callStmt.setDouble (3, 25000);
      callStmt.setString (4, "E11");

      //  initialize output parameters 
      callStmt.setInt (5, 0);
      callStmt.setString (6, "");

      //  call the stored procedure 
      System.out.println ("\nCall stored procedure named " + procName);
      callStmt.execute();

      //  retrieve output parameters 
      int outErrorCode = callStmt.getInt(5);
      String outErrorLabel = callStmt.getString(6);

      if (outErrorCode == 0) {
        System.out.println(procName + " completed successfully");

        //  Display total salary after calling IN_PARAMS 
        queryRS = queryStmt.executeQuery();
        queryRS.next();
        sumSalary = queryRS.getDouble(1);
        queryRS.close();
        System.out.println("Sum of salaries for dept. E11 = " 
            + sumSalary + " after " + procName);
        queryStmt.close();
      }
      else  { //  stored procedure failed 
        System.out.println(procName + " failed with SQLCODE " + outErrorCode);
        System.out.println(procName + " failed at " + outErrorLabel.trim());
      }
    }
    catch (SQLException e)
    {
        /* Roll back any UPDATE statements issued before the SQLException */
        con.rollback();
        System.out.println(e);
    }
  }

  public static void callInoutParameter(Connection con, double median) 
  {
    try
    {
      //  prepare the CALL statement for INOUT_PARAM 
      String procName = "INOUT_PARAM";
      String sql = "CALL " + procName + "(?, ?, ?)";
      CallableStatement callStmt = con.prepareCall(sql);

      //  set input parameter to median value passed back by OUT_PARAM 
      callStmt.setDouble (1, median);

      //  register the output parameters 
      callStmt.registerOutParameter (1, Types.DOUBLE);
      callStmt.registerOutParameter (2, Types.INTEGER);
      callStmt.registerOutParameter (3, Types.CHAR);

      //  call the stored procedure 
      System.out.println ("\nCall stored procedure named " + procName);

      if (median == 99999.99)
      {
        System.out.println("with an input value that causes a NOT FOUND error");
      }
      callStmt.execute();

      //  retrieve output parameters 
      double inoutMedian = callStmt.getDouble(1);
      int outErrorCode = callStmt.getInt(2);
      String outErrorLabel = callStmt.getString(3);

      if (outErrorCode == 0) {
        System.out.println(procName + " completed successfully");
        System.out.println("Median salary returned from " + procName + " = "
          + inoutMedian);
      }
      else  { //  stored procedure failed 
        System.out.println(procName + " failed with SQLCODE " + outErrorCode);
        System.out.println(procName + " failed at " + outErrorLabel.trim());
      }
    }
    catch (SQLException e)
    {
       System.out.println(e);
    }
  }

  public static void callOneResultSet(Connection con) 
  {
    try
    {
      //  prepare the CALL statement for ONE_RESULT_SET 
      String procName = "ONE_RESULT_SET";
      String sql = "CALL " + procName + "(?, ?)";
      CallableStatement callStmt = con.prepareCall(sql);

      //  set input parameter to median value passed back by OUT_PARAM 
      callStmt.setDouble (1, outMedian);

      //  register the output parameter 
      callStmt.registerOutParameter (2, Types.INTEGER);

      //  call the stored procedure 
      System.out.println ("\nCall stored procedure named " + procName);
      callStmt.execute();

      //  retrieve output parameter 
      int outErrorCode = callStmt.getInt(2);

      if (outErrorCode == 0) {
        System.out.println(procName + " completed successfully");
        ResultSet rs = callStmt.getResultSet();
        while (rs.next()) {
          fetchAll(rs);
        }

        //  close ResultSet 
        rs.close();
      }
      else  { //  stored procedure failed 
        System.out.println(procName + " failed with SQLCODE " + outErrorCode);
      }
    }
    catch (SQLException e)
    {
       System.out.println(e);
    }
  }

  public static void callTwoResultSets(Connection con) 
  {
    try
    {
      //  prepare the CALL statement for TWO_RESULT_SETS 
      String procName = "TWO_RESULT_SETS";
      String sql = "CALL " + procName + "(?, ?)";
      CallableStatement callStmt = con.prepareCall(sql);

      //  set input parameter to median value passed back by OUT_PARAM 
      callStmt.setDouble (1, outMedian);

      //  register the output parameter 
      callStmt.registerOutParameter (2, Types.INTEGER);

      //  call the stored procedure 
      System.out.println ("\nCall stored procedure named " + procName);
      callStmt.execute();

      //  retrieve output parameter 
      int outErrorCode = callStmt.getInt(2);

      if (outErrorCode == 0) {
        System.out.println(procName + " completed successfully");

        System.out.println("Result set 1: Employees who make more than " + outMedian);
        //  get first result set 
        ResultSet rs = callStmt.getResultSet();
        while (rs.next()) {
          fetchAll(rs);
        }

        System.out.println("\nResult set 2: Employees who make less than " + outMedian);
        //  get second result set 
        callStmt.getMoreResults();
        rs = callStmt.getResultSet();
        while (rs.next()) {
          fetchAll(rs);
        }

        //  close ResultSet 
        rs.close();
      }
      else  { //  stored procedure failed 
        System.out.println(procName + " failed with SQLCODE " + outErrorCode);
      }
    }
    catch (SQLException e)
    {
       System.out.println(e);
    }
  }

  public static void callClobExtract(String empNo, Connection con) 
  {
    String outInterests;
    try
    {
      //  prepare the CALL statement for CLOB_EXTRACT 
      String procName = "CLOB_EXTRACT";
      String sql = "CALL " + procName + "(?, ?, ?)";
      CallableStatement callStmt = con.prepareCall(sql);

      //  set input parameter to median value passed back by OUT_PARAM 
      callStmt.setString (1, empNo);

      //  register the output parameters 
      callStmt.registerOutParameter (2, Types.VARCHAR);
      callStmt.registerOutParameter (3, Types.INTEGER);

      //  call the stored procedure 
      System.out.println ("\nCall stored procedure named " + procName);
      callStmt.execute();

      //  retrieve output parameters 
      outInterests = callStmt.getString(2);
      int outErrorCode = callStmt.getInt(3);

      if (outErrorCode == 0)
      {
        System.out.println(procName + " completed successfully");
        System.out.println ("Resume section returned for employee "
        + empNo + "=\n" + outInterests);
      }
      else  
      { //  stored procedure failed 
        System.out.println(procName + " failed with SQLCODE "
           + outErrorCode);
        System.out.println("from procedure section labelled " 
           + outInterests);
      }
    }
    catch (SQLException e)
    {
       System.out.println(e);
    }
  }

  public static void callDecimalType(Connection con) 
  {
    try
    {
      //  prepare the CALL statement for ALL_DATA_TYPES 
      String procName = "DECIMAL_TYPE";
      String sql = "CALL " + procName + "(?, ?, ?)";
      CallableStatement callStmt = con.prepareCall(sql);

      //  declare and initialize input variable 
      BigDecimal inoutDecimal = new BigDecimal("400000.00");

      //  set input parameter 
      callStmt.setBigDecimal (1, inoutDecimal);

      //  register the output parameters 
      callStmt.registerOutParameter (1, Types.DECIMAL, 2);
      callStmt.registerOutParameter (2, Types.INTEGER);
      callStmt.registerOutParameter (3, Types.CHAR);

      //  call the stored procedure 
      System.out.println ("\nCall stored procedure named " + procName);
      callStmt.execute();

      //  retrieve SQLCODE output parameter 
      int outErrorCode = callStmt.getInt(2);

      if (outErrorCode == 0) {
        System.out.println(procName + " completed successfully");

        //  retrieve output parameters 
        inoutDecimal = callStmt.getBigDecimal(1,2);
        System.out.println("Value of DECIMAL = " + inoutDecimal);
      }
      else  { //  stored procedure failed 
        String outErrorLabel = callStmt.getString(3);
        System.out.println(procName + " failed with SQLCODE " + outErrorCode);
        System.out.println(" from procedure section labelled " + outErrorLabel);
      }
      callStmt.close ();
    }
    catch (SQLException e)
    {
      System.out.println(e);
    }

  }

  public static void callAllDataTypes(Connection con) 
  {
    try
    {
      //  prepare the CALL statement for ALL_DATA_TYPES 
      String procName = "ALL_DATA_TYPES";
      String sql = "CALL " + procName + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
      CallableStatement callStmt = con.prepareCall(sql);

      //  declare and initialize input variables 
      short inoutSmallint = 32000;
      int inoutInteger = 2147483000;
      long inoutBigint = 2147483000;
      float inoutReal = 100000;
      double inoutDouble = 2500000;

      //  declare output variables 
      String outChar, outChars, outVarchar;
      Date outDate;
      Time outTime;

      //  set input parameters 
      callStmt.setShort (1, inoutSmallint);
      callStmt.setInt (2, inoutInteger);
      callStmt.setLong (3, inoutBigint);
      callStmt.setFloat (4, inoutReal);
      callStmt.setDouble (5, inoutDouble);

      //  register the output parameters 
      callStmt.registerOutParameter (1, Types.SMALLINT);
      callStmt.registerOutParameter (2, Types.INTEGER);
      callStmt.registerOutParameter (3, Types.BIGINT);
      callStmt.registerOutParameter (4, Types.REAL);
      callStmt.registerOutParameter (5, Types.DOUBLE);
      callStmt.registerOutParameter (6, Types.CHAR);
      callStmt.registerOutParameter (7, Types.CHAR);
      callStmt.registerOutParameter (8, Types.VARCHAR);
      callStmt.registerOutParameter (9, Types.DATE);
      callStmt.registerOutParameter (10, Types.TIME);
      callStmt.registerOutParameter (11, Types.INTEGER);
      callStmt.registerOutParameter (12, Types.CHAR);

      //  call the stored procedure 
      System.out.println ("\nCall stored procedure named " + procName);
      callStmt.execute();

      //  retrieve SQLCODE output parameter 
      int outErrorCode = callStmt.getInt(11);

      if (outErrorCode == 0) {
        System.out.println(procName + " completed successfully");

        //  retrieve output parameters 
        inoutSmallint = callStmt.getShort(1);
        inoutInteger = callStmt.getInt(2);
        inoutBigint = callStmt.getLong(3);
        inoutReal = callStmt.getFloat(4);
        inoutDouble = callStmt.getDouble(5);
        outChar = callStmt.getString(6);
        outChars = callStmt.getString(7);
        outVarchar = callStmt.getString(8);
        outDate = callStmt.getDate(9);
        outTime = callStmt.getTime(10);

        System.out.println("Value of SMALLINT = " + inoutSmallint);
        System.out.println("Value of INTEGER = " + inoutInteger);
        System.out.println("Value of BIGINT = " + inoutBigint);
        System.out.println("Value of REAL = " + inoutReal);
        System.out.println("Value of DOUBLE = " + inoutDouble);
        System.out.println("Value of CHAR(1) = " + outChar);
        System.out.println("Value of CHAR(15) = " + outChars.trim());
        System.out.println("Value of VARCHAR(12) = " + outVarchar.trim());
        System.out.println("Value of DATE = " + outDate);
        System.out.println("Value of TIME = " + outTime);
      }
      else  { //  stored procedure failed 
        String outErrorLabel = callStmt.getString(12);
        System.out.println(procName + " failed with SQLCODE " + outErrorCode);
        System.out.println(" from procedure section labelled " + outErrorLabel);
      }
      callStmt.close ();
    }
    catch (SQLException e)
    {
      System.out.println(e);
    }

  }

  public static void callDB2SQL(String job, Connection con)
  {
    try
    {
      //  prepare the CALL statement for DB2SQL_EXAMPLE 
      String procName = "DB2SQL_EXAMPLE";
      String sql = "CALL " + procName + "(?, ?)";
      CallableStatement callStmt = con.prepareCall(sql);

      //  set the input parameter 
      callStmt.setString (1, job);

      //  register the output parameter 
      callStmt.registerOutParameter (2, Types.DOUBLE);

      //  call the stored procedure 
      System.out.println ("\nCall stored procedure named " + procName);
      callStmt.execute();

      //  retrieve output parameters 
      outMedian = callStmt.getDouble(2);

      System.out.println(procName + " completed successfully");
      System.out.println ("Median salary returned from " + procName + " = "
        + outMedian);
    }
    catch (SQLException e)
    {
       //  Note that the JDBC specification only allows us to 
       //  get the SQLSTATE as a method called on an instance of 
       //  an SQLException. If you call DB2SQL_EXAMPLE with  
       //  null input, so that the stored procedure returns the 
       //  custom SQLSTATE ('38100') and message, the Java client 
       //  considers it a successful call because the returned SQLCODE = 0  
       //  and an SQLException is not raised. 

       System.out.println(e);
    }
  }

  public static void callDBINFO(String job, Connection con)
  {
    try
    {
      String databaseName, databaseVersion;
      
      //  prepare the CALL statement for DBINFO_EXAMPLE 
      String procName = "DBINFO_EXAMPLE";
      String sql = "CALL " + procName + "(?, ?, ?, ?, ?)";
      CallableStatement callStmt = con.prepareCall(sql);

      //  set the input parameter 
      callStmt.setString (1, job);

      //  register the output parameters 
      callStmt.registerOutParameter (2, Types.DOUBLE);
      callStmt.registerOutParameter (3, Types.CHAR);
      callStmt.registerOutParameter (4, Types.CHAR);
      callStmt.registerOutParameter (5, Types.INTEGER);

      //  call the stored procedure 
      System.out.println ("\nCall stored procedure named " + procName);
      callStmt.execute();

      //  retrieve output parameters 
      outMedian = callStmt.getDouble(2);
      databaseName = callStmt.getString(3);
      databaseVersion = callStmt.getString(4);
      int outErrorCode = callStmt.getInt(5);

      if (outErrorCode == 0) {
        System.out.println(procName + " completed successfully");
        System.out.println ("Average salary for job " + job + " = "
          + outMedian);
        System.out.println("Database name from DBINFO structure = "
          + databaseName.trim());
        System.out.println("Database version from DBINFO structure = "
          + databaseVersion.trim());
      }
      else  { //  stored procedure failed 
        System.out.println(procName + " failed with SQLCODE " 
          + outErrorCode);
      }
    }
    catch (SQLException e)
    {
       System.out.println(e);
    }
  }

  public static void callProgramTypeMain(String job, Connection con)
  {
    try
    {
      //  prepare the CALL statement for MAIN_EXAMPLE 
      String procName = "MAIN_EXAMPLE";
      String sql = "CALL " + procName + "(?, ?, ?)";
      CallableStatement callStmt = con.prepareCall(sql);

      //  set the input parameter 
      callStmt.setString (1, job);

      //  register the output parameter 
      callStmt.registerOutParameter (2, Types.DOUBLE);
      callStmt.registerOutParameter (3, Types.INTEGER);

      //  call the stored procedure 
      System.out.println ("\nCall stored procedure named " + procName);
      callStmt.execute();

      //  retrieve output parameters 
      outMedian = callStmt.getDouble(2);
      int outErrorCode = callStmt.getInt(3);

      if (outErrorCode == 0) {
        System.out.println(procName + " completed successfully");
        System.out.println ("Average salary for job " + job + " = "
          + outMedian);
      }
      else  { //  stored procedure failed 
        System.out.println(procName + " failed with SQLCODE " 
          + outErrorCode);
      }
    }
    catch (SQLException e)
    {
       System.out.println(e);
    }
  }

  //  ====================================================== 
  //  Method: fetchAll -- returns all rows from a result set 
  //  ====================================================== 
  public static void fetchAll( ResultSet rs )
  {   try
    {   System.out.println("=================================================================");
      ResultSetMetaData stmtInfo = rs.getMetaData();
      int numOfColumns = stmtInfo.getColumnCount();
      int r = 0;

      while( rs.next() )
      {   r++;
        System.out.print("Row: " + r + ": ");
        for( int i=1; i <= numOfColumns; i++ )
        {   System.out.print(rs.getString(i));
          if( i != numOfColumns ) System.out.print(" , ");
        }
        System.out.println("");
      }
    }
    catch (SQLException e)
    {   System.out.println("Error: fetchALL: exception");
      System.out.println (e);
    }
  }

}