/***************************************************************************
** (c) Copyright IBM Corp. 2008 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.
*****************************************************************************
**
** SAMPLE FILE NAME: xmludfs.c
**
** PURPOSE: The purpose of this sample is to show extended support of XML for 
**	    sourced UDF and SQL bodied UDF in serial and DPF environment 
** 	    for DB2 Cobra. 
**
** USAGE SCENARIO: The scenario is for a Book Store that has two types of 
**     customers, retail customers and corporate customers. Corporate 
**      customers do bulk purchases of books for their company libraries. 
**      The Book Store also maintains list of �registered customers� 
**      who are frequent buyers from the store and have registered 
**      themselves with the store. The store has a DBA, sales clerk and a 
**      manager for maintaining the database and to run queries on different 
**      tables to view the book sales.
**
**      The store manager frequently queries various tables to get 
**      information such as contact numbers of different departments,
**      location details, location manager details, employee details 
**      in order to perform various business functions like promoting 
**      employees, analysing sales, giving awards and bonus to employees 
**      based on their sales.
**
**      The manager is frustrated writing the same queries every time to 
**      get the information and observes performance degradation as well.  
**      So he decides to create a user-defined function and a stored  
**      procedure for each of his requirements. 

**
** PREREQUISITE: NONE
**
** EXECUTION: bldapp xmludfs
**            xmludfs
**
** INPUTS: NONE
**
** OUTPUTS: Successfull execution of all UDFs and stored procedures.
**
**                           
**
** SQL STATEMENTS USED:
**           CREATE TABLE
**           INSERT
**           DROP
**
** SQL/XML FUNCTIONS USED:
**          XMLPARSE
**          XMLQUERY
**          XMLEXISTS
**
*****************************************************************************
**
** For more information about the command line processor (CLP) scripts,
** see the README file.
**
** For information on using SQL statements, see the SQL Reference.
**
*****************************************************************************
**
**  SAMPLE DESCRIPTION
**
*****************************************************************************
** 1. UDF Scalar function which takes an XML variable as input the parameter
**    and returns XML value as output.
**
** 2. UDF Table function which takes an XML variable as input the parameter
**    and returns table with XML values as output.
**
** 3. Sourced UDF which takes an XML variable as the input parameter   
**    and returns XML value as output.
**
** 4. SQL bodied UDF which takes an XML variable as the input parameter
**    and returns a table with XML values as output. This UDF 
**    internally calls a stored procedure which takes an XML variable
**    as the input parameter and returns an XML value as output.
************************************************************************
**
**  INCLUDE ALL HEADER FILES
**
****************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sqlcli1.h>
#include "utilcli.h" /* Header file for CLI sample code */

int main(int argc, char *argv[])
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;

  SQLHANDLE henv; /* environment handle */
  SQLHANDLE hdbc; /* connection handles */

  char dbAlias[SQL_MAX_DSN_LENGTH + 1];
  char user[MAX_UID_LENGTH + 1];
  char pswd[MAX_PWD_LENGTH + 1];

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

  printf("\n This sample shows how to pass XML type variables");
  printf(" as input parameters, return type or local ");
  printf(" variables in SQL bodied UDFs\n\n");

  /* initialize the application by calling a helper
     utility function defined in utilcli.c */
  rc = CLIAppInit(dbAlias,
                  user,
                  pswd,
                  &henv,
                  &hdbc,
                  (SQLPOINTER)SQL_AUTOCOMMIT_ON);
  if (rc != 0)
  {
    return rc;
  }

  rc = SetUpTables(hdbc);
  rc = ScalarUDF(hdbc);
  rc = TableUDF(hdbc);
  rc = SourcedUDF(hdbc);
  rc = InvokeSpFromUDF(hdbc);
  rc = CleanUpTables(hdbc);
 
    
  /* terminate the application by calling a helper
    utility function defined in utilcli.c */
  rc = CLIAppTerm(&henv, &hdbc, dbAlias);

  return rc;
}

int SetUpTables(SQLHANDLE hdbc)
{
   SQLRETURN cliRC = SQL_SUCCESS;
   int rc = 0;
   SQLHANDLE hstmt; /* statement handle */
   SQLCHAR *stmt = 0;
  
   printf("---------------------------------\n");
   printf("Setting up tables for the sample\n");
   printf("---------------------------------\n");

   printf("\n-----------------------------------------------------------");
   printf("\nUSE THE CLI FUNCTIONS\n");
   printf("  SQLSetConnectAttr\n");
   printf("  SQLAllocHandle\n");
   printf("  SQLExecDirect\n");
   printf("  SQLEndTran\n");
   printf("  SQLFreeHandle\n");
   printf("TO USE XML TYPE IN UDFs :\n");

   /* set AUTOCOMMIT OFF */
   cliRC = SQLSetConnectAttr(hdbc,
                            SQL_ATTR_AUTOCOMMIT,
                            (SQLPOINTER)SQL_AUTOCOMMIT_OFF,
                            SQL_NTS);
   DBC_HANDLE_CHECK(hdbc, cliRC);


   /* allocate a statement handle */
   cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
   DBC_HANDLE_CHECK(hdbc, cliRC);
 
   stmt = (SQLCHAR *)"CREATE TABLE sales_department(dept_id CHAR(10), "
                    " dept_info XML)";
   printf("\n%s\n\n", stmt);
  
   /* execute create table statement directly */
   printf("create the table called dept\n");
   cliRC = SQLExecDirect(hstmt, stmt, SQL_NTS);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

   stmt = (SQLCHAR *)"CREATE TABLE sales_employee (emp_id INTEGER, "
          "total_sales INTEGER, emp_details XML)";
   cliRC = SQLExecDirect(hstmt, stmt, SQL_NTS);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
   printf("\n%s\n\n", stmt);

   stmt = (SQLCHAR *)"CREATE TABLE performance_bonus_employees(bonus_info XML)";
   cliRC = SQLExecDirect(hstmt, stmt, SQL_NTS);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
   printf("\n%s\n\n", stmt);
   
   stmt = (SQLCHAR *)"INSERT INTO sales_employee VALUES (5001, 40000, XMLPARSE(document "
			  "'<employee id=\"5001\">"
			  "<name>Lethar Kessy</name>"
			  "<address>"
			    "<street>555 M G Road</street>"
			    "<city>Bangalore</city>"
			    "<state>Karnataka</state>"
			    "<country>India</country>"
			    "<zipcode>411004</zipcode>"
			  "</address>"
			  "<phone>"
			    "<cell>9435344354</cell>"
			  "</phone>"
			  "<dept>DS02</dept>"
			  "<skill_level>7</skill_level>"
			  "<sales>40000</sales>"
			  "<salary currency=\"INR\">25500</salary>"
			  "<designation>Sr. Manager</designation>"
			  "<employee_type>regular</employee_type>"
			  "<manager>Harry</manager> "
			  "</employee> '))";
   cliRC = SQLExecDirect(hstmt, stmt, SQL_NTS);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

   stmt = (SQLCHAR *) "INSERT INTO sales_employee VALUES (5002, 50000, XMLPARSE(document "
	"'<employee id=\"5002\">"
	  "<name>Mathias Jessy</name>"
	  "<address>"
		"<street>Indra Nagar Road No. 5</street>"
		"<city>Bangalore</city>"
	      "<state>Karnataka</state>"
            "<country>India</country>"
            "<zipcode>411004</zipcode>"
	  "</address>"
	  "<phone>"
	    "<cell>9438884354</cell>"
	  "</phone>"
	  "<dept>DS02</dept>"
	  "<skill_level>6</skill_level>"
	  "<sales>50000</sales>"
	  "<salary currency=\"INR\">22500</salary>"
	  "<designation>Manager</designation>"
	  "<employee_type>regular</employee_type>"
	  "<manager>Harry</manager>"
	  "</employee> '))";

   cliRC = SQLExecDirect(hstmt, stmt, SQL_NTS);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

   stmt = (SQLCHAR *)"INSERT INTO sales_employee VALUES (5003, 40000, XMLPARSE(document "
		"'<employee id=\"5003\">"
  		"<name>Mohan Kumar</name>"
		"  <address>"
		    "<street>Vijay Nagar Road No. 5</street>"
    		    "<city>Bangalore</city>"
                "<state>Karnataka</state>"
                "<country>India</country>"
                "<zipcode>411004</zipcode>"
  	      "</address>"
		"<phone>"
    			"<cell>9438881234</cell>"
  		"</phone>"
  		"<dept>DS02</dept>"
  		"<skill_level>5</skill_level>"
  		"<sales>40000</sales>"
  		"<salary currency=\"INR\">15500</salary>"
  		"<designation>Associate Manager</designation>"
  		"<employee_type>regular</employee_type>"
  		"<manager>Harry</manager>"
		"</employee> '))";

   cliRC = SQLExecDirect(hstmt, stmt, SQL_NTS);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
 
   stmt = (SQLCHAR *) "INSERT INTO sales_department VALUES ('DS02', XMLPARSE(document "
			"'<department id=\"DS02\">"
			  "<name>sales</name>"
  			  "<manager id=\"M2001\">"
    			  "<name>Harry Thomas</name>"
    			  "<phone>"
      			"<cell>9732432423</cell>"
    			  "</phone>"
  			  "</manager>"
  			  "<address>"
    			  "<street>Bannerghatta</street>"
    			  "<city>Bangalore</city>"
    			  "<state>Karnataka</state>"
    			  "<country>India</country>"
    			  "<zipcode>560012</zipcode>"
  			  "</address>"
  			  "<phone>"
    			  "<office>080-23464879</office>"
    			  "<office>080-56890728</office>"
    			  "<fax>080-45282976</fax>"
  			  "</phone>"
			  "</department>'))";

   cliRC = SQLExecDirect(hstmt, stmt, SQL_NTS);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

   stmt = (SQLCHAR *) "COMMIT";
   cliRC = SQLExecDirect(hstmt, stmt, SQL_NTS);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

   printf("\n  Rolling back the transaction...\n");

   cliRC = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK);
   DBC_HANDLE_CHECK(hdbc, cliRC);
   printf("  Transaction rolled back.\n");

   /* free the statement handle */
   cliRC = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

   return rc;
 
}

/*-----------------------------------------------------------------------
-- 1. UDF Scalar function which takes an XML variable as input parameter
--    and returns an XML value as output.
-------------------------------------------------------------------------*/

int ScalarUDF(SQLHANDLE hdbc)
{
   SQLRETURN cliRC = SQL_SUCCESS;
   int rc = 0;
   SQLHANDLE hstmt; /* statement handle */
   SQLCHAR *stmt = 0;
   struct 
   {
     SQLINTEGER ind;
     SQLVARCHAR data[1000];
   }xmldata;

   printf("---------------------------------\n");
   printf("Create a scalar function 'getDeptContactNumbers' which ");
   printf("returns a list of department phone numbers\n");
   printf("---------------------------------\n");

   stmt = "CREATE FUNCTION getDeptContactNumbers(dept_info_p XML) "
              "RETURNS XML "
              "LANGUAGE SQL "
              "SPECIFIC contactNumbers "
              "NO EXTERNAL ACTION "
              "BEGIN ATOMIC "
  	        "RETURN XMLQuery('document {<phone_list>{"
              "$dep/department/phone}</phone_list>}'  "
              "PASSING dept_info_p as \"dep\"); "
		  "END";
   printf("\n%s\n\n", stmt);

     /* set AUTOCOMMIT OFF */
   cliRC = SQLSetConnectAttr(hdbc,
                            SQL_ATTR_AUTOCOMMIT,
                            (SQLPOINTER)SQL_AUTOCOMMIT_OFF,
                            SQL_NTS);
   DBC_HANDLE_CHECK(hdbc, cliRC);


   /* allocate a statement handle */
   cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
   DBC_HANDLE_CHECK(hdbc, cliRC);
 
   /* execute create table statement directly */
   cliRC = SQLExecDirect(hstmt, stmt, SQL_NTS);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

   printf("--------------------------------------------------\n");
   printf("Call scalar UDF 'getDeptContactNumbers' to get contact");
   printf(" numbers of the department \"DS02\"\n\n");
   printf("--------------------------------------------------\n");

   stmt = (SQLCHAR *) "SELECT getDeptContactNumbers(sales_department.dept_info) "
          "FROM sales_department WHERE dept_id = 'DS02'";
   printf("\n%s\n\n", stmt);
   
  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC);

  /* prepare the statement */
  cliRC = SQLPrepare(hstmt, stmt, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* execute the statement */
  cliRC = SQLExecute(hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column POID to variable PID*/
  cliRC = SQLBindCol(hstmt, 1, SQL_C_CHAR, &xmldata.data, 3000, &xmldata.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* fetch result returned from Select statement*/
  cliRC = SQLFetch(hstmt); 
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  while (cliRC != SQL_NO_DATA_FOUND) 
  {
    if(xmldata.ind > 0)
    {
      printf("\n%s\n\n", xmldata.data);
    }
    /* fetch next row */
    cliRC = SQLFetch(hstmt);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
  }
  
    printf("\n  Rolling back the transaction...\n");

   /* end the transactions on the connection */
   cliRC = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK);
   DBC_HANDLE_CHECK(hdbc, cliRC);
   printf("  Transaction rolled back.\n");

   /* free the statement handle */
   cliRC = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

   return rc;
 
}

/*------------------------------------------------------------------------
-- 2. UDF Table function which takes an XML variable as input parameter
--    and returns a table with XML values as output.
--------------------------------------------------------------------------*/

int TableUDF(SQLHANDLE hdbc)
{
   SQLRETURN cliRC = SQL_SUCCESS;
   int rc = 0;
   SQLHANDLE hstmt; /* statement handle */
   SQLCHAR *stmt = 0;
   SQLINTEGER emp_id;
   SQLINTEGER skill_level;
   SQLINTEGER salary;
   SQLVARCHAR name[50];
   struct 
   {
     SQLINTEGER ind;
     SQLVARCHAR data[1000];
   }xmldata;

   printf("---------------------------------\n");
   printf("The store opens new branches in different parts of the city. ");
   printf("The book store manager wants to promote senior managers and associate ");
   printf("managers and designate them to manage these new branches. He wants to ");
   printf("update the skill level and salaries of all the promoted managers in the");
   printf("sales_employee table. He asks the DBA to create a table function for ");
   printf("this requirement. The DBA creates the 'updatePromotedEmployeesInfo' ");
   printf("table function. This function updates the skill level and salaries of");
   printf("the promoted managers in sales_employee table and returns details of ");
   printf("all the managers who got promoted. \n");
   printf("---------------------------------\n");

   stmt = "CREATE FUNCTION updatePromotedEmployeesInfo(emp_id_p INTEGER) "
          "RETURNS TABLE (name VARCHAR(50), emp_id integer, skill_level integer, "
	  "               salary double, address XML) "
	  "LANGUAGE SQL "
	  "MODIFIES SQL DATA "
	  "SPECIFIC func1 "
	  "BEGIN ATOMIC "
	  "UPDATE sales_employee SET emp_details = XMLQuery('transform "
	  "       copy $emp_info := $emp "
          "       modify if ($emp_info/employee[skill_level = 7 and "
	  "               designation = \"Sr. Manager\"]) "
	  "then "
	  "( "
	  " do replace value of $emp_info/employee/skill_level with 8, "
	  " do replace value of $emp_info/employee/salary with "
	  "        $emp_info/employee/salary * 9.5 "
	  ") "
	  "else if ($emp_info/employee[skill_level = 6  and  "
	  "        designation = \"Manager\"]) "
	  "then "
	  "( "
	  "do replace value of $emp_info/employee/skill_level with 7, "
	  "do replace value of $emp_info/employee/salary with "
	  "        $emp_info/employee/salary * 7.5 "
	  ") "
	  "else if ($emp_info/employee[skill_level = 5  and "
	  "designation = \"Associate Manager\"]) "
	  "then "
	  "( "
	  "do replace value of $emp_info/employee/skill_level with 6, "
	  "do replace value of $emp_info/employee/salary with "
	  "$emp_info/employee/salary * 5.5 "
	  ") "
	  "else () "
	  "return $emp_info' PASSING emp_details as \"emp\")"
	  "WHERE emp_id = emp_id_p;"
	  "RETURN SELECT X.* "
	  "FROM sales_employee, XMLTABLE('$e_info/employee' PASSING "
	  "emp_details as \"e_info\" "
	  " COLUMNS "
	  "name VARCHAR(50) PATH 'name', "
	  "emp_id integer PATH '@id', "
	  "skill_level integer path 'skill_level', "
	  "salary double path 'salary', "
	  "addr XML path 'address') AS X WHERE sales_employee.emp_id = emp_id_p; "
          "END";

	  
   printf("%s\n\n", stmt);

     /* set AUTOCOMMIT OFF */
   cliRC = SQLSetConnectAttr(hdbc,
                            SQL_ATTR_AUTOCOMMIT,
                            (SQLPOINTER)SQL_AUTOCOMMIT_OFF,
                            SQL_NTS);
   DBC_HANDLE_CHECK(hdbc, cliRC);

   /* allocate a statement handle */
   cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
   DBC_HANDLE_CHECK(hdbc, cliRC);
 
   /* execute create table statement directly */
   cliRC = SQLExecDirect(hstmt, stmt, SQL_NTS);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

   printf("---------------------------------------------------------\n");
   printf("Call the 'updatePromotedEmployeesInfo' table function to ");
   printf("update the details of promoted employees in 'sales_employee' table ");
   printf("---------------------------------------------------------\n");


   stmt = "SELECT A.* FROM sales_employee AS E, "
         "table(updatePromotedEmployeesInfo(E.emp_id)) AS A";
  printf("%s\n\n", stmt);

  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC);

  /* prepare the statement */
  cliRC = SQLPrepare(hstmt, stmt, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* execute the statement */
  cliRC = SQLExecute(hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column info to variable xmldata*/
  cliRC = SQLBindCol(hstmt, 1, SQL_C_CHAR, &name, 50, NULL);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column info to variable xmldata*/
  cliRC = SQLBindCol(hstmt, 2, SQL_C_LONG, &emp_id, 0, NULL);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column info to variable xmldata*/
  cliRC = SQLBindCol(hstmt, 3, SQL_C_LONG, &skill_level, 0, NULL);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column info to variable xmldata*/
  cliRC = SQLBindCol(hstmt, 4, SQL_C_LONG, &salary, 0, NULL);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column info to variable xmldata*/
  cliRC = SQLBindCol(hstmt, 5, SQL_C_CHAR, &xmldata.data, 2000, &xmldata.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
  

  /* fetch result returned from Select statement*/
  cliRC = SQLFetch(hstmt); 
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  while (cliRC != SQL_NO_DATA_FOUND) 
  {
    printf("\n\n%s   %d  %d  %d", name, emp_id, skill_level, salary);
    if(xmldata.ind > 0)
    {
      printf("\n%s\n\n", xmldata.data);
    }
    /* fetch next row */
    cliRC = SQLFetch(hstmt);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
  }
  
   printf("\n  Rolling back the transaction...\n");

   /* end the transactions on the connection */
   cliRC = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK);
   DBC_HANDLE_CHECK(hdbc, cliRC);
   printf("  Transaction rolled back.\n");

   /* free the statement handle */
   cliRC = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

   return rc;
    	
}

/*--------------------------------------------------------------------------
-- 3. Sourced UDF which takes an XML variable as the input parameter
--    and returns an XML value as output.
--------------------------------------------------------------------------*/

int SourcedUDF(SQLHANDLE hdbc)
{
   SQLRETURN cliRC = SQL_SUCCESS;
   int rc = 0;
   SQLHANDLE hstmt; /* statement handle */
   SQLCHAR *stmt = 0;
   struct 
   {
     SQLINTEGER ind;
     SQLVARCHAR data[1000];
   }xmldata;

   printf("---------------------------------\n");
   printf("The store manager would like to get a particular dept manager ");
   printf("name and his contact numbers. The DBA then creates a ");
   printf("'getManagerDetails' UDF to get a particular department manager ");
   printf("name and manager contact details. ");
   printf("---------------------------------\n");

   stmt = (SQLCHAR *)"CREATE FUNCTION getManagerDetails(dept_info_p XML, dept_p VARCHAR(5)) "
    	 	   "RETURNS XML "
		   "LANGUAGE SQL "
		   "SPECIFIC getManagerDetails "
		   "BEGIN ATOMIC "
		   "RETURN XMLQuery('$info/department[name=$dept_name]/manager' "
		   "PASSING dept_info_p as \"info\", dept_p as \"dept_name\"); "
		   "END";
   printf("%s\n\n", stmt);

     /* set AUTOCOMMIT OFF */
   cliRC = SQLSetConnectAttr(hdbc,
                            SQL_ATTR_AUTOCOMMIT,
                            (SQLPOINTER)SQL_AUTOCOMMIT_OFF,
                            SQL_NTS);
   DBC_HANDLE_CHECK(hdbc, cliRC);

   /* allocate a statement handle */
   cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
   DBC_HANDLE_CHECK(hdbc, cliRC);
 
   /* execute create table statement directly */
   cliRC = SQLExecDirect(hstmt, stmt, SQL_NTS);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

   printf("Create a sourced UDF 'getManagerInfo' based on ");
   printf("'getManagerDetails' user defined function \n\n");
 
   stmt = "CREATE FUNCTION getManagerInfo(XML, CHAR(10))"
            "RETURNS XML "
	    "SOURCE getManagerDetails(XML, VARCHAR(5)) ";
   /* execute create table statement directly */
   cliRC = SQLExecDirect(hstmt, stmt, SQL_NTS);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
   printf("%s\n\n", stmt);

   printf("Call the sourced UDF 'getManagerInfo' to get ");
   printf("'sales' department manager details");

   stmt = "SELECT getManagerInfo(sales_department.dept_info, 'sales') "
          " FROM sales_department WHERE dept_id='DS02'";
   printf("%s\n\n", stmt);

   
  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC);

  /* prepare the statement */
  cliRC = SQLPrepare(hstmt, stmt, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* execute the statement */
  cliRC = SQLExecute(hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column POID to variable PID*/
  cliRC = SQLBindCol(hstmt, 1, SQL_C_CHAR, &xmldata.data, 3000, &xmldata.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* fetch result returned from Select statement*/
  cliRC = SQLFetch(hstmt); 
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  while (cliRC != SQL_NO_DATA_FOUND) 
  {
    if(xmldata.ind > 0)
    {
      printf("\n%s\n\n", xmldata.data);
    }
    /* fetch next row */
    cliRC = SQLFetch(hstmt);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
  }
  
   printf("\n  Rolling back the transaction...\n");

   /* end the transactions on the connection */
   cliRC = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK);
   DBC_HANDLE_CHECK(hdbc, cliRC);
   printf("  Transaction rolled back.\n");

   /* free the statement handle */
   cliRC = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

   return rc;
   
}

/*-----------------------------------------------------------------------
-- 4. SQL bodied UDF which takes an XML variable as the input parameter
--    and returns a table with XML values as output. This UDF
--    calls a stored procedure which takes an XML variable
--    as the input parameter and returns an XML value as output.
------------------------------------------------------------------------*/
int InvokeSpFromUDF(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
   int rc = 0;
   SQLHANDLE hstmt; /* statement handle */
   SQLCHAR *stmt = 0;
   struct 
   {
     SQLINTEGER ind;
     SQLVARCHAR data[1000];
   }xmldata;

   printf("---------------------------------\n");
   printf("Create a function which calculates an employee gift cheque ");
   printf("amount and adds this value as a new element into the ");
   printf("employee information document");
   printf("---------------------------------\n");

   stmt = "CREATE PROCEDURE calculateGiftChequeAmount("
               " INOUT emp_info_p XML, "
               "IN emp_name_p VARCHAR(20)) "
	       "LANGUAGE SQL "
	       "MODIFIES SQL DATA "
	       "SPECIFIC customer_award "
	       "BEGIN  "
	       "DECLARE emp_bonus_info_v XML; "
	       "IF XMLEXISTS('$e_info/employee[name = $emp1]' "
               "PASSING emp_info_p as \"e_info\","
	       "emp_name_p as \"emp1\")"
	       "THEN "
	       "SET emp_bonus_info_v = XMLQuery('copy $bonus := $info "
	       "modify "
	       "do insert <customer_gift_cheque>{"
               " $bonus/employee/salary * 0.50 + 25000} "
	       "</customer_gift_cheque> into $bonus/employee "
	       "return $bonus' PASSING emp_info_p as \"info\"); "
	       "END IF; "
	       "SET emp_info_p = emp_bonus_info_v; "
	       "END ";
   printf("%s\n\n", stmt);
   
     /* set AUTOCOMMIT OFF */
   cliRC = SQLSetConnectAttr(hdbc,
                            SQL_ATTR_AUTOCOMMIT,
                            (SQLPOINTER)SQL_AUTOCOMMIT_OFF,
                            SQL_NTS);
   DBC_HANDLE_CHECK(hdbc, cliRC);

   /* allocate a statement handle */
   cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
   DBC_HANDLE_CHECK(hdbc, cliRC);
 
   /* execute create table statement directly */
   cliRC = SQLExecDirect(hstmt, stmt, SQL_NTS);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

   printf("-----------------------------------------------------\n");
   printf("Some employees who got customer appreciation awards ");
   printf("and whose total sales are greater than expected sales ");
   printf("were given gift cheques by the store. The DBA creates ");
   printf("'calculatePerformanceBonus' function to calculate ");
   printf("employee performance bonus along with customer gift ");
   printf("cheque amount and update the employee information ");
   printf("in sales_employee table. ");
   printf("-----------------------------------------------------\n");

   stmt = "CREATE FUNCTION calculatePerformanceBonus(sales_info_p XML) "
	    "RETURNS table(info XML) "
	    "LANGUAGE SQL "
	    "SPECIFIC awardedemployees "
	    "MODIFIES SQL DATA "
   	    "BEGIN ATOMIC "
	    "DECLARE awarded_emp_info_v  XML; "
            "DECLARE emp_name VARCHAR(20); "
	    "DECLARE min_sales_v INTEGER; "
            "DECLARE avg_sales_v INTEGER; "
	    "SET min_sales_v = XMLCAST(XMLQuery('$info/sales_per_annum/min_sales' "
            "PASSING sales_info_p as \"info\")  AS INTEGER); "
	    "SET avg_sales_v = XMLCAST(XMLQuery('$info/sales_per_annum/avg_sales' "
            "PASSING sales_info_p as \"info\")  AS INTEGER); "
	    "FOR_LOOP: FOR EACH_ROW AS "
            "SELECT XMLCAST(XMLQuery('$info/employee/name' PASSING awarded_emp_info_v "    
	    "as \"info\") AS VARCHAR(20)) as name, "
	    "XMLQuery('copy $e_info := $inf "
	    "modify "
	    "do insert <performance_bonus>{$e_info/employee/salary "
	    "* 0.25 + 5000} "
	    "</performance_bonus> into $e_info/employee "
	    "return $e_info' PASSING emp_details as \"inf\") "
	    "as info "
	    "FROM sales_employee "
	    "WHERE  total_sales between min_sales_v and avg_sales_v "
	    "DO "
	    "SET awarded_emp_info_v = EACH_ROW.info; "
	    "SET emp_name = EACH_ROW.name; "
	    "CALL calculateGiftChequeAmount(awarded_emp_info_v, emp_name); "
	    "INSERT INTO performance_bonus_employees "
	    "VALUES (EACH_ROW.info); "
	    "END FOR; "
	    "RETURN SELECT * FROM performance_bonus_employees; "
	    "END " ;
		    
   printf("%s\n\n", stmt);

   /* execute create table statement directly */
   cliRC = SQLExecDirect(hstmt, stmt, SQL_NTS);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

   printf("Call the table function 'calculatePerformanceBonus' ");
   printf("to get the information of all the employees who got gift ");
   printf("cheques and performance bonus.");

   stmt = "SELECT * FROM table(calculatePerformanceBonus(XMLPARSE(document "
	    "'<sales_per_annum> "
	    "<target_sales>80000</target_sales> "
	    "<avg_sales>70000</avg_sales> "
	    "<min_sales>35000</min_sales> "
	    "</sales_per_annum>')))";
  printf("%s\n\n", stmt);

  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC);

  /* prepare the statement */
  cliRC = SQLPrepare(hstmt, stmt, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* execute the statement */
  cliRC = SQLExecute(hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column POID to variable PID*/
  cliRC = SQLBindCol(hstmt, 1, SQL_C_CHAR, &xmldata.data, 3000, &xmldata.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* fetch result returned from Select statement*/
  cliRC = SQLFetch(hstmt); 
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  while (cliRC != SQL_NO_DATA_FOUND) 
  {
    if(xmldata.ind > 0)
    {
      printf("\n%s\n\n", xmldata.data);
    }
    /* fetch next row */
    cliRC = SQLFetch(hstmt);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
  }
  
   printf("\n  Rolling back the transaction...\n");

   /* end the transactions on the connection */
   cliRC = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK);
   DBC_HANDLE_CHECK(hdbc, cliRC);
   printf("  Transaction rolled back.\n");

   /* free the statement handle */
   cliRC = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
   STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

   return rc;
  
}

int CleanUpTables(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
   int rc = 0;
   SQLHANDLE hstmt; /* statement handle */
   SQLCHAR *stmt = 0;
   char clean[200];


     /* set AUTOCOMMIT OFF */
   cliRC = SQLSetConnectAttr(hdbc,
                            SQL_ATTR_AUTOCOMMIT,
                            (SQLPOINTER)SQL_AUTOCOMMIT_OFF,
                            SQL_NTS);
   DBC_HANDLE_CHECK(hdbc, cliRC);

   /* allocate a statement handle */
   cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
   DBC_HANDLE_CHECK(hdbc, cliRC);

  printf("cleaning up.... \n");
 
  strcpy(clean,"DROP TABLE sales_department");
  printf("%s \n",clean);
  rc = SQLExecDirect(hstmt, (SQLCHAR *)clean, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, rc);

  strcpy(clean,"DROP TABLE sales_employee");
  printf("%s \n",clean);
  rc = SQLExecDirect(hstmt, (SQLCHAR *)clean, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, rc);

  strcpy(clean,"DROP TABLE performance_bonus_employees");
  printf("%s \n",clean);
  rc = SQLExecDirect(hstmt, (SQLCHAR *)clean, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, rc);

  strcpy(clean, "COMMIT");
  printf("%s \n",clean);
  rc = SQLExecDirect(hstmt, (SQLCHAR *)clean, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, rc);
  
  /* Free the allocated handle */
  rc = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, rc);

  return 1;

}