/****************************************************************************
** (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: udfemcli.sqc
**
** SAMPLE: Call a variety of types of embedded SQL user-defined functions.
**
**         This file is a client for the UDFs from udfemsrv.sqc.
**
** SQL STATEMENTS USED:
**         CLOSE
**         COMMIT
**         CREATE FUNCTION
**         CREATE TABLE
**         DECLARE CURSOR
**         DROP FUNCTION
**         DROP TABLE
**         EXECUTE IMMEDIATE
**         FETCH
**         PREPARE
**
** STRUCTURES USED:
**         sqlca
**
**                           
*****************************************************************************
**
** For more information on the sample programs, see the README file.
**
** For information on developing embedded SQL applications see the Developing Embedded SQL Applications book.
**
** 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
****************************************************************************/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlenv.h>
#include "utilemb.h"

/* scalar UDFs */
int ExternalScalarUDFWithSQLUse(void);
int ExternalScalarUDFWithNestingUse(void);
int ExternalClobLocatorScalarUDFUse(void);

/* table UDFs */
int ExternalTableUDFWithSQLUse(void);

EXEC SQL BEGIN DECLARE SECTION;
  char name[15];
  char job[15];
  double salary;
  short salaryInd;
  double salaryInUS;
  char empno[7];
  char resume_format[7];
  sqlint32 numWords;
  char deptNo[4];
  short deptNoInd;
  double amount;
  short amountInd;

  char strStmt[256];

EXEC SQL END DECLARE SECTION;

int main(int argc, char *argv[])
{
  int rc = 0;
  char dbAlias[SQL_ALIAS_SZ + 1];
  char user[USERID_SZ + 1];
  char pswd[PSWD_SZ + 1];

  /* check the command line arguments */
  rc = CmdLineArgsCheck1(argc, argv, dbAlias, user, pswd);
  if (rc != 0)
  {
    return rc;
  }

  printf("\nTHIS SAMPLE SHOWS HOW TO WORK WITH EMBEDDED SQL UDFs.\n");

  /* connect to database */
  rc = DbConn(dbAlias, user, pswd);
  if (rc != 0)
  {
    return rc;
  }

  /* scalar UDFs */
  rc = ExternalScalarUDFWithSQLUse();
  rc = ExternalScalarUDFWithNestingUse();
  rc = ExternalClobLocatorScalarUDFUse();

  /* table UDFs */
  rc = ExternalTableUDFWithSQLUse();

  /* disconnect from database */
  rc = DbDisconn(dbAlias);

  return 0;
} /* main */

int ExternalScalarUDFWithSQLUse()
{
  struct sqlca sqlca;
  int rc = 0;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS\n");
  printf("  CREATE FUNCTION\n");
  printf("  COMMIT\n");
  printf("  SELECT\n");
  printf("  DROP FUNCTION\n");
  printf("TO WORK WITH SCALAR UDF THAT CONTAINS EMBEDDED SQL:\n");

  printf("\n  DROP FUNCTION Convert, if exists.\n");
  EXEC SQL DROP FUNCTION Convert;

  EXEC SQL DROP TABLE exchangeRate;

  /* register the Scalar UDF 'Convert' */
  printf("\n  Register the Scalar UDF 'Convert'.\n");

  EXEC SQL CREATE FUNCTION Convert(CHAR(2), DOUBLE, CHAR(2))
    RETURNS DOUBLE
    EXTERNAL NAME 'udfemsrv!Convert'
    FENCED
    CALLED ON NULL INPUT
    NOT VARIANT
    READS SQL DATA
    PARAMETER STYLE DB2SQL
    LANGUAGE C
    NO EXTERNAL ACTION;
  EMB_SQL_CHECK("scalar UDF 'Convert' -- register");

  /* Create the exchangeRate table */
  printf("\n  Create a table called 'exchangeRate'.\n");

  EXEC SQL CREATE TABLE exchangeRate (sourceCurrency char(2),
                                      resultCurrency char(2),
                                      exchangeRate double);
  EMB_SQL_CHECK("scalar UDF 'Convert' -- create table");

  /* Insert into the exchangeRate table */
  printf("\n  Populate 'exchangeRate' table.\n");

  strcpy(strStmt, "INSERT INTO exchangeRate values "
         "(char('US'), char('CA'), 1.5), (char('CA'), char('US'), 0.67)");

  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("scalar UDF 'Convert' -- populate table");

  printf("\n  COMMIT.\n");

  EXEC SQL COMMIT;
  EMB_SQL_CHECK("transaction -- commit");

  /* use scalar UDF */
  printf("\n  Use the scalar UDF 'Convert':\n");
  printf("    SELECT name, job, salary, Convert(char('CA'), ");
  printf("salary, char('US'))\n");
  printf("      FROM staff\n");
  printf("      WHERE name LIKE 'S%%'\n");

  strcpy(strStmt, "SELECT name, job, salary, Convert(char('CA'), salary,"
                  " char('US')) FROM staff WHERE name LIKE 'S%' ");

  EXEC SQL PREPARE stmt10 FROM :strStmt;
  EMB_SQL_CHECK("statement -- prepare");

  /* declare cursor */
  EXEC SQL DECLARE c10 CURSOR FOR stmt10;

  /* open cursor */
  EXEC SQL OPEN c10;
  EMB_SQL_CHECK("cursor -- open");

  /* fetch cursor */
  printf("\n  Fetch each row and display.\n");
  printf("    NAME       JOB     SALARY   SALARY IN US\n");
  printf("    ---------- ------- -------- ------------\n");

  EXEC SQL FETCH c10 INTO :name, :job, :salary, :salaryInUS;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %-10s %-7s %-7.2f %-7.2f", name, job, salary, salaryInUS);
    printf("\n");

    EXEC SQL FETCH c10 INTO :name, :job, :salary, :salaryInUS;
    EMB_SQL_CHECK("cursor -- fetch");
  }

  /* close cursor */
  EXEC SQL CLOSE c10;
  EMB_SQL_CHECK("cursor -- close");

  /* drop scalar UDF */
  printf("\n  DROP the scalar UDF 'Convert'.\n");

  EXEC SQL DROP FUNCTION Convert;
  EMB_SQL_CHECK("scalar UDF -- drop");

  /* drop exchangeRate table */
  printf("\n  DROP TABLE exchangeRate.\n");

  EXEC SQL DROP TABLE exchangeRate;
  EMB_SQL_CHECK("scalar UDF -- drop table");

  printf("\n  COMMIT.\n");

  EXEC SQL COMMIT;
  EMB_SQL_CHECK("transaction -- commit");

  return 0;
} /* ExternalScalarUDFWithSQLUse */

int ExternalScalarUDFWithNestingUse()
{
  struct sqlca sqlca;
  int rc = 0;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS\n");
  printf("  CREATE FUNCTION\n");
  printf("  COMMIT\n");
  printf("  SELECT\n");
  printf("  DROP FUNCTION\n");
  printf("TO WORK WITH NESTED SCALAR UDF THAT CONTAINS EMBEDDED SQL:\n");

  /* drop scalar UDF, if exists */
  printf("\n  DROP the scalar UDF, if exists.\n");

  EXEC SQL DROP FUNCTION Convert;

  EXEC SQL DROP FUNCTION SumSalary;

  EXEC SQL DROP TABLE exchangeRate;

  /* register scalar UDF */
  printf("\n  Register the scalar UDFs 'Convert' and 'SumSalary'.\n");

  EXEC SQL CREATE FUNCTION Convert(CHAR(2), DOUBLE, CHAR(2))
    RETURNS DOUBLE
    EXTERNAL NAME 'udfemsrv!Convert'
    FENCED
    CALLED ON NULL INPUT
    NOT VARIANT
    READS SQL DATA
    PARAMETER STYLE DB2SQL
    LANGUAGE C
    NO EXTERNAL ACTION;
  EMB_SQL_CHECK("scalar UDF -- register");

  EXEC SQL CREATE FUNCTION SumSalary(CHAR(3))
    RETURNS DOUBLE
    EXTERNAL NAME 'udfemsrv!SumSalary'
    FENCED
    CALLED ON NULL INPUT
    NOT VARIANT
    READS SQL DATA
    PARAMETER STYLE DB2SQL
    LANGUAGE C
    NO EXTERNAL ACTION;
  EMB_SQL_CHECK("scalar UDF -- register");

  /* Create the exchangeRate table */
  printf("\n  Create the table 'exchangeRate'.\n");

  EXEC SQL CREATE TABLE exchangeRate (sourceCurrency char(2),
                                      resultCurrency char(2),
                                      exchangeRate double);
  EMB_SQL_CHECK("scalar UDF -- create table");

  /* Insert into the exchangeRate table */
  printf("\n  Populate the table 'exchangeRate'.\n");

  strcpy(strStmt, "INSERT INTO exchangeRate values (char('US'), "
                  "char('CA'), 1.5), (char('CA'), char('US'), 0.67)");

  EXEC SQL EXECUTE IMMEDIATE :strStmt;

  EMB_SQL_CHECK("scalar UDF -- populate table");

  printf("\n  COMMIT.\n");

  EXEC SQL COMMIT;
  EMB_SQL_CHECK("transaction -- commit");

  /* use scalar UDF */
  printf("\n  Use the scalar UDF 'SumSalary':\n");
  printf("    SELECT DeptNo, SumSalary(DeptNo)\n");
  printf("      FROM department\n");

  strcpy(strStmt, "SELECT DeptNo, SumSalary(DeptNo) FROM department");

  EXEC SQL PREPARE stmt30 FROM :strStmt;
  EMB_SQL_CHECK("statement -- prepare");

  /* declare cursor */
  EXEC SQL DECLARE c30 CURSOR FOR stmt30;

  /* open cursor */
  EXEC SQL OPEN c30;
  EMB_SQL_CHECK("cursor -- open");

  /* fetch cursor */
  printf("\n  Fetch each row and display.\n");
  printf("    DeptNo Sum of Salary in US currency\n");
  printf("    ------ ------------------------------\n");

  EXEC SQL FETCH c30 INTO :deptNo :deptNoInd, :amount :amountInd;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    if (deptNoInd >= 0)
    {
      printf("    %-6s",deptNo);
    }
    else
    {
      printf("    -     ");
    }

    if (amountInd >= 0)
    {
         printf(" %f",amount);
    }
    else
    {
      printf(" -");
    }
    printf("\n");

    EXEC SQL FETCH c30 INTO :deptNo :deptNoInd, :amount :amountInd;
    EMB_SQL_CHECK("cursor -- fetch");
  }

  /* close cursor */
  EXEC SQL CLOSE c30;
  EMB_SQL_CHECK("cursor -- close");

  /* drop scalar UDF 'SumSalary' */
  printf("\n  DROP the scalar UDF 'SumSalary'.\n");

  EXEC SQL DROP FUNCTION SumSalary;
  EMB_SQL_CHECK("scalar UDF -- drop");

  /* drop scalar UDF 'Convert' */
  printf("\n  DROP the scalar UDF 'Convert'.\n");

  EXEC SQL DROP FUNCTION Convert;
  EMB_SQL_CHECK("scalar UDF -- drop");

  /* drop exchangeRate table */
  printf("\n  DROP TABLE exchangeRate.\n");

  EXEC SQL DROP TABLE exchangeRate;
  EMB_SQL_CHECK("scalar UDF -- drop table");

  printf("\n  COMMIT.\n");

  EXEC SQL COMMIT;
  EMB_SQL_CHECK("transaction -- commit");

  return 0;
} /* ExternalScalarUDFWithNestingUse */

int ExternalClobLocatorScalarUDFUse()
{
  struct sqlca sqlca;
  int rc = 0;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS\n");
  printf("  CREATE FUNCTION\n");
  printf("  COMMIT\n");
  printf("  SELECT\n");
  printf("  DROP FUNCTION\n");
  printf("TO WORK WITH CLOB LOCATOR SCALAR UDF THAT CONTAINS ");
  printf("EMBEDDED SQL:\n");

  /* drop CLOB locator scalar UDF, if exists */
  printf("\n  DROP the CLOB locator scalar UDF, if exists.\n");

  EXEC SQL DROP FUNCTION CountWords;

  /* register the CLOB Locator scalar UDF */
  printf("\n  Register the CLOB locator scalar UDF 'CountWords'.\n");

  EXEC SQL CREATE FUNCTION CountWords(CLOB(5k) AS LOCATOR)
    RETURNS INTEGER
    EXTERNAL NAME 'udfemsrv!ClobLocatorScalarUDF'
    FENCED
    RETURNS NULL ON NULL INPUT
    NOT VARIANT
    READS SQL DATA
    PARAMETER STYLE DB2SQL
    LANGUAGE C
    NO EXTERNAL ACTION;
  EMB_SQL_CHECK("scalar UDF -- register");

  printf("\n  COMMIT.\n");

  EXEC SQL COMMIT;
  EMB_SQL_CHECK("transaction -- commit");

  /* use CLOB locator scalar UDF */
  printf("\n  Use the CLOB locator scalar UDF:\n");
  printf("    SELECT empno, resume_format, CountWords(resume)\n");
  printf("      FROM emp_resume\n");
  printf("      WHERE resume_format = 'ascii'\n");

  strcpy(strStmt, "SELECT empno, resume_format, CountWords(resume) "
                  "  FROM emp_resume "
                  "  WHERE resume_format = 'ascii' ");


  EXEC SQL PREPARE stmt40 FROM :strStmt;
  EMB_SQL_CHECK("statement -- prepare");

  /* declare cursor */
  EXEC SQL DECLARE c40 CURSOR FOR stmt40;

  /* open cursor */
  EXEC SQL OPEN c40;
  EMB_SQL_CHECK("cursor -- open");

  /* fetch cursor */
  printf("\n  Fetch each row and display.\n");
  printf("    EMPNO   RESUME_FORMAT NUM.WORDS\n");
  printf("    ------- ------------- ---------\n");

  EXEC SQL FETCH c40 INTO :empno, :resume_format, :numWords;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %-7s %-13s %ld", empno, resume_format, numWords);
    printf("\n");

    EXEC SQL FETCH c40 INTO :empno, :resume_format, :numWords;
    EMB_SQL_CHECK("cursor -- fetch");
  }

  /* close cursor */
  EXEC SQL CLOSE c40;
  EMB_SQL_CHECK("cursor -- close");

  /* drop the CLOB locator scalar UDF */
  printf("\n  DROP the CLOB locator scalar UDF 'CountWords'.\n");

  EXEC SQL DROP FUNCTION CountWords;
  EMB_SQL_CHECK("scalar UDF -- drop");

  printf("\n  COMMIT.\n");

  EXEC SQL COMMIT;
  EMB_SQL_CHECK("transaction -- commit");

  return 0;
} /* ExternalClobLocatorScalarUDFUse */


int ExternalTableUDFWithSQLUse()
{
  struct sqlca sqlca;
  int rc = 0;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS\n");
  printf("  CREATE FUNCTION\n");
  printf("  COMMIT\n");
  printf("  SELECT\n");
  printf("  DROP FUNCTION\n");
  printf("TO WORK WITH TABLE UDF THAT CONTAINS EMBEDDED SQL:\n");

  /* drop table UDF, if exists */
  printf("\n  DROP FUNCTION TableUDFWithSQL, if exists.\n");

  EXEC SQL DROP FUNCTION TableUDFWithSQL;

  /* register table UDF */
  printf("\n  Register the table UDF 'TableUDFWithSQL'.\n");

  EXEC SQL CREATE FUNCTION TableUDFWithSQL(DOUBLE)
    RETURNS TABLE(name VARCHAR(20),
                  job VARCHAR(20),
                  salary DOUBLE)
    EXTERNAL NAME 'udfemsrv!TableUDFWithSQL'
    LANGUAGE C
    PARAMETER STYLE DB2SQL
    NOT DETERMINISTIC
    FENCED
    READS SQL DATA
    NO EXTERNAL ACTION
    SCRATCHPAD 10
    FINAL CALL DISALLOW
    PARALLEL NO DBINFO;
  EMB_SQL_CHECK("table UDF with SQL -- register");

  printf("\n  COMMIT.\n");

  EXEC SQL COMMIT;
  EMB_SQL_CHECK("transaction -- commit");

  /* use table UDF */
  printf("\n  Use the table UDF 'TableUDFWithSQL':");
  printf("\n    SELECT udfTable.name, udfTable.job, udfTable.salary");
  printf("\n      FROM TABLE(TableUDFWithSQL(1.5))");
  printf("\n      AS udfTable\n");

  strcpy(strStmt, "SELECT udfTable.name, udfTable.job, udfTable.salary "
                  "  FROM TABLE(TableUDFWithSQL(1.5)) "
                  "  AS udfTable ");

  EXEC SQL PREPARE stmt50 FROM :strStmt;
  EMB_SQL_CHECK("statement -- prepare");

  /* delcare cursor */
  EXEC SQL DECLARE c50 CURSOR FOR stmt50;

  /* open cursor */
  EXEC SQL OPEN c50;
  EMB_SQL_CHECK("cursor -- open");

  /* fetch cursor */
  printf("\n  Fetch each row and display.\n");
  printf("    NAME       JOB     SALARY   \n");
  printf("    ---------- ------- ---------\n");

  EXEC SQL FETCH c50 INTO :name, :job, :salary:salaryInd;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %-10s %-7s", name, job);
    if (salaryInd >= 0)
    {
      printf(" %7.2f", salary);
    }
    else
    {
      printf(" %8s", "-");
    }
    printf("\n");

    EXEC SQL FETCH c50 INTO :name, :job, :salary:salaryInd;
    EMB_SQL_CHECK("cursor -- fetch");
  }

  /* close cursor */
  EXEC SQL CLOSE c50;
  EMB_SQL_CHECK("cursor -- close");

  /* drop table UDF */
  printf("\n  DROP FUNCTION TableUDFWithSQL.\n");

  EXEC SQL DROP FUNCTION TableUDFWithSQL;
  EMB_SQL_CHECK("table UDF -- drop");

  printf("\n  COMMIT.\n");

  EXEC SQL COMMIT;
  EMB_SQL_CHECK("transaction -- commit");

  return 0;
} /* TableUDFWithSQLUse */