//  
//  Source File Name = udfcli.sqC 
//  
//  Licensed Materials - Property of IBM 
//  
//  (C) COPYRIGHT International Business Machines Corp. 1995, 2000 
//  All Rights Reserved. 
//  
//  US Government Users Restricted Rights - Use, duplication or 
//  disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
//  
//  
//     PURPOSE : 
//     To demonstrate how to use User-Defined Funtions, It includes scalar UDF 
//     and sourced UDF in embedded SQL of C++. 
//     This is a client for the UDFs from CLI udfsrv.c . 
//  
//  
//     EXTERNAL DEPENDENCIES : 
//        - Existing database for precompile purposes. 
//        - Precompile with the SQL precompiler (PREP in DB2) 
//        - Binding to a database (BIND in DB2) 
//        - Compiling and linking with the IBM Cset++ compiler (AIX and OS/2) 
//          or the Microsoft Visual C++ compiler (Windows) 
//          or the compiler supported on your platform. 
//  
//  For more information about these samples see the README file. 
//  
//  For more information on programming in C++, see the 
//     -  "Programming in C and C++" section of the Application Development Guide 
//  
//  For more information on Building C++ Applications, see the: 
//     - "Building C++ Applications" section of the Application Building Guide. 
//  
//  For more information on the SQL language see the SQL Reference. 
//  

#include <iostream.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sqlenv.h>
#include <sqlda.h>
#include <sqlca.h>
#include <sqlutil.h>
#include "utilemb.h"

extern "C"  {
int printf( const char *,...);
}
 

EXEC SQL INCLUDE SQLCA;  

class CNT {
   public:
      void Connect();
      void Connect(char *, char *);

      void Disconnect();
   private:
      EXEC SQL BEGIN DECLARE SECTION;
         char userid[9];
         char passwd[19];
      EXEC SQL END DECLARE SECTION;

};


void CNT::Connect () {
   cout <<  "Connecting to database SAMPLE... \n";
   EXEC SQL CONNECT TO sample;
   EMB_SQL_CHECK("CONNECT TO SAMPLE") ;
   cout << "Connected to database SAMPLE \n";
}

void CNT::Connect (char *uid, char *pswd) {
   strcpy (userid, uid);
   strcpy (passwd, pswd);

   cout <<  "Connecting to database SAMPLE... \n";
   EXEC SQL CONNECT TO sample USER :userid USING :passwd;
   EMB_SQL_CHECK("CONNECT TO SAMPLE") ;
   cout <<  "Connected to database SAMPLE \n";
}

void CNT::Disconnect () {
   //  Disconnect from Remote Database 
   cout <<  "Disconnecting from database SAMPLE... \n";
   EXEC SQL CONNECT RESET;
   EMB_SQL_CHECK("CONNECT RESET");
   cout <<  "Disconnected from database SAMPLE \n";
}

class udfcli {
   public:
       int ScalarUDFCall( ) ;
       int SourcedUDFUse( ) ;    

   private:

};

int main(int argc, char *argv[]) {

   CNT    cnt;
   udfcli udfcli;

   if (argc == 1) {
     cnt.Connect();

   } else if (argc == 3) {
     cnt.Connect(argv[1], argv[2]);

   } else {
     cout << "\nUSAGE: udfcli [userid passwd]\n\n";
     return 1;
   } //  end if 

   //  call  scalar UDF  
   udfcli.ScalarUDFCall( ) ;
   //  Use  Sourced UDF function 
   udfcli.SourcedUDFUse( ) ;    

   EXEC SQL ROLLBACK;
   EMB_SQL_CHECK ("ROLLBACK");

   cnt.Disconnect();

   return 0;
}
//  end of main  


/******************************************************************************
**   SourcedUDFUse 
******************************************************************************/
int udfcli::SourcedUDFUse() 
{   
    int         rc = 0; 

    char   * stmt1 =  
    "CREATE DISTINCT TYPE CNUM AS INTEGER WITH COMPARISONS";

    char   * stmt2 = 
    "CREATE FUNCTION MAX(CNUM) RETURNS CNUM source sysibm.max(integer)";

    char   * stmt3 = "CREATE TABLE CUSTOMER ( "
        "CustNum     CNUM NOT NULL, "
        "CustName    CHAR(30) NOT NULL)" ;  

    char   * stmt4 = "INSERT INTO CUSTOMER VALUES "
        " ( CAST(1 AS CNUM), 'JOHN WALKER'), "
        " ( CAST(2 AS CNUM), 'BRUCE ADAMSON'), "
        " ( CAST(3 AS CNUM), 'SALLY KWAN')" ;

    char   * stmt5 =  
           "SELECT CAST(MAX(CustNum) AS INTEGER ) FROM customer " ;

    char   * stmt6 =  "DROP TABLE CUSTOMER" ;	    

    char   * stmt7 =  "DROP FUNCTION MAX(CNUM)" ;	    

    char   * stmt8 =  "DROP DISTINCT TYPE CNUM" ;	    

    EXEC SQL BEGIN DECLARE SECTION; 
    char         prep_str[200];
    sqlint32     maxCustNum_val; 

    EXEC SQL END DECLARE SECTION;

    cout << sp5 <<"CREATE DISTINCT TYPE CNUM AS INTEGER\n";
    cout << sp5 <<"WITH COMPARISONS\n";    

    EXEC SQL CREATE DISTINCT TYPE CNUM AS INTEGER WITH COMPARISONS;

    EMB_SQL_CHECK ("Create DISTINCT");
    
    /* create a sourced UDF */ 
    cout << "        CREATE FUNCTION MAX(CNUM) RETURNS CNUM\n";
    cout << "        source sysibm.max(integer)\n";    

    EXEC SQL CREATE FUNCTION MAX(CNUM) RETURNS CNUM source sysibm.max(integer);

    EMB_SQL_CHECK ("Create FUNCTION");
    /* create a table that uses the distinct type */ 
    cout << sp5 <<"CREATE TABLE CUSTOMER ( \n";
    cout << sp8 <<"CustNum     CNUM NOT NULL,\n";
    cout << sp8 <<"CustName    CHAR(30) NOT NULL)\n";    

    EXEC SQL CREATE TABLE customer (CustNum     CNUM NOT NULL, 
        CustName    CHAR(30) NOT NULL);  

    EMB_SQL_CHECK ("Create TABLE");
    /* insert values in table */ 
    cout << sp5 << "INSERT INTO CUSTOMER VALUES\n";
    cout << sp8 <<"( CAST(1 AS CNUM), 'JOHN WALKER'),\n";    
    cout << sp8 <<"( CAST(2 AS CNUM), 'BRUCE ADAMSON'),\n";    
    cout << sp8 <<"( CAST(3 AS CNUM), 'SALLY KWAN')\n";    
    
    strcpy(prep_str,stmt4);
    EXEC SQL PREPARE statement1 from :prep_str;
    EMB_SQL_CHECK ("PREPARE");

    EXEC SQL EXECUTE  statement1;
    EMB_SQL_CHECK ("Insert TABLE");

    /* execute directly the select statement */ 
    cout << sp5 <<"Use the sourced UDF performing this statement:\n";    
    cout << sp8 <<"SELECT FROM CUSTOMER CAST(MAX(CUSTNUM) AS INTEGER)\n"; 
 
    strcpy(prep_str,stmt5);
    EXEC SQL PREPARE statement1 from :prep_str;
    EMB_SQL_CHECK ("PREPARE");

    EXEC SQL DECLARE c1 CURSOR FOR statement1;
    EXEC SQL OPEN c1 ;
    EMB_SQL_CHECK ("OPEN");
    EXEC SQL FETCH c1 INTO :maxCustNum_val ;
    EMB_SQL_CHECK ("SELECT TABLE");

    if (SQLCODE ==0 ){
       cout << sp8 << "Max(CustNum) is :  " << maxCustNum_val <<endl ;
    }	    
    
    EXEC SQL CLOSE c1;

    /* drop the table */ 
    cout << sp5 <<stmt6 <<endl;    

    EXEC SQL DROP TABLE customer ;	    

    EMB_SQL_CHECK ("DROP TABLE");
    /* drop the sourced UDF */ 
    cout << sp5 <<stmt7 <<endl;    

    strcpy(prep_str,stmt7);
    EXEC SQL PREPARE statement1 from :prep_str;
    EMB_SQL_CHECK ("PREPARE");

    EXEC SQL EXECUTE  statement1;
    EMB_SQL_CHECK ("DROP FUNCTION");

    /* drop the distinct type */
    cout << sp5 <<stmt8 <<endl;  
  
	EXEC SQL DROP DISTINCT TYPE CNUM ;	    
    EMB_SQL_CHECK ("DROP TYPE");

    return(rc);    

error:
    EXEC SQL  WHENEVER  SQLERROR  continue; 
    cout << "Error occured SQLCODE = " << SQLCODE << "\n SQLERRMES ="
         <<sqlca.sqlerrmc <<endl;

    return(rc);    
}

/******************************************************************************
**   ScalarUDFCall 
******************************************************************************/
int udfcli::ScalarUDFCall( )
{
    int         rc = 0; 

    char    * stmtDrop = "DROP FUNCTION ScalarUDF";
    char    * stmtRegister = 
    "  CREATE FUNCTION ScalarUDF (CHAR(5), float) RETURNS float "
    "    FENCED "
    "    EXTERNAL NAME 'udfsrv!ScalarUDF' "
    "    NOT VARIANT NO SQL PARAMETER STYLE DB2SQL"
    "    LANGUAGE C  NO EXTERNAL ACTION  ";

    EXEC SQL BEGIN DECLARE SECTION; 

    char      * stmtSelect = 
    "SELECT name, job, salary, ScalarUDF( job, salary)"
    "    FROM staff WHERE name LIKE 'S%' " ;    

    char       name_val[10];
    char       job_val[6];
    double     salary_val,newsalary_val;

    EXEC SQL END DECLARE SECTION;
/*
    EXEC SQL  WHENEVER  SQLERROR  GOTO error; 
*/
    cout <<"\nUSE THE EMBEDDED SQL \n";
    cout <<"-Scalar UDF\n";
    cout <<"TO CALL UDFs:\n";   

    EXEC SQL DROP FUNCTION ScalarUDF;
    if (SQLCODE !=0) {
    cout << sp5 <<"No function dropped\n";
    }

    cout << sp5 <<"Register the scalar UDF.\n";    

    EXEC SQL 
      CREATE FUNCTION ScalarUDF (CHAR(5), float) RETURNS float 
      FENCED 
      EXTERNAL NAME 'udfsrv!ScalarUDF' 
      NOT VARIANT NO SQL PARAMETER STYLE DB2SQL LANGUAGE C  
      NO EXTERNAL ACTION  ;

    EMB_SQL_CHECK ("Create Function");
    cout << sp5 <<"Use the scalar UDF performing this statement:\n";    
    cout << sp8 <<"SELECT name, job, salary, ScalarUDF( job, salary)\n"; 
    cout << sp8 <<"FROM staff WHERE name LIKE 'S%%'\n";    

    EXEC SQL PREPARE statement2 from :stmtSelect;
    EMB_SQL_CHECK ("PREPARE");

    EXEC SQL DECLARE c2 CURSOR FOR statement2;

    EXEC SQL OPEN c2 ;
    EMB_SQL_CHECK ("OPEN");

    /* fetch each row, and display */ 
    cout << sp5 <<"Fetch each row and dispaly.\n";
    cout << sp8 <<"NAME       JOB    SALARY     NEW_SALARY\n";
    cout << sp8 <<"---------- ------ ---------- ----------\n";

    EXEC SQL FETCH c2 INTO :name_val,:job_val,:salary_val,
                           :newsalary_val; 
    
    EMB_SQL_CHECK ("First fetch");
    if (SQLCODE == 100)
    {   cout << sp5 <<"Data not found.\n";
    }
    while (SQLCODE == 0 )     {  
/*
       printf("%s%-10s %-7s %-7.2f   %-7.2f\n", 
               sp8,name_val, job_val, salary_val,newsalary_val);
*/
       cout << sp8 <<name_val <<", " << job_val<<", " << salary_val
            <<","<<newsalary_val <<endl;

    EXEC SQL FETCH c2 INTO :name_val,:job_val,:salary_val,
                           :newsalary_val; 
    }	    

    EMB_SQL_CHECK ("Fetch");
    /* close the cursor */ 
    
    EXEC SQL CLOSE c2;

    EXEC SQL DROP FUNCTION ScalarUDF;

    return(rc);    

error:
    EXEC SQL  WHENEVER  SQLERROR  continue; 
    cout << "Error occured SQLCODE = " << SQLCODE << "\n SQLERRMES= "
         <<sqlca.sqlerrmc <<endl;
    return(rc);    
      
}