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

//   Sample Program UDFcli - Calling UDFsrv 

//   Steps to run the sample: 
//   (1) create and populate the SAMPLE database (db2sampl) 
//   (2) (n)make UDFsrv 
//   (3) (n)make UDFcli   
//   (4) run UDFcli with the following command:  
//          java UDFcli   
//   (5) run UDFcli and drop the UDFs, UDT, and  
//       table with the following command: 
//          java UDFcli drop 

//   NOTES: (1) You must set the jdk11_path database manager configuration  
//              parameter 
//          (2) You must set the CLASSPATH and shared library path  
//              environment variables, 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 and using user-defined functions, refer to  
//  the "Object-Relational Programming" 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. 

//   Class UDFcli contains seven methods: 
//   (1) registerUDT: register a test UDT for the column UDF 
//   (2) registerUDFs 
//   (3) callScalarUDF 
//   (4) callColumnUDF 
//   (5) callTableUDF   
//   (6) dropAll 
//   (7) main: application body 

import java.sql.*;              //  JDBC classes 

class UDFcli  
{   static
    {   try
        {   Class.forName ("COM.ibm.db2.jdbc.app.DB2Driver").newInstance ();
        } 
        catch (Exception e)
        {   System.out.println ("\n  Error loading DB2 Driver...\n");
            System.out.println (e);
            System.exit(1);
        }
    }


    //  (1) Register the UDT for the column UDF 
    public static void 
    registerUDT (Connection con) throws Exception
    {   try
        {   Statement stmt = con.createStatement ();
            System.out.println ("\n  Registering Java UDT for the column UDF");
            stmt.executeUpdate (
                      " CREATE DISTINCT TYPE UDTforColumnUDF AS DECIMAL(9,2) " + 
                      " WITH COMPARISONS "); 
            stmt.close ();
        }
        catch (SQLException e)
        {   int sqlCode = e.getErrorCode();
            if (sqlCode == -601)
            { //  ignore "UDT already exists" error 
            }
            else System.out.println(e);
        }
    }

    //  (2) Register the UDFs 
    public static void 
    registerUDFs (Connection con) throws Exception
    {   try
        {   Statement registerStmt = con.createStatement ();

            //  scalar UDF 
            System.out.println ("  Registering Java UDF: scalarUDF");
            registerStmt.executeUpdate (
                      " CREATE FUNCTION scalarUDF ( VARCHAR(20) ) " + 
                      " RETURNS int " +
                      " EXTERNAL NAME 'UDFsrv!scalarUDF' " +
                      " LANGUAGE java " + 
                      " PARAMETER STYLE db2general " + 
                      " DETERMINISTIC " +
                      " FENCED " +
                      " NOT NULL CALL " +
                      " NO SQL " +
                      " NO EXTERNAL ACTION " +
                      " NO SCRATCHPAD " +
                      " NO FINAL CALL " +
                      " ALLOW PARALLEL " +
                      " NO DBINFO");

            //  column UDF 
            System.out.println ("  Registering Java UDF: columnUDF");
            registerStmt.executeUpdate (
                      " CREATE FUNCTION columnUDF ( UDTforColumnUDF ) " + 
                      " RETURNS UDTforColumnUDF " +
                      " SOURCE \"SYSIBM\".AVG( DECIMAL() ) ");

            //  table UDF 
            System.out.println ("  Registering Java UDF: tableUDF");
            registerStmt.executeUpdate (
                      " CREATE FUNCTION tableUDF ( VARCHAR(20) ) " + 
                      " RETURNS TABLE ( name VARCHAR(20), " + 
                      "                 job  VARCHAR(20)  ) " +
                      " EXTERNAL NAME 'UDFsrv!tableUDF' " +
                      " LANGUAGE java " + 
                      " PARAMETER STYLE db2general " + 
                      " NOT DETERMINISTIC " +
                      " FENCED " +
                      " NULL CALL " +
                      " NO SQL " +
                      " NO EXTERNAL ACTION " +
                      " NO SCRATCHPAD " +
                      " FINAL CALL " +
                      " DISALLOW PARALLEL " +
                      " NO DBINFO");


            //  table for column UDF 
            registerStmt.executeUpdate("CREATE TABLE TableForColumnUDF ( COL1 UDTforColumnUDF) ");

            registerStmt.close ();
        }
        catch (SQLException e)
        {   int sqlCode = e.getErrorCode();
            if ((sqlCode == -601) || (sqlCode == -454))
            { //  ignore "object already exists" error 
            }
            else System.out.println(e);
        }
    }

    //  (3) Call the scalar UDF 
    public static void 
    callScalarUDF (Connection con) throws Exception
    {   try
        {   System.out.println("\n  Calling a scalar UDF");
            Statement stmt = con.createStatement ();
            ResultSet rs = stmt.executeQuery(
                              " SELECT scalarUDF(name), name FROM STAFF " +
                              " WHERE  name LIKE 'D%'  ");
            while(rs.next())
            {   int nameLen = rs.getInt(1); 
                String name = rs.getString(2);
                System.out.println("    Output - the length of the name: " + nameLen +
                                   "    Input - the name: " + name);
            } 
            stmt.close ();
        }
        catch (Exception e)
        {   System.out.println (e);
        }
    }

    //  (4) Call the column UDF 
    public static void 
    callColumnUDF (Connection con) throws Exception
    {   try
        {   System.out.println("\n  Calling a column UDF");
            Statement stmt = con.createStatement ();
            stmt.executeUpdate(
                       "INSERT INTO  TableForColumnUDF " + 
                       "VALUES ( UDTforColumnUDF(CAST(  4.1 AS DECIMAL(9,2))) )," + 
                             " ( UDTforColumnUDF(CAST( 10.7 AS DECIMAL(9,2))) )," + 
                             " ( UDTforColumnUDF(CAST( 11.5 AS DECIMAL(9,2))) )," + 
                             " ( UDTforColumnUDF(CAST( 14.4 AS DECIMAL(9,2))) ) ");
            //  use the column UDF 
            ResultSet rs = stmt.executeQuery(" SELECT " + 
                                             " double(DECIMAL(columnUDF(COL1)))" +  
                                             " FROM TableForColumnUDF " );
            rs.next(); //  the result set contains one record 
            double udfResult = rs.getDouble(1);
            System.out.println("    The input for the column UDF is:");
            System.out.println("      the column COL1 of the table TableForColumnUDF" );
            System.out.println("    The output from the column UDF is:");
            System.out.println("      the average for COL1 - " + udfResult );
            rs.close();
            stmt.close ();
        }
        catch (Exception e)
        {   System.out.println (e);
        }
    }

    //  (5) Call the table UDF 
    public static void 
    callTableUDF (Connection con) throws Exception
    {   try
        {   System.out.println("\n  Calling a table UDF");
            Statement stmt = con.createStatement();
            ResultSet rs = stmt.executeQuery(
                              " SELECT name, job  " +
                              " FROM TABLE(tableUDF('Hello')) AS smallStaff ");

            int rowNb = 1;
            System.out.println("    The input for the table UDF is:");
            System.out.println("      Hello" );
            System.out.println("    The output from the table UDF is:");
            while(rs.next())
            {   String name = rs.getString(1);
                String job = rs.getString(2);
                System.out.println("      Row number " + rowNb);
                System.out.println("         Name: " + name);
                System.out.println("          Job: " + job);
                rowNb++;
            } 
            rs.close();
            stmt.close ();
        }
        catch (Exception e)
        {   System.out.println (e);
        }
    }

    //  (6) Drop the UDFs, UDT, and table 
    public static void 
    dropAll (Connection con) throws Exception
    {   try
        {   Statement dropStmt = con.createStatement ();
            try 
            {   dropStmt.executeUpdate ("DROP TABLE TableForColumnUDF");
                dropStmt.executeUpdate ("DROP FUNCTION scalarUDF");
                dropStmt.executeUpdate ("DROP FUNCTION columnUDF");
                dropStmt.executeUpdate ("DROP FUNCTION tableUDF");
                dropStmt.executeUpdate ("DROP DISTINCT TYPE UDTforColumnUDF");
            }
            catch (SQLException e)
            {   int sqlCode = e.getErrorCode();
                if (sqlCode == -204)
                { //  ignore "Object does not exist" error 
                }
                else System.out.println(e);
            }

            dropStmt.close ();
        }
        catch (Exception e)
        {   System.out.println (e);
        }
    }

    //  (7) main application: .connect to the database 
    //                        .register and call the UDFs 
    //                        .commit the transaction 
    public static void main (String argv[])
    {   try 
        {   System.out.println ("  Java User Defined Functions Sample");
            //  Connect to Sample database 

            boolean drop = false;
            Connection con = null;
            //  URL is jdbc:db2:dbname 
            String url = "jdbc:db2:sample";

            if (argv.length == 0) 
            {   //  connect with default id/password 
                con = DriverManager.getConnection(url);
            }
            else if (argv.length == 1 && argv[0].equalsIgnoreCase("drop"))
            {   //  connect with default id/password 
                con = DriverManager.getConnection(url);
                //  call dropAll() to clean up 
                drop = true;
            }
            else if (argv.length == 2)
            {   String userid = argv[0];
                String passwd = argv[1];

                //  connect with user-provided username and password 
                con = DriverManager.getConnection(url, userid, passwd);
            }
            else if (argv.length == 3 && argv[0].equalsIgnoreCase("drop"))
            {   
                //  connect with user-provided username and password 
                String userid = argv[1];
                String passwd = argv[2];
                con = DriverManager.getConnection(url, userid, passwd);

                //  call dropAll() to clean up 
                drop = true;
            }
            else 
            {   throw new Exception("\nUsage: java UDFcli [drop] [username password]\n");
            } 
    
            //  Enable transactions 
            con.setAutoCommit(false);

            //  Drop, register and call UDFs 
            try
            {   dropAll(con); //  ensure the UDFs, UDT and table do not exist 
                registerUDT(con); //  for the column UDF 
                registerUDFs(con);
                callScalarUDF(con);
                callColumnUDF(con);
                callTableUDF(con);
                if (drop)
                {   //  drop UDFs, UDT, and table when program finishes 
                    dropAll(con);
                }
            }  
            catch( Exception e )
            {   throw e; 
            } 
            finally
            {   //  Commit the transaction 
                System.out.println("\nCommit the transaction...");
                con.commit();
                System.out.println("Committed.");
            }

        }
        catch (Exception e)
        {   System.out.println (e);
        }
    }
}