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

//   Sample program - Client Side of Java User-defined Functions Sample 

//  Run these samples by the following steps: 
//  (1) you must have the "sample" database catalogued on the client 
//  (2) compile this Java file (javac DB2UdCli.java) 
//  (3) compile the UDF server program (javac DB2Udf.java) and copy 
//      it to the sqllib/function directory 
//  (4) run the sample (java DB2UdCli [username password]) 

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

//  Class DB2UdCli implements the sample client (configuration and 
//  execution using JDBC).  Class DB2Udf implements the UDF 
//  method bodies. 

//  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. 

import COM.ibm.db2.jdbc.app.*;
import java.sql.*;
import java.io.*;


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

    try {
      System.out.println ("Java User-defined Function Sample");

      //  Load DB2 JDBC application driver 
      Class.forName ("COM.ibm.db2.jdbc.app.DB2Driver").newInstance ();

      //  Connect to database 
      if (argv.length == 0) {
         //  connect with default id/password 
         con = DriverManager.getConnection(url);
      }
      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 {
         System.out.println("\nUsage: java DB2UdCli [username password]\n");
         System.exit(0);
      }

      System.out.println ("Connected to the database");

      //  Execute DECLARE FUNCTION calls to register Java UDFs 
      System.out.println ("Declaring the Java UDFs");
      declareUDFs (con, false);	//  use NOT FENCED mode 

      //  Prepare table for UDF runs 
      System.out.println ("Preparing test tables for Java UDFs");
      prepareTable (con);

      //  Call UDFs 
      System.out.println ("Calling UDFs in UNFENCED mode");
      callUDFs (con);

      //  Clean-up table 
      System.out.println ("Cleaning up test tables");
      deleteTable (con);
    }
    catch (Throwable x)
      {
        try { con.close (); } catch (Throwable y) {}
        System.err.println ("Aborted due to exception.");
        x.printStackTrace ();
      }
  }


  //  Run DECLARE FUNCTION to register JAVA UDFs 
  static void
  declareUDFs (Connection con,
               boolean fenced) throws Exception
  {
    declareUDF (con, "MATH.\"/\"", "int, int", "int",
		fenced, false, "DB2Udf!divid");

    declareUDF (con, "fold", "clob(100k), int", "clob(100k)",
		fenced, false, "DB2Udf!fold");

    declareUDF (con, "findv", "varchar(500)", "int",
		fenced, false, "DB2Udf!findvwl");

    declareUDF (con, "counter", "", "int",
		fenced, true, "DB2Udf!ctr");

    declareUDF (con, "get_blob", "varchar(200)", "blob(1K)", 
                fenced, false, "DB2Udf!getBlob");

    declareUDF (con, "listDirectory", "varchar(250)",
		"table(f_name varchar(250), f_type varchar(20)," +
                "f_link varchar(250), f_len int)",
		fenced, true, "DB2Udf!listDirectory");
  }


  //  Build and run just one CREATE FUNCTION statement. 
  static void
  declareUDF (Connection con,
              String name,
              String argumentTypes,
              String returnType,
              boolean fenced,
              boolean scratchpad,
              String externalName) throws Exception
  {
    Statement s = con.createStatement ();
    String sqlClean = "drop function " + name;
    try { s.executeUpdate (sqlClean); } catch (SQLException x) { }
    String sql = "create function " + name +
      " (" + argumentTypes + ") " +
      " returns " + returnType +
      (fenced ? " fenced" : " not fenced") +
      (scratchpad ? " scratchpad" : "") +
      " variant no sql no external action " +
      " language java parameter style db2general " +
      " final call disallow parallel dbinfo " +
      " external name '" + externalName + "'";
    s.executeUpdate (sql);
    System.out.println ("Registered Java UDF " + name);
    s.close ();
  }


  //  Create sample tables for UDF runs - parallel C UDF examples 
  static void
  prepareTable (Connection con) throws Exception
  {
    Statement s = con.createStatement ();
    String sqlClean = "drop table udftest";
    String sqlClean1 = "drop table datalink_udftest";
    String sqlUrlIns1 = "insert into datalink_udftest values (16,dlvalue('";
    String sqlUrlIns2 = "'))";
    String sqlUrlIns;
    byte[] dlUrl = new byte[400];
    String dlUrlString;

    try 
    { 
      s.executeUpdate (sqlClean); 
      s.executeUpdate (sqlClean1);
    } catch (SQLException x) { }
    s.executeUpdate ("create table udftest(int1 integer,int2 integer," +
		     "part char(5),descr clob(33K))");
    //  Create table for the DATALINK-UDF sample. "no link control" 
    //  has been used just for simplicity. Powerful UDFs can be 
    //  written with "file link control" along similiar lines. 
    s.executeUpdate ("create table datalink_udftest(int1 integer," +
		     "file_reference datalink linktype url no link control)");
    s.executeUpdate ("insert into udftest values (16,1,'brain'," +
		     "'The only part of the body capable of forgetting.')");
    s.executeUpdate ("insert into udftest values (8,2,'heart'," +
		     "'The seat of the emotions?')");
    s.executeUpdate ("insert into udftest values (4,4,'elbow'," +
		     "'That bendy place in mid-arm')");
    s.executeUpdate ("insert into udftest values (2,0,NULL,NULL)");
    s.executeUpdate ("insert into udftest values (97,16,'xxxxx','Unknown.')");

    System.out.print("\nTo call the DATALINK-UDF, type a URL\n" +
                     "(for example, http://www.ibm.com) at the following prompt.\n" +
                     "The URL must be accessible from the database server.\n" +
                     "To skip the DATALINK-UDF sample, just press ENTER.\n" +
                     "URL>");
 
    //  Get from user the URL whose contents are to be materialized into BLOB 
    //  When user just presses ENTER, the string is zero-length. 
    //  The result BLOB from the UDF in this case will be NULL. 
    //  This can be used to skip the DATALINK-UDF sample. 
    System.in.read(dlUrl);

    //  Construct a string from user input, trimming whitespaces at 
    //  both ends 
    dlUrlString = (new String(dlUrl)).trim();

    //  Use the input value to insert into a DATALINK column 
    sqlUrlIns = sqlUrlIns1 + dlUrlString + sqlUrlIns2;
    s.executeUpdate (sqlUrlIns);
    System.out.println ("Inserted DATALINK value into sample" +
                        " table datalink_udftest!");
    s.close ();
    System.out.println ("Created UDF test tables.");
  }


  //  Call the UDFs - parallel C UDF examples 
  static void
  callUDFs (Connection con) throws Exception
  {
    runQuery (con, "select substr(descr,1,30)," +
	      "substr(fold(descr,6),1,30) from udftest");
    System.out.println ();

    //  exception expected for this run 
    try
    {
      runQuery (con, "select part, findv(part) from udftest");
    }
    catch (SQLException x)
    {
      if (x.getSQLState ().equals ("38700"))
	System.out.println ("Got expected exception: " + x.toString ());
      else
	throw x;
    }
    //  System.out.println(); 

    runQuery (con, "select substr(descr,1,25), " +
	      "findv(cast(descr as varchar(60))) from udftest");
    System.out.println ();

    runQuery (con, "select int1, counter(), int1/counter() from udftest");
    System.out.println ();

    runQuery (con, "select get_blob(dlurlcomplete(file_reference)) " +
		   "from datalink_udftest");
    System.out.println ();

    runQuery (con, "select * from table(listDirectory('/')) as files");
    System.out.println ();
  }


  //  Run a query and print out its results 
  static void
  runQuery (Connection con, String sql) throws Exception
  {
    Statement s = con.createStatement ();

    System.out.println ("Executing SQL query:");
    System.out.println (sql);

    ResultSet rs = s.executeQuery (sql);
    int row = 1;
    int numColumns = rs.getMetaData ().getColumnCount ();
    while (rs.next ())
      {
	System.out.print ("Row " + row + ": ");
	for (int column = 1; column <= numColumns; column++)
	  {
	    if (column > 1)
	      System.out.print (", ");
	    System.out.print (rs.getString (column));
	  }
	System.out.println ();
	row++;
      }
    rs.close ();
    s.close ();
  }


  //  Drop the UDF tables we built 
  static void
  deleteTable (Connection con) throws Exception
  {
    Statement s = con.createStatement ();
    s.executeUpdate ("drop table udftest");
    s.executeUpdate ("drop table datalink_udftest");
    s.close ();
    System.out.println ("Dropped UDF test tables.");
  }
}