/****************************************************************************
** (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: dbinline.sqc
**
** SAMPLE: How to use inline SQL Procedure Language
**
**         This sample demonstrates how to use Inline SQL Procedure Language.
**         The program uses an existing database to create various tables,
**         functions and triggers to show three sample usages. The first
**         example is a scalar function. The second example is a table-level
**         function and the third example demonstrates a pair of triggers.
**
** SQL STATEMENTS USED:
**         INCLUDE
**         CREATE TABLE
**         CREATE FUNCTION (SQL Scalar, Table or Row)
**         CREATE TRIGGER
**         INSERT
**         DECLARE CURSOR
**         OPEN
**         FETCH
**         CLOSE
**         BEGIN ATOMIC
**         LEAVE
**         IF statement
**         WHILE statement
**         FOR statement
**         RETURN
**         SELECT
**         UPDATE
**         DROP FUNCTION
**         DROP TABLE
**         DROP TRIGGER
**
** 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 <stdlib.h>
#include <string.h>
#include <sqlenv.h>
#include <sqlcodes.h>
#include <sqlutil.h>
#include "utilemb.h"

EXEC SQL INCLUDE SQLCA;

EXEC SQL BEGIN DECLARE SECTION;

  char dbAlias[15];
  char user[15];
  char pswd[15];
  char strStmt[1000];
  short input;
  short output;
  short key;
  short value;
  char status[8 + 1];
  char part_no[5 + 1];
  short amount;
  short reorder_no;
  short stock_change;
  char action[3 + 1];
  char timestamp [25 + 1];
  short transaction_no;

EXEC SQL END DECLARE SECTION;

int ScalarFunction(void);
int TableFunction(void);
int Triggers(void);

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 USE INLINE SQL PROCEDURE LANGUAGE.\n");

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

  ScalarFunction();
  TableFunction();
  Triggers();

  /* disconnect from the database */
  rc = DbDisconn(dbAlias);
  if(rc != 0)
  {
    return rc;
  }

  return 0;

} /* main */

  /* ScalarFunction
     This function shows BEGIN ATOMIC, DECLARE variable, IF, WHILE <label>,
     SET and LEAVE. It consists of a function that calculates the sum of even
     numbers from zero up to and including an input integer, which may be
     positive or negative. */

int ScalarFunction(void)
{
  printf("\n---------------------------------------------------------");
  printf("\nSCALAR FUNCTION EXAMPLE\n");
  printf("---------------------------------------------------------\n\n");
  printf("  Function which calculates the sum of even numbers up to and ");
  printf("including \n  an input integer, which may be positive or ");
  printf("negative\n\n");
  printf("  CREATE FUNCTION Even_sum(input INT)\n");
  printf("    RETURNS int\n");
  printf("    BEGIN ATOMIC\n");
  printf("      DECLARE to_add INT DEFAULT 0;\n");
  printf("      DECLARE result INT DEFAULT 0;\n");
  printf("      DECLARE direction INT DEFAULT 0;\n");
  printf("        IF (input < 0) THEN\n");
  printf("          SET direction = -1;\n");
  printf("        END IF;\n");
  printf("        summing_loop:\n");
  printf("        WHILE (1 = 1) DO\n");
  printf("          IF (direction = 0) THEN\n");
  printf("            SET to_add = to_add + 2;\n");
  printf("              IF (to_add > input) THEN\n");
  printf("                LEAVE summing_loop;\n");
  printf("              END IF;\n");
  printf("          ELSE\n");
  printf("            SET to_add = to_add -2;\n");
  printf("            IF (to_add < input) THEN\n");
  printf("              LEAVE summing_loop;\n");
  printf("            END IF;\n");
  printf("          END IF;\n");
  printf("          SET result = result + to_add;\n");
  printf("        END WHILE summing_loop;\n");
  printf("    RETURN result;\n");
  printf("  END\n");

  strcpy(strStmt, "CREATE FUNCTION Even_sum(input INT) "
                  "RETURNS int "
                  "BEGIN ATOMIC "
                  "DECLARE to_add INT DEFAULT 0; "
                  "DECLARE result INT DEFAULT 0; "
                  "DECLARE direction INT DEFAULT 0; "
                  "IF (input < 0) THEN "
                  "SET direction = -1; "
                  "END IF; "
                  "summing_loop: "
                  "WHILE (1 = 1) DO "
                  "IF (direction = 0) THEN "
                  "SET to_add = to_add + 2; "
                  "IF (to_add > input) THEN "
                  "LEAVE summing_loop; "
                  "END IF; "
                  "ELSE "
                  "SET to_add = to_add -2; "
                  "IF (to_add < input) THEN "
                  "LEAVE summing_loop; "
                  "END IF; "
                  "END IF; "
                  "SET result = result + to_add; "
                  "END WHILE summing_loop; "
                  "RETURN result; "
                  "END");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("CREATE SCALAR FUNCTION");

  printf("\n  Table to hold results generated by the function Even_sum");
  printf("\n\n  CREATE TABLE Even_sum_result(input INT, output INT)");

  /* table to hold the results demonstrating the function Even_sum */
  EXEC SQL CREATE TABLE Even_sum_result(input INT, output INT);
  EMB_SQL_CHECK("CREATE TABLE EVEN_SUM_RESULT");

  printf("\n\n  INSERT INTO even_sum_result\n");
  printf("    VALUES (10, Even_sum(10))\n");
  printf("           (-5, Even_sum(-5))\n");
  printf("           (-10, Even_sum(-10))\n");

  strcpy(strStmt, "INSERT INTO even_sum_result VALUES "
                  "(10, Even_sum(10)), "
                  "(-5, Even_sum(-5)), "
                  "(-10, Even_sum(-10))");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("INSERT INTO even_sum_result");

  printf("\n  SELECT * FROM even_sum_result ORDER BY input");
  printf("\n    INPUT OUTPUT\n");
  printf("    ----- ------\n");

  strcpy(strStmt, "SELECT * FROM even_sum_result ORDER BY input");
  EXEC SQL PREPARE S1 FROM :strStmt;
  EMB_SQL_CHECK("PREPARE S1");

  EXEC SQL DECLARE C1 CURSOR FOR S1;

  EXEC SQL OPEN C1;
  EMB_SQL_CHECK("OPEN CURSOR");

  EXEC SQL FETCH C1 INTO :input, :output;
  EMB_SQL_CHECK("FETCH CURSOR");

  while (sqlca.sqlcode != SQL_RC_W100)
  {
    printf("%9d %6d\n", input, output);

    EXEC SQL FETCH C1 INTO :input, :output;
    EMB_SQL_CHECK("FETCH CURSOR");
  }

  EXEC SQL CLOSE C1;
  EMB_SQL_CHECK("CLOSE CURSOR");

  printf("\n  DROP TABLE even_sum_result\n");
  EXEC SQL DROP TABLE even_sum_result;
  EMB_SQL_CHECK("DROP TABLE EVEN_SUM_RESULT");

  printf("\n  DROP FUNCTION Even_sum\n");
  EXEC SQL DROP FUNCTION Even_sum;
  EMB_SQL_CHECK("DROP FUNCTION EVEN_SUM");

  return 0;

} /* ScalarFunction */

  /* TableFunction:
     Demonstrates a table function. No new statements introduced.
     This function cleans a table by removing all values outside
     the range between 0 and 1000, and aligns the remaining values
     to the closest multiple of 10. */

int TableFunction(void)
{
  printf("\n---------------------------------------------------------");
  printf("\nTABLE FUNCTION EXAMPLE\n");
  printf("---------------------------------------------------------\n\n");
  printf("Function which cleans a table by removing all values outside");
  printf("\nthe range between 0 and 1000, and aligns the remaining values");
  printf("\nto the closest multiple of 10\n\n");
  printf("  CREATE FUNCTION Discretize(raw INTEGER) RETURNS INTEGER\n");
  printf("  RETURN CASE\n");
  printf("    WHEN raw < 0 THEN CAST(NULL AS INTEGER)\n");
  printf("    WHEN raw > 1000 THEN NULL\n");
  printf("    ELSE (((raw + 5) / 10) * 10) END\n");

  strcpy(strStmt, "CREATE FUNCTION Discretize(raw INTEGER) RETURNS INTEGER "
                  "RETURN CASE "
                  "WHEN raw < 0 THEN CAST(NULL AS INTEGER) "
                  "WHEN raw > 1000 THEN NULL "
                  "ELSE (((raw + 5) / 10) * 10) END");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("CREATE TABLE FUNCTION");

  printf("\n  Table to be cleaned by the function Discretize");
  printf("\n\n  CREATE TABLE source(key INT NOT NULL PRIMARY KEY,");
  printf(" value INT)\n");

  /* table to be cleaned by the function Discretize */
  EXEC SQL CREATE TABLE source(key INT NOT NULL PRIMARY KEY, value INT);
  EMB_SQL_CHECK("CREATE TABLE SOURCE");

  printf("\n  INSERT INTO source\n");
  printf("    VALUES (1,   -5),\n");
  printf("           (2,    8),\n");
  printf("           (3, 1200),\n");
  printf("           (4,   23),\n");
  printf("           (5,   10),\n");
  printf("           (6,  876)\n");

    strcpy(strStmt, "INSERT INTO source VALUES "
                  "(1,   -5), "
                  "(2,    8), "
                  "(3, 1200), "
                  "(4,   23), "
                  "(5,   10), "
                  "(6,  876)");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("INSERT INTO SOURCE");

  printf("\n  SELECT * FROM source ORDER BY key");
  printf("\n    KEY VALUE\n");
  printf("    --- -----\n");

  strcpy(strStmt, "SELECT * FROM source ORDER BY key");
  EXEC SQL PREPARE S2 FROM :strStmt;
  EMB_SQL_CHECK("PREPARE S2");

  EXEC SQL DECLARE C2 CURSOR FOR S2;

  EXEC SQL OPEN C2;
  EMB_SQL_CHECK("OPEN CURSOR");

  EXEC SQL FETCH C2 INTO :key, :value;
  EMB_SQL_CHECK("FETCH CURSOR");

  while (sqlca.sqlcode != SQL_RC_W100)
  {
    printf("%7d %5d\n", key, value);

    EXEC SQL FETCH C2 INTO :key, :value;
    EMB_SQL_CHECK("FETCH CURSOR");
  }

  EXEC SQL CLOSE C2;
  EMB_SQL_CHECK("CLOSE CURSOR");

  printf("\n  Table to hold the values cleaned by the function Discretize");
  printf("\n\n  CREATE TABLE table_result(key INT NOT NULL PRIMARY KEY,");
  printf(" value INT)\n");

  /* table to hold the values cleaned by the function Discretize */
  EXEC SQL CREATE TABLE table_result(key INT NOT NULL PRIMARY KEY,
                                     value INT);
  EMB_SQL_CHECK("CREATE TABLE TABLE_RESULT");

  printf("\n  Table to hold the values outside of range");
  printf("\n\n  CREATE TABLE outside_range(key INT NOT NULL PRIMARY KEY,\n");
  printf("                             value INT)\n");

  /* table to hold the values outside of range */
  EXEC SQL CREATE TABLE outside_range(key INT NOT NULL PRIMARY KEY,
                                      value INT);
  EMB_SQL_CHECK("CREATE TABLE OUTSIDE_RANGE");

  printf("\n  BEGIN ATOMIC\n");
  printf("    FOR row AS\n");
  printf("      SELECT key, value, Discretize(value) AS d FROM source\n");
  printf("    DO\n");
  printf("      IF row.d is NULL THEN\n");
  printf("        INSERT INTO outside_range VALUES(row.key, row.value);\n");
  printf("      ELSE\n");
  printf("        INSERT INTO result VALUES(row.key, row.d);\n");
  printf("      END IF;\n");
  printf("    END FOR;\n");
  printf("  END\n");

  strcpy(strStmt, "BEGIN ATOMIC "
                  "FOR row AS "
                  "SELECT key, value, Discretize(value) AS d FROM source "
                  "DO "
                  "IF row.d is NULL THEN "
                  "INSERT INTO outside_range VALUES(row.key, row.value); "
                  "ELSE "
                  "INSERT INTO table_result VALUES(row.key, row.d); "
                  "END IF; "
                  "END FOR; "
                  "END");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("EXECUTE SCRIPT");

  printf("\n  SELECT * FROM outside_range ORDER BY key");
  printf("\n    KEY VALUE\n");
  printf("    --- -----\n");

  strcpy(strStmt, "SELECT * FROM outside_range ORDER BY key");
  EXEC SQL PREPARE S3 FROM :strStmt;
  EMB_SQL_CHECK("PREPARE S3");

  EXEC SQL DECLARE C3 CURSOR FOR S3;

  EXEC SQL OPEN C3;
  EMB_SQL_CHECK("OPEN CURSOR");

  EXEC SQL FETCH C3 INTO :key, :value;
  EMB_SQL_CHECK("FETCH CURSOR");

  while (sqlca.sqlcode != SQL_RC_W100)
  {
    printf("%7d %5d\n", key, value);

    EXEC SQL FETCH C3 INTO :key, :value;
    EMB_SQL_CHECK("FETCH CURSOR");
  }

  EXEC SQL CLOSE C3;
  EMB_SQL_CHECK("CLOSE CURSOR");

  printf("\n  SELECT * FROM table_result ORDER BY key");
  printf("\n    KEY VALUE\n");
  printf("    --- -----\n");

  strcpy(strStmt, "SELECT * FROM table_result ORDER BY key");
  EXEC SQL PREPARE S4 FROM :strStmt;
  EMB_SQL_CHECK("PREPARE S4");

  EXEC SQL DECLARE C4 CURSOR FOR S4;

  EXEC SQL OPEN C4;
  EMB_SQL_CHECK("OPEN CURSOR");

  EXEC SQL FETCH C4 INTO :key, :value;
  EMB_SQL_CHECK("FETCH CURSOR");

  while (sqlca.sqlcode != SQL_RC_W100)
  {
    printf("%7d %5d\n", key, value);

    EXEC SQL FETCH C4 INTO :key, :value;
    EMB_SQL_CHECK("FETCH CURSOR");
  }

  EXEC SQL CLOSE C4;
  EMB_SQL_CHECK("CLOSE CURSOR");

  printf("\n  DROP TABLE source\n");

  EXEC SQL DROP TABLE source;
  EMB_SQL_CHECK("DROP TABLE SOURCE");

  printf("\n  DROP TABLE table_result\n");

  EXEC SQL DROP TABLE table_result;
  EMB_SQL_CHECK("DROP TABLE TABLE_RESULT");

  printf("\n  DROP TABLE outside_range\n");

  EXEC SQL DROP TABLE outside_range;
  EMB_SQL_CHECK("DROP TABLE OUTSIDE_RANGE");

  printf("\n  DROP FUNCTION Discretize\n");

  EXEC SQL DROP FUNCTION Discretize;
  EMB_SQL_CHECK("DROP FUNCTION DISCRETIZE");

  return 0;

} /* TableFunction */

  /* Triggers:
     Demonstrates Inline SQL PL in triggers. This shows DECLARE <condition>,
     SIGNAL and GET DIAGNOSTICS. */

int Triggers(void)
{
  printf("\n---------------------------------------------------------");
  printf("\nTRIGGER EXAMPLE\n");
  printf("---------------------------------------------------------\n\n");

  printf("  Table to record amount of inventory on hand using part number");
  printf(", amount,\n  an amount in stock level upon which the given part");
  printf(" will be reordered\n  and a status variable stating whether the ");
  printf("amount of a given part in\n  stock is sufficient or a reorder ");
  printf("is required\n\n");
  printf("  CREATE TABLE in_stock(part_no VARCHAR(5) NOT NULL, amount INT,");
  printf("\n                        reorder_no INT, status VARCHAR(8))\n");

  /* table records amount of inventory on hand: part number, amount, an
  amount in stock number upon which the given part will be reordered and
  a status variable stating whether the amount of a given part in stock
  is sufficient or a reorder is required */
  EXEC SQL CREATE TABLE in_stock(part_no VARCHAR(5) NOT NULL, amount INT,
                                 reorder_no INT, status VARCHAR(8));
  EMB_SQL_CHECK("CREATE TABLE IN_STOCK");

  printf("\n  INSERT INTO in_stock\n");
  printf("    VALUES('10001', 50, 30, 'fine')\n");
  printf("          ('10002', 30, 10, 'fine')\n");
  strcpy(strStmt, "INSERT INTO in_stock VALUES "
                  "('10001', 50, 30, 'fine'), "
                  "('10002', 30, 10, 'fine')");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("INSERT INTO IN_STOCK");

  printf("\n  SELECT * FROM in_stock ORDER BY part_no");
  printf("\n    PART_NO AMOUNT REORDER_NO  STATUS\n");
  printf("    ------- ------ ---------- -------\n");

  strcpy(strStmt, "SELECT * FROM in_stock ORDER BY part_no");
  EXEC SQL PREPARE S66 FROM :strStmt;
  EMB_SQL_CHECK("PREPARE S66");

  EXEC SQL DECLARE C66 CURSOR FOR S66;

  EXEC SQL OPEN C66;
  EMB_SQL_CHECK("OPEN CURSOR");

  EXEC SQL FETCH C66 INTO :part_no, :amount, :reorder_no, :status;
  EMB_SQL_CHECK("FETCH CURSOR");

  while (sqlca.sqlcode != SQL_RC_W100)
  {
    printf("%11s %6d %10d %7s\n", part_no, amount, reorder_no, status);

    EXEC SQL FETCH C66 INTO :part_no, :amount, :reorder_no, :status;
    EMB_SQL_CHECK("FETCH CURSOR");
  }

  EXEC SQL CLOSE C66;
  EMB_SQL_CHECK("CLOSE CURSOR");

  printf("\n  This trigger updates the STATUS variable of the in_stock ");
  printf("table if\n  the new amount is less than or equal to the reorder ");
  printf("number\n");

  /* This trigger updates the STATUS variable of the in_stock
     table if the new amount is less than or equal to the
     reorder number. */

  printf("\n  CREATE TRIGGER stock_status\n");
  printf("    NO CASCADE BEFORE UPDATE OF amount ON IN_STOCK\n");
  printf("    REFERENCING NEW AS n\n");
  printf("    FOR EACH ROW MODE DB2SQL\n");
  printf("      BEGIN ATOMIC\n");
  printf("        IF (n.amount > n.reorder_no) THEN\n");
  printf("          SET n.status = 'fine';\n");
  printf("        ELSE\n");
  printf("          SET n.status = 'reorder';\n");
  printf("        END IF;\n");
  printf("  END\n");

  strcpy(strStmt, "CREATE TRIGGER stock_status "
                  "NO CASCADE BEFORE UPDATE OF amount ON IN_STOCK "
                  "REFERENCING NEW AS n "
                  "FOR EACH ROW MODE DB2SQL "
                  "BEGIN ATOMIC "
                  "IF (n.amount > n.reorder_no) THEN "
                  "SET n.status = 'fine'; "
                  "ELSE "
                  "SET n.status = 'reorder'; "
                  "END IF; "
                  "END");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("CREATE TRIGGER STOCK_STATUS");

  printf("\n  Table to record stock transactions using part number, amount");
  printf(" by which\n  the given part changed, an action code (ADD for ");
  printf("adding to stock,\n  SUB for removing from stock) and the time ");
  printf("the transaction took place\n\n");
  printf("  CREATE TABLE stock_history(part_no VARCHAR(5) NOT NULL,\n");
  printf("                             stock_change INT,\n");
  printf("                             action VARCHAR(3),\n");
  printf("                             timestamp TIMESTAMP)\n");

  /* table records stock transactions: part number, amount by which the
  given part changed, an action code (ADD for adding to stock, SUB for
  removing from stock) and the time the transaction took place */
  EXEC SQL CREATE TABLE stock_history(part_no VARCHAR(5) NOT NULL,
                                      stock_change INT, action VARCHAR(3),
                                      timestamp TIMESTAMP);
  EMB_SQL_CHECK("CREATE TABLE STOCK_HISTORY");

  printf("\n  This trigger is activated when AMOUNT changes in the table ");
  printf("in_stock\n  to record details of the transaction into the ");
  printf("table stock_history\n");

  /* This trigger is activated when AMOUNT changes in the table
     in_stock to record the transaction into the table
     stock_history. */

  printf("\n  CREATE TRIGGER history\n");
  printf("    AFTER UPDATE OF amount ON IN_STOCK\n");
  printf("    REFERENCING NEW as n OLD AS o\n");
  printf("    FOR EACH ROW MODE DB2SQL\n");
  printf("      BEGIN ATOMIC\n");
  printf("        DECLARE change INT;\n");
  printf("        DECLARE type VARCHAR(3);\n");
  printf("        DECLARE no_change CONDITION FOR SQLSTATE '80001';\n");
  printf("        IF (n.amount > o.amount) THEN\n");
  printf("          SET change = n.amount - o.amount;\n");
  printf("          SET type = 'ADD';\n");
  printf("        ELSEIF (n.amount < o.amount) THEN\n");
  printf("          SET change = o.amount - n.amount;\n");
  printf("          SET type = 'SUB';\n");
  printf("        ELSE\n");
  printf("          SIGNAL no_change SET MESSAGE_TEXT = ");
  printf("'Identical amount';\n");
  printf("        END IF;\n");
  printf("        INSERT INTO stock_history\n");
  printf("          VALUES(n.part_no,\n");
  printf("                 change,\n");
  printf("                 type,\n");
  printf("                 TIMESTAMP(generate_unique()));\n");
  printf("  END\n");

  strcpy(strStmt, "CREATE TRIGGER history "
                  "AFTER UPDATE OF amount ON IN_STOCK "
                  "REFERENCING NEW as n OLD AS o "
                  "FOR EACH ROW MODE DB2SQL "
                  "BEGIN ATOMIC "
                  "DECLARE change INT; "
                  "DECLARE type VARCHAR(3); "
                  "DECLARE no_change CONDITION FOR SQLSTATE '80001'; "
                  "IF (n.amount > o.amount) THEN "
                  "SET change = n.amount - o.amount; "
                  "SET type = 'ADD'; "
                  "ELSEIF (n.amount < o.amount) THEN "
                  "SET change = o.amount - n.amount; "
                  "SET type = 'SUB'; "
                  "ELSE "
                  "SIGNAL no_change SET MESSAGE_TEXT = 'Identical amount'; "
                  "END IF; "
                  "INSERT INTO stock_history "
                  "VALUES(n.part_no, "
                  "change, "
                  "type, "
                  "TIMESTAMP(generate_unique())); "
                  "END");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("CREATE TRIGGER HISTORY");

  printf("\n  UPDATE in_stock SET amount = 25 WHERE part_no = '10001'\n");
  strcpy(strStmt, "UPDATE in_stock SET amount = 25 "
                  "WHERE part_no = '10001'");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("FIRST UPDATE OF IN_STOCK");

  printf("\n  SELECT status FROM in_stock WHERE part_no = '10001'");
  printf("\n    STATUS\n");
  printf("    -------\n");

  strcpy(strStmt, "SELECT status FROM in_stock WHERE part_no = '10001'");
  EXEC SQL PREPARE S5 from :strStmt;
  EMB_SQL_CHECK("PREPARE S5");

  EXEC SQL DECLARE C5 CURSOR FOR S5;

  EXEC SQL OPEN C5;
  EMB_SQL_CHECK("OPEN CURSOR");

  EXEC SQL FETCH C5 INTO :status;
  EMB_SQL_CHECK("FETCH CURSOR");

  while (sqlca.sqlcode != SQL_RC_W100)
  {
    printf("%11s\n", status);

    EXEC SQL FETCH C5 INTO :status;
    EMB_SQL_CHECK("FETCH CURSOR");
  }

  EXEC SQL CLOSE C5;
  EMB_SQL_CHECK("CLOSE CURSOR");

  printf("\n  Updating in_stock with an identical amount is not possible ");
  printf("in this\n  example to demonstrate DECLARE <condition> and ");
  printf("SIGNAL\n");

  printf("\n  UPDATE in_stock SET amount = 20 WHERE part_no = '10001'");
  strcpy(strStmt, "UPDATE in_stock SET amount = 20 "
                  "WHERE part_no = '10001'");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("SECOND UPDATE OF IN_STOCK");

  printf("\n  UPDATE in_stock SET amount = 20 WHERE part_no = '10001'\n");
  strcpy(strStmt, "UPDATE in_stock SET amount = 20 WHERE part_no = '10001'");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EXPECTED_ERR_CHECK("UPDATE OF IN_STOCK - FAILS BECAUSE AMOUNT IDENTICAL");

  printf("\n  Populate stock_history with more entries\n");

  printf("\n  UPDATE in_stock SET amount = 40 WHERE part_no = '10002'");
  strcpy(strStmt, "UPDATE in_stock SET amount = 40 "
                  "WHERE part_no = '10002'");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("FOURTH UPDATE OF IN_STOCK");

  printf("\n  UPDATE in_stock SET amount = 10 WHERE part_no = '10002'");
  strcpy(strStmt, "UPDATE in_stock SET amount = 10 "
                  "WHERE part_no = '10002'");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("FIFTH UPDATE OF IN_STOCK");

  printf("\n  UPDATE in_stock SET amount = 25 WHERE part_no = '10002'");
  strcpy(strStmt, "UPDATE in_stock SET amount = 25 "
                  "WHERE part_no = '10002'");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("SIXTH UPDATE OF IN_STOCK");

  printf("\n\n  SELECT * FROM in_stock ORDER BY part_no");
  printf("\n    PART_NO AMOUNT REORDER_NO  STATUS\n");
  printf("    ------- ------ ---------- -------\n");

  strcpy(strStmt, "SELECT * FROM in_stock ORDER BY part_no");
  EXEC SQL PREPARE S6 FROM :strStmt;
  EMB_SQL_CHECK("PREPARE S6");

  EXEC SQL DECLARE C6 CURSOR FOR S6;

  EXEC SQL OPEN C6;
  EMB_SQL_CHECK("OPEN CURSOR");

  EXEC SQL FETCH C6 INTO :part_no, :amount, :reorder_no, :status;
  EMB_SQL_CHECK("FETCH CURSOR");

  while (sqlca.sqlcode != SQL_RC_W100)
  {
    printf("%11s %6d %10d %7s\n", part_no, amount, reorder_no, status);

    EXEC SQL FETCH C6 INTO :part_no, :amount, :reorder_no, :status;
    EMB_SQL_CHECK("FETCH CURSOR");
  }

  EXEC SQL CLOSE C6;
  EMB_SQL_CHECK("CLOSE CURSOR");

  printf("\n  SELECT * FROM stock_history ORDER BY timestamp");
  printf("\n    PART_NO STOCK_CHANGE ACTION TIMESTAMP\n");
  printf("    ------- ------------ ------ --------------------------\n");

  strcpy(strStmt, "SELECT * FROM stock_history ORDER BY timestamp");
  EXEC SQL PREPARE S7 FROM :strStmt;
  EMB_SQL_CHECK("PREPARE S7");

  EXEC SQL DECLARE C7 CURSOR FOR S7;

  EXEC SQL OPEN C7;
  EMB_SQL_CHECK("OPEN CURSOR");

  EXEC SQL FETCH C7 INTO :part_no, :stock_change, :action, :timestamp;
  EMB_SQL_CHECK("FETCH CURSOR");

  while (sqlca.sqlcode != SQL_RC_W100)
  {
    printf("%11s %12d %6s %.26s\n", part_no, stock_change, action, timestamp);

    EXEC SQL FETCH C7 INTO :part_no, :stock_change, :action, :timestamp;
    EMB_SQL_CHECK("FETCH CURSOR");
  }

  EXEC SQL CLOSE C7;
  EMB_SQL_CHECK("CLOSE CURSOR");

  printf("\n  Function Transaction_number determines the number of ");
  printf("transactions\n  associated with a particular part number ");
  printf("in stock_history\n");

  /* Function transaction_number determines number of transactions
     associated with particular part number in stock_history. */

  printf("\n  CREATE FUNCTION Transaction_number(part VARCHAR(5))\n");
  printf("    RETURNS INT\n");
  printf("    BEGIN ATOMIC\n");
  printf("      DECLARE a INT DEFAULT 0;\n");
  printf("      SELECT * FROM stock_history WHERE part_no = part;\n");
  printf("      GET DIAGNOSTICS a = ROW_COUNT;\n");
  printf("      RETURN a;\n");
  printf("  END\n");

  strcpy(strStmt, "CREATE FUNCTION Transaction_number(part VARCHAR(5)) "
                  "RETURNS INT "
                  "BEGIN ATOMIC "
                  "DECLARE a INT DEFAULT 0; "
                  "SELECT * FROM stock_history WHERE part_no = part; "
                  "GET DIAGNOSTICS a = ROW_COUNT; "
                  "RETURN a; "
                  "END");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("CREATE FUNCTION TRANSACTION_NUMBER");

  printf("\n  Table to hold each part number which occurs in the ");
  printf("stock_history\n  table and show the number of times it ");
  printf("appears\n\n");
  printf("  CREATE TABLE transaction_record(part_no VARCHAR(5) NOT NULL,\n");
  printf("                                  transaction_no INT)\n");

  /* table to hold each part number which occurs in the stock_history
  table and the number of times it appears */
  EXEC SQL CREATE TABLE transaction_record(part_no VARCHAR(5) NOT NULL,
                                           transaction_no INT);
  EMB_SQL_CHECK("CREATE TABLE TRANSACTION_RECORD");

  /* routine to fill transaction_record */

  printf("\n  BEGIN ATOMIC\n");
  printf("    FOR row AS\n");
  printf("      SELECT DISTINCT part_no, Transaction_number(part_no) AS p");
  printf("\n        FROM stock_history GROUP BY part_no\n");
  printf("    DO\n");
  printf("      INSERT INTO transaction_record VALUES ");
  printf("(row.part_no, row.p);\n");
  printf("    END FOR;\n");
  printf("  END\n");

  strcpy(strStmt, "BEGIN ATOMIC "
                  "FOR row AS "
                  "SELECT DISTINCT part_no, Transaction_number(part_no) "
                                  "AS p FROM stock_history GROUP BY part_no "
                  "DO "
                  "INSERT INTO transaction_record "
                  "VALUES(row.part_no, row.p); "
                  "END FOR; "
                  "END");
  EXEC SQL EXECUTE IMMEDIATE :strStmt;
  EMB_SQL_CHECK("EXECUTE SCRIPT");

  printf("\n  SELECT * FROM transaction_record ORDER BY part_no");
  printf("\n    PART_NO TRANSACTION_NO\n");
  printf("    ------- --------------\n");

  strcpy(strStmt, "SELECT * FROM transaction_record ORDER BY part_no");
  EXEC SQL PREPARE S8 FROM :strStmt;
  EMB_SQL_CHECK("PREPARE S8");

  EXEC SQL DECLARE C8 CURSOR FOR S8;

  EXEC SQL OPEN C8;
  EMB_SQL_CHECK("OPEN CURSOR");

  EXEC SQL FETCH C8 INTO :part_no, :transaction_no;
  EMB_SQL_CHECK("FETCH CURSOR");

  while (sqlca.sqlcode != SQL_RC_W100)
  {
    printf("%11s %14d\n", part_no, transaction_no);

    EXEC SQL FETCH C8 INTO :part_no, :transaction_no;
    EMB_SQL_CHECK("FETCH CURSOR");
  }

  EXEC SQL CLOSE C8;
  EMB_SQL_CHECK("CLOSE CURSOR");

  printf("\n  DROP FUNCTION Transaction_number\n");
  EXEC SQL DROP FUNCTION Transaction_number;
  EMB_SQL_CHECK("DROP FUNCTION TRANSACTION_NUMBER");

  printf("\n  DROP TRIGGER stock_status\n");
  EXEC SQL DROP TRIGGER stock_status;
  EMB_SQL_CHECK("DROP TRIGGER STOCK_STATUS");

  printf("\n  DROP TRIGGER history\n");
  EXEC SQL DROP TRIGGER history;
  EMB_SQL_CHECK("DROP TRIGGER HISTORY");

  printf("\n  DROP TABLE in_stock\n");
  EXEC SQL DROP TABLE in_stock;
  EMB_SQL_CHECK("DROP TABLE IN_STOCK");

  printf("\n  DROP TABLE stock_history\n");
  EXEC SQL DROP TABLE stock_history;
  EMB_SQL_CHECK("DROP TABLE STOCK_HISTORY");

  printf("\n  DROP TABLE transaction_record\n");
  EXEC SQL DROP TABLE transaction_record;
  EMB_SQL_CHECK("DROP TABLE TRANSACTION_HISTORY");

  return 0;

} /* Triggers */