/****************************************************************************
** (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: tbconstr.c                                       
**                                                                        
** SAMPLE: How to create, use and drop constraints associated with tables
**
** CLI FUNCTIONS USED:
**         SQLAllocHandle -- Allocate Handle
**         SQLBindCol -- Bind a Column to an Application Variable or
**                       LOB locator
**         SQLExecDirect -- Execute a Statement Directly
**         SQLFetch -- Fetch Next Row
**         SQLForeignKeys -- Get the List of Foreign Key Columns
**         SQLFreeHandle -- Free Handle Resources
**         SQLPrimaryKeys -- Get Primary Key Columns of a Table
**         SQLSetConnectAttr -- Set Connection Attributes
**         SQLSpecialColumns -- Get Special (Row Identifier) Columns
**         SQLStatistics -- Get Index and Statistics Information
**                          for a Base Table
**
**                           
*****************************************************************************
**
** For more information on the sample programs, see the README file.
**
** For information on developing CLI applications, see the CLI Guide
** and Reference.
**
** 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 <sqlcli1.h>
#include "utilcli.h" /* Header file for CLI sample code */

int CnDefine(SQLHANDLE);
int CnListPrimaryKeys(SQLHANDLE);
int CnListForeignKeys(SQLHANDLE);
int CnListSpecialColumns(SQLHANDLE);
int CnListIndexColumns(SQLHANDLE);
int CnCleanUp(SQLHANDLE);
int Cn_NOT_NULL_Show(SQLHANDLE);
int Cn_UNIQUE_Show(SQLHANDLE);
int Cn_PRIMARY_KEY_Show(SQLHANDLE);
int Cn_CHECK_Show(SQLHANDLE);
int Cn_CHECK_INFO_Show(SQLHANDLE);
int Cn_WITH_DEFAULT_Show(SQLHANDLE);
int Cn_FK_OnInsertShow(SQLHANDLE);
int Cn_FK_ON_UPDATE_NO_ACTION_Show(SQLHANDLE);
int Cn_FK_ON_UPDATE_RESTRICT_Show(SQLHANDLE);
int Cn_FK_ON_DELETE_CASCADE_Show(SQLHANDLE);
int Cn_FK_ON_DELETE_SET_NULL_Show(SQLHANDLE);
int Cn_FK_ON_DELETE_NO_ACTION_Show(SQLHANDLE);
int Cn_FK_ON_DELETE_RESTRICT_Show(SQLHANDLE);

/* support functions */
int FK_TwoTablesCreate(SQLHANDLE);
int FK_TwoTablesDisplay(SQLHANDLE);
int FK_TwoTablesDrop(SQLHANDLE);
int FK_Create(SQLHANDLE, char *);
int FK_Drop(SQLHANDLE);

int main(int argc, char *argv[])
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE henv; /* environment handle */
  SQLHANDLE hdbc; /* connection handle */

  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("\nTHIS SAMPLE SHOWS HOW TO WORK WITH CONSTRAINTS.\n");

  /* initialize the CLI 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;
  }

  /* create tables that have constraints */
  rc = CnDefine(hdbc);

  /* list the primary keys of a specified table */
  rc = CnListPrimaryKeys(hdbc);

  /* list the foreign keys for a specified table */
  rc = CnListForeignKeys(hdbc);

  /* list the special (row identifier) columns for a specified table */
  rc = CnListSpecialColumns(hdbc);

  /* list the index columns for a specified table */
  rc = CnListIndexColumns(hdbc);

  /* clean up the tables created in CnDefine */
  rc = CnCleanUp(hdbc);

  /* show a NOT NULL constraint */
  rc = Cn_NOT_NULL_Show(hdbc);

  /* show a UNIQUE constraint */
  rc = Cn_UNIQUE_Show(hdbc);

  /* show a PRIMARY KEY constraint */
  rc = Cn_PRIMARY_KEY_Show(hdbc);

  /* show a CHECK constraint */
  rc = Cn_CHECK_Show(hdbc);

  /* show INFORMATIONAL constraint */ 
  rc = Cn_CHECK_INFO_Show(hdbc);

  /* show a 'WITH DEFAULT' constraint */
  rc = Cn_WITH_DEFAULT_Show(hdbc);

  printf("\n#####################################################\n"
         "#    Create tables for FOREIGN KEY sample functions #\n"
         "#####################################################\n");
  
  /* create tables for FOREIGN KEY sample functions  */
  rc = FK_TwoTablesCreate(hdbc);

  /* show how a FOREIGN KEY works on insert */
  rc = Cn_FK_OnInsertShow(hdbc);

  /* show 'ON UPDATE NO ACTION' foreign key constraint */
  rc = Cn_FK_ON_UPDATE_NO_ACTION_Show(hdbc);

  /* show 'ON UPDATE RESTRICT' foreign key constraint */
  rc = Cn_FK_ON_UPDATE_RESTRICT_Show(hdbc);

  /* show an 'ON DELETE CASCADE' foreign key constraint */
  rc = Cn_FK_ON_DELETE_CASCADE_Show(hdbc);

  /* show an 'ON DELETE SET NULL' foreign key constraint */
  rc = Cn_FK_ON_DELETE_SET_NULL_Show(hdbc);

  /* show an 'ON DELETE NO ACTION' foreign key constraint */
  rc = Cn_FK_ON_DELETE_NO_ACTION_Show(hdbc);
  
  printf("\n########################################################\n"
         "# Drop tables created for FOREIGN KEY sample functions #\n"
         "########################################################\n");

  /* drop tables created for FOREIGN KEY sample functions */
  rc = FK_TwoTablesDrop(hdbc);

  /* terminate the CLI application by calling a helper
     utility function defined in utilcli.c */
  rc = CLIAppTerm(&henv, &hdbc, dbAlias);

  return rc;
} /* main */

/* create tables that have constraints */
int CnDefine(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  /* SQL CREATE TABLE statements to be executed */
  SQLCHAR *stmt1 = (SQLCHAR *)
    "CREATE TABLE sch.dept(deptno CHAR(3) NOT NULL PRIMARY KEY, "
    "                      deptname VARCHAR(32))";

  SQLCHAR *stmt2 = (SQLCHAR *)
    "CREATE TABLE sch.emp(empno CHAR(7) NOT NULL PRIMARY KEY, "
    "                     deptno CHAR(3) NOT NULL, "
    "                     sex CHAR(1) WITH DEFAULT 'M', "
    "                     salary DECIMAL(7,2) WITH DEFAULT, "
    "  CONSTRAINT check1 CHECK(sex IN('M', 'F')), "
    "  CONSTRAINT check2 CHECK(salary < 70000.00), "
    "  CONSTRAINT fk1 FOREIGN KEY (deptno) REFERENCES sch.dept(deptno))";

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLSetConnectAttr\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("TO CREATE TABLES WITH CONSTRAINTS:\n");

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

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

  /* create the first table */
  printf("\n  Directly execute the statement\n");
  printf("    CREATE TABLE sch.dept(");
  printf("deptno CHAR(3) NOT NULL PRIMARY KEY,\n");
  printf("                          deptname VARCHAR(32))\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* create the second table */
  printf("\n  Directly execute the statement\n");
  printf("    CREATE TABLE sch.emp(");
  printf("empno CHAR(7) NOT NULL PRIMARY KEY,\n");
  printf("                         ");
  printf("deptno CHAR(3) NOT NULL,\n");
  printf("                         ");
  printf("sex CHAR(1) WITH DEFAULT 'M',\n");
  printf("                         ");
  printf("salary DECIMAL(7,2) WITH DEFAULT,\n");
  printf("      CONSTRAINT check1 CHECK(sex IN('M', 'F')),\n");
  printf("      CONSTRAINT check2 CHECK(salary < 70000.00),\n");
  printf("      CONSTRAINT fk1 ");
  printf("FOREIGN KEY (deptno) REFERENCES sch.dept(deptno))\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  return rc;
} /* CnDefine */

/* drop the tables created in the CnDefine function */
int CnCleanUp(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  /* SQL DROP statements to be executed */
  SQLCHAR *stmt1 = (SQLCHAR *)"DROP TABLE sch.dept";
  SQLCHAR *stmt2 = (SQLCHAR *)"DROP TABLE sch.emp";

  printf("\nDrop the tables created in CnDefine.\n");

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

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

  /* drop the first table */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* drop the second table */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  return rc;
} /* CnCleanUp */

/* list the primary keys of a specified table */
int CnListPrimaryKeys(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  /* specifications of the table to look for */
  SQLCHAR tbSchema[] = "SCH";
  SQLCHAR tbName[] = "DEPT";

  struct
  {
    SQLINTEGER ind;
    SQLCHAR val[129];
  }
  pkColumnName, pkName;

  struct
  {
    SQLINTEGER ind;
    SQLSMALLINT val;
  }
  pkColumnPos;

  SQLINTEGER rowNb = 0;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLSetConnectAttr\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLPrimaryKeys\n");
  printf("  SQLBindCol\n");
  printf("  SQLFetch\n");
  printf("  SQLFreeHandle\n");
  printf("TO LIST THE PRIMARY KEYS FOR A SPECIFIED TABLE:\n");

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

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

  /* call SQLPrimaryKeys */
  printf("\n  Call SQLPrimaryKeys for the table %s.%s\n",
         tbSchema, tbName);

  /* get the primary key columns of a table */
  cliRC = SQLPrimaryKeys(hstmt, NULL, 0, tbSchema, SQL_NTS, tbName, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 4 to variable */
  cliRC = SQLBindCol(hstmt,
                     4,
                     SQL_C_CHAR,
                     (SQLPOINTER)pkColumnName.val,
                     129,
                     &pkColumnName.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 5 to variable */
  cliRC = SQLBindCol(hstmt,
                     5,
                     SQL_C_SHORT,
                     (SQLPOINTER)&pkColumnPos.val,
                     0,
                     &pkColumnPos.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 6 to variable */
  cliRC = SQLBindCol(hstmt,
                     6,
                     SQL_C_CHAR,
                     (SQLPOINTER)pkName.val,
                     129,
                     &pkName.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* fetch each row, and display */
  printf("\n  Fetch each row and display.\n");

  /* fetch next row */
  cliRC = SQLFetch(hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  if (cliRC == SQL_NO_DATA_FOUND)
  {
    printf("\nData not found.\n");
  }
  while (cliRC != SQL_NO_DATA_FOUND)
  {
    rowNb = rowNb + 1;
    printf("  ------- row number %lu --------\n", rowNb);
    printf("  Primary Key Name: %s\n", pkName.val);
    printf("  Primary Key Column Name: %s\n", pkColumnName.val);
    printf("  Primary Key Column Position: %d\n", pkColumnPos.val);

    /* fetch next row */
    cliRC = SQLFetch(hstmt);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
  }

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

  return rc;
} /* CnListPrimaryKeys */

/* list the foreign keys for a specified table */
int CnListForeignKeys(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  /* specifications of the table to look for */
  SQLCHAR tbSchema[] = "SCH";
  SQLCHAR tbName[] = "DEPT";

  struct
  {
    SQLINTEGER ind;
    SQLCHAR val[129];
  }
  pkTableSch, pkTableName, pkColumnName, pkName;

  struct
  {
    SQLINTEGER ind;
    SQLCHAR val[129];
  }
  fkTableSch, fkTableName, fkColumnName, fkName;

  struct
  {
    SQLINTEGER ind;
    SQLSMALLINT val;
  }
  deleteRule, updateRule;

  SQLINTEGER rowNb = 0;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLSetConnectAttr\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLForeignKeys\n");
  printf("  SQLBindCol\n");
  printf("  SQLFetch\n");
  printf("  SQLFreeHandle\n");
  printf("TO LIST THE FOREIGN KEYS FOR A SPECIFIED TABLE:\n");

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

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

  /* call SQLForeignKeys */
  printf("\n  Call SQLForeignKeys for the table %s.%s\n",
         tbSchema, tbName);

  /* get the list of foreign key columns */
  cliRC = SQLForeignKeys(hstmt,
                         NULL,
                         0,
                         tbSchema,
                         SQL_NTS,
                         tbName,
                         SQL_NTS,
                         NULL,
                         0,
                         NULL,
                         SQL_NTS,
                         NULL,
                         SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 2 to variable */
  cliRC = SQLBindCol(hstmt,
                     2,
                     SQL_C_CHAR,
                     (SQLPOINTER)pkTableSch.val,
                     129,
                     &pkTableSch.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 3 to variable */
  cliRC = SQLBindCol(hstmt,
                     3,
                     SQL_C_CHAR,
                     (SQLPOINTER)pkTableName.val,
                     129,
                     &pkTableName.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 4 to variable */
  cliRC = SQLBindCol(hstmt,
                     4,
                     SQL_C_CHAR,
                     (SQLPOINTER)pkColumnName.val,
                     129,
                     &pkColumnName.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 6 to variable */
  cliRC = SQLBindCol(hstmt,
                     6,
                     SQL_C_CHAR,
                     (SQLPOINTER)fkTableSch.val,
                     129,
                     &fkTableSch.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 7 to variable */
  cliRC = SQLBindCol(hstmt,
                     7,
                     SQL_C_CHAR,
                     (SQLPOINTER)fkTableName.val,
                     129,
                     &fkTableName.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 8 to variable */
  cliRC = SQLBindCol(hstmt,
                     8,
                     SQL_C_CHAR,
                     (SQLPOINTER)fkColumnName.val,
                     129,
                     &fkColumnName.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 10 to variable */
  cliRC = SQLBindCol(hstmt,
                     10,
                     SQL_C_SHORT,
                     (SQLPOINTER)&updateRule.val,
                     0,
                     &updateRule.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 11 to variable */
  cliRC = SQLBindCol(hstmt,
                     11,
                     SQL_C_SHORT,
                     (SQLPOINTER)&deleteRule.val,
                     0,
                     &deleteRule.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 12 to variable */
  cliRC = SQLBindCol(hstmt,
                     12,
                     SQL_C_CHAR,
                     (SQLPOINTER)fkName.val,
                     129,
                     &fkName.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 13 to variable */
  cliRC = SQLBindCol(hstmt,
                     13,
                     SQL_C_CHAR,
                     (SQLPOINTER)pkName.val,
                     129,
                     &pkName.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* fetch each row and display */
  printf("\n  Fetch each row and display.\n");

  /* fetch next row */
  cliRC = SQLFetch(hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  if (cliRC == SQL_NO_DATA_FOUND)
  {
    printf("\n  Data not found.\n");
  }
  while (cliRC != SQL_NO_DATA_FOUND)
  {
    rowNb = rowNb + 1;
    printf("  ------- row number %lu --------\n", rowNb);
    printf("  Foreign Key Name: %s\n", fkName.val);
    printf("  Primary Key Name: %s\n", pkName.val);
    printf("  Foreign Key Column: %s.%s.%s\n",
           fkTableSch.val, fkTableName.val, fkColumnName.val);
    printf("  Primary Key Column: %s.%s.%s\n",
           pkTableSch.val, pkTableName.val, pkColumnName.val);

    printf("  Update Rule: ");
    switch (updateRule.val)
    {
      case SQL_RESTRICT:
        printf("RESTRICT\n"); /* always for IBM DBMSs */
        break;
      case SQL_CASCADE:
        printf("CASCADE\n"); /* non-IBM only */
        break;
      default:
        printf("SET NULL\n");
        break;
    }

    printf("  Delete Rule: ");
    switch (deleteRule.val)
    {
      case SQL_RESTRICT:
        printf("RESTRICT\n"); /* always for IBM DBMSs */
        break;
      case SQL_CASCADE:
        printf("CASCADE\n"); /* non-IBM only */
        break;
      case SQL_NO_ACTION:
        printf("NO ACTION\n"); /* non-IBM only */
        break;
      default:
        printf("SET NULL\n");
        break;
    }

    /* fetch next row */
    cliRC = SQLFetch(hstmt);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
  }

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

  return rc;
} /* CnListForeignKeys */

/* list the special (row identifier) columns for a specified table */
int CnListSpecialColumns(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */

  struct
  {
    SQLINTEGER ind;
    SQLCHAR val[129];
  }
  colName, colType;

  struct
  {
    SQLINTEGER ind;
    SQLCHAR val[255];
  }
  colRemarks;

  struct
  {
    SQLINTEGER ind;
    SQLINTEGER val;
  }
  colPrecision;

  struct
  {
    SQLINTEGER ind;
    SQLSMALLINT val;
  }
  colScale;

  /* specifications of the table to look for */
  SQLCHAR tbSchema[] = "SCH";
  SQLCHAR tbName[] = "DEPT";

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLSetConnectAttr\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLSpecialColumns\n");
  printf("  SQLBindCol\n");
  printf("  SQLFetch\n");
  printf("  SQLFreeHandle\n");
  printf("TO LIST SPECIAL (ROW IDENTIFIER) COLUMNS FOR A SPECIFIED TABLE:\n");

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

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

  /* call SQLSpecialColumns */
  printf("\n  Call SQLSpecialColumns for the table %s.%s\n",
         tbSchema, tbName);

  /* get special columns */
  cliRC = SQLSpecialColumns(hstmt,
                            SQL_BEST_ROWID,
                            NULL,
                            0,
                            tbSchema,
                            SQL_NTS,
                            tbName,
                            SQL_NTS,
                            SQL_SCOPE_CURROW,
                            SQL_NULLABLE);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 2 to variable */
  cliRC = SQLBindCol(hstmt, 2, SQL_C_CHAR, colName.val, 129, &colName.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 4 to variable */
  cliRC = SQLBindCol(hstmt, 4, SQL_C_CHAR, colType.val, 129, &colType.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 5 to variable */
  cliRC = SQLBindCol(hstmt,
                     5,
                     SQL_C_LONG,
                     (SQLPOINTER)&colPrecision.val,
                     sizeof(colPrecision.val),
                     &colPrecision.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 7 to variable */
  cliRC = SQLBindCol(hstmt,
                     7,
                     SQL_C_SHORT,
                     (SQLPOINTER)&colScale.val,
                     sizeof(colScale.ind),
                     &colScale.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* fetch each row and display */
  printf("\n  Fetch each row and display.\n");

  /* fetch next row */
  cliRC = SQLFetch(hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  if (cliRC == SQL_NO_DATA_FOUND)
  {
    printf("\n  Data not found.\n");
  }
  while (cliRC != SQL_NO_DATA_FOUND)
  {
    printf("    %-10.10s", colName.val);

    printf(", %s", colType.val);

    if (colPrecision.ind != SQL_NULL_DATA)
    {
      printf(" (%ld", colPrecision.val);
    }
    else
    {
      printf("(\n");
    }

    if (colScale.ind != SQL_NULL_DATA)
    {
      printf(", %d)\n", colScale.val);
    }
    else
    {
      printf(")\n");
    }

    /* fetch next row */
    cliRC = SQLFetch(hstmt);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
  }

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

  return rc;
} /* CnListSpecialColumns */

/* list the index columns for a specified table */
int CnListIndexColumns(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  /* specifications of the table to look for */
  SQLCHAR tbSchema[] = "SCH";
  SQLCHAR tbName[] = "DEPT";

  struct
  {
    SQLINTEGER ind;
    SQLCHAR val[129];
  }
  columnName, indexName;

  struct
  {
    SQLINTEGER ind;
    SQLSMALLINT val;
  }
  type;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLSetConnectAttr\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLStatistics\n");
  printf("  SQLBindCol\n");
  printf("  SQLFetch\n");
  printf("  SQLFreeHandle\n");
  printf("TO LIST INDEX COLUMNS FOR A SPECIFIED TABLE:\n");

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

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

  /* call SQLStatistics */
  printf("\n  Call SQLStatistics for the table %s.%s\n", tbSchema, tbName);

  /* get index and statistics information for a base table */
  cliRC = SQLStatistics(hstmt,
                        NULL,
                        0,
                        tbSchema,
                        SQL_NTS,
                        tbName,
                        SQL_NTS,
                        SQL_INDEX_UNIQUE,
                        SQL_QUICK);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 6 to variable */
  cliRC = SQLBindCol(hstmt,
                     6,
                     SQL_C_CHAR,
                     (SQLPOINTER)indexName.val,
                     129,
                     &indexName.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 7 to variable */
  cliRC = SQLBindCol(hstmt,
                     7,
                     SQL_C_SHORT,
                     (SQLPOINTER)&type.val,
                     0,
                     &type.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* bind column 9 to variable */
  cliRC = SQLBindCol(hstmt,
                     9,
                     SQL_C_CHAR,
                     (SQLPOINTER)columnName.val,
                     129,
                     &columnName.ind);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* fetch each row and display */
  printf("\n  Fetch each row and display.\n");

  /* fetch next row */
  cliRC = SQLFetch(hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  if (cliRC == SQL_NO_DATA_FOUND)
  {
    printf("\n  Data not found.\n");
  }
  while (cliRC != SQL_NO_DATA_FOUND)
  {
    if (type.val != SQL_TABLE_STAT)
    {
      printf("    Column    : %-10s\n", columnName.val);
      printf("    Index Name: %s\n", indexName.val);
    }

    /* fetch next row */
    cliRC = SQLFetch(hstmt);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
  }

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

  return rc;
} /* CnListIndexColumns */

/* show how to use a NOT NULL constraint */
int Cn_NOT_NULL_Show(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  
  /* create table */
  SQLCHAR *stmt1 = (SQLCHAR *)"CREATE TABLE emp_sal(lastname VARCHAR(10) "
                              "NOT NULL, firstname VARCHAR(10), "
                              "salary DECIMAL(7, 2))";

  /* insert into the table */
  SQLCHAR *stmt2 = (SQLCHAR *)"INSERT INTO emp_sal "
                              "  VALUES(NULL, 'PHILIP', 17000.00)";
  
  /* drop table */ 
  SQLCHAR *stmt3 = (SQLCHAR *)"DROP TABLE emp_sal";

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO SHOW A 'NOT NULL' CONSTRAINT:\n");

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

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

  /* create table */
  printf("\n  Directly execute the statement\n");
  printf("    CREATE TABLE emp_sal(lastname VARCHAR(10) NOT NULL,\n"
         "                         firstname VARCHAR(10),\n"
         "                         salary DECIMAL(7, 2))\n");
   
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

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

  printf("\n    INSERT INTO emp_sal VALUES(NULL, 'PHILIP', 17000.00)\n");
   
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  
  /* display the expected error */
  printf("\n-- The following error report is expected! --");
  EX_STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  printf("\n    DROP TABLE emp_sal\n");
   
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt3, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  return rc;
} /* Cn_NOT_NULL_Show */
 
/* show how to use UNIQUE constraint */
int Cn_UNIQUE_Show(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  
  /* create table */
  SQLCHAR *stmt1 = (SQLCHAR *)"CREATE TABLE emp_sal ("
                              "lastname VARCHAR(10) NOT NULL, "
                              "firstname VARCHAR(10) NOT NULL, "
                              "salary DECIMAL(7, 2), "
                              "CONSTRAINT unique_cn UNIQUE "
                              "(lastname, firstname))";
  
  /* insert into the table */
  SQLCHAR *stmt2 = (SQLCHAR *)"INSERT INTO emp_sal VALUES "
                              "('SMITH', 'PHILIP', 17000.00), "
                              "('SMITH', 'PHILIP', 21000.00)";
  
  /* drop constraint */
  SQLCHAR *stmt3 = (SQLCHAR *)"ALTER TABLE emp_sal "
                              "DROP CONSTRAINT unique_cn";
  
  /* drop table */
  SQLCHAR *stmt4 = (SQLCHAR *)"DROP TABLE emp_sal";

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO SHOW A 'UNIQUE' CONSTRAINT:\n");

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

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

  printf("\n  Directly execute the statement\n");
  printf("    CREATE TABLE emp_sal(lastname VARCHAR(10) NOT NULL,\n"
         "                         firstname VARCHAR(10) NOT NULL,\n"
         "                         salary DECIMAL(7, 2),\n"
         "    CONSTRAINT unique_cn UNIQUE(lastname, firstname))\n");
   
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

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

  printf("\n    INSERT INTO emp_sal VALUES('SMITH', 'PHILIP', 17000.00),\n"
         "                              ('SMITH', 'PHILIP', 21000.00) \n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  
  /* display the expected error */
  printf("\n-- The following error report is expected! --");
  EX_STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  printf("\n    ALTER TABLE emp_sal DROP CONSTRAINT unique_cn\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt3, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

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

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

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt4, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  return rc;
} /* Cn_UNIQUE_Show */

/* show how to use PRIMARY key constraint */
int Cn_PRIMARY_KEY_Show(SQLHANDLE hdbc)
{
  
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  
  /* create table */
  SQLCHAR *stmt1 = (SQLCHAR *)"CREATE TABLE emp_sal( "
                              "lastname VARCHAR(10) NOT NULL, "
                              "firstname VARCHAR(10) NOT NULL, " 
                              "salary DECIMAL(7, 2), "
                              "CONSTRAINT pk_cn PRIMARY KEY "
                              "(lastname, firstname))";

  /* insert into the table */
  SQLCHAR *stmt2 = (SQLCHAR *)"INSERT INTO emp_sal VALUES "
                              "('SMITH', 'PHILIP', 17000.00), "
                              "('SMITH', 'PHILIP', 21000.00)";

  /* drop constraint */
  SQLCHAR *stmt3 = (SQLCHAR *)"ALTER TABLE emp_sal DROP CONSTRAINT pk_cn";

  /* drop table */
  SQLCHAR *stmt4 = (SQLCHAR *)"DROP TABLE emp_sal";
  
  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO SHOW A 'PRIMARY KEY' CONSTRAINT:\n");

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

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

  printf("\n  Directly execute the statement\n");
  printf("    CREATE TABLE emp_sal(lastname VARCHAR(10) NOT NULL,\n"
         "                         firstname VARCHAR(10) NOT NULL,\n"
         "                         salary DECIMAL(7, 2),\n"
         "    CONSTRAINT pk_cn PRIMARY KEY(lastname, firstname))\n");
   
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

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

  printf("\n    INSERT INTO emp_sal VALUES('SMITH', 'PHILIP', 17000.00),\n"
         "                              ('SMITH', 'PHILIP', 21000.00) \n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  
  /* display the expected error */
  printf("\n-- The following error report is expected! --");
  EX_STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  printf("\n    ALTER TABLE emp_sal DROP CONSTRAINT pk_cn\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt3, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

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

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

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt4, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  return rc;
} /* Cn_PRIMARY_KEY_Show */

/* show how to use CHECK constraint */
int Cn_CHECK_Show(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  
  /* create table */
  SQLCHAR *stmt1 = (SQLCHAR *)"CREATE TABLE emp_sal(lastname VARCHAR(10), "
                              "                     firstname VARCHAR(10),"
                              "                     salary DECIMAL(7, 2), "
                              "CONSTRAINT check_cn CHECK(salary < 25000.00))";

  /* insert table */    
  SQLCHAR *stmt2 = (SQLCHAR *)"INSERT INTO emp_sal VALUES "
                              "('SMITH', 'PHILIP', 27000.00)";
  
  /* drop constraint */
  SQLCHAR *stmt3 = (SQLCHAR *)"ALTER TABLE emp_sal "
                              "DROP CONSTRAINT check_cn";

  /* drop table */
  SQLCHAR *stmt4 = (SQLCHAR *)"DROP TABLE emp_sal";  

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO SHOW A 'CHECK' CONSTRAINT:\n");

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

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

  printf("\n  Directly execute the statement\n");
  printf("    CREATE TABLE emp_sal(lastname VARCHAR(10),\n"
         "                         firstname VARCHAR(10),\n"
         "                         salary DECIMAL(7, 2),\n"
         "    CONSTRAINT check_cn CHECK(salary < 25000.00))\n");
 
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

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

  printf("\n    INSERT INTO emp_sal VALUES('SMITH', 'PHILIP', 27000.00)\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  
  /* display the expected error */
  printf("\n-- The following error report is expected! --");
  EX_STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  printf("\n    ALTER TABLE emp_sal DROP CONSTRAINT check_cn\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt3, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

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

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

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt4, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  return rc;
} /* Cn_CHECK_Show */

/* show how to use INFORMATIONAL constraint */ 
int Cn_CHECK_INFO_Show(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  
  /* create table */
  SQLCHAR *stmt1 = (SQLCHAR *)"CREATE TABLE empl( "
                              "  empno INTEGER NOT NULL PRIMARY KEY, "
                              "  name VARCHAR(10), "
                              "  firstname VARCHAR(20), "
                              "  salary INTEGER "
                              "CONSTRAINT minsalary "
                              "  CHECK (salary >= 25000)"
                              "  NOT ENFORCED"
                              "  ENABLE QUERY OPTIMIZATION)";
  
   /* insert into the table */
   SQLCHAR *stmt2 = (SQLCHAR *)"INSERT INTO empl "
                               "VALUES(1, 'SMITH', 'PHILIP', 1000)";
   
   /* alter the constraint to make it ENFORCED by database manager */
   SQLCHAR *stmt3 = (SQLCHAR *)"ALTER TABLE empl "
                               "ALTER CHECK minsalary ENFORCED"; 

  /* delete entries from empl table */
  SQLCHAR *stmt4 = (SQLCHAR *)"DELETE FROM empl";

  /* drop the table */
  SQLCHAR *stmt5 = (SQLCHAR *)"DROP TABLE empl";
 
  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO SHOW AN 'INFORMATIONAL' CONSTRAINT:\n");

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

  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC);
  
  /* create table */ 
  printf("\n  Directly execute the statement\n"); 
  printf("    CREATE TABLE empl(empno INTEGER NOT NULL PRIMARY KEY,\n"
         "                     name VARCHAR(10),\n"
         "                     firstname VARCHAR(20),\n"
         "                     salary INTEGER CONSTRAINT minsalary\n"
         "                            CHECK (salary >= 25000)\n"
         "                            NOT ENFORCED\n"
         "                            ENABLE QUERY OPTIMIZATION)\n");     
  
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  /* insert data that doesn't satisfy the constraint 'minsalary'. 
     database manager does not enforce the constraint for IUD operations */
  printf("\nTO SHOW NOT ENFORCED OPTION\n");

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

  printf("    INSERT INTO empl VALUES(1, 'SMITH', 'PHILIP', 1000)\n\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  /* alter the constraint to make it ENFORCED by database manager */ 
  printf("Alter the constraint to make it ENFORCED by database manager\n");

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

  printf("    ALTER TABLE empl ALTER CHECK minsalary ENFORCED\n");
 
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt3, SQL_NTS);
  
  /* display the expected error */
  printf("\n-- The following error report is expected! --");
  EX_STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

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

  /* delete entries from empl table */
  printf("\n    DELETE FROM empl\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt4, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* free the statement handle */
  cliRC = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);    
 
  /* alter the constraint to make it ENFORCED by database manager */ 
  printf("\n\nTO SHOW ENFORCED OPTION\n");
 
  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC);

  printf("    ALTER TABLE empl ALTER CHECK minsalary ENFORCED\n");
 
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt3, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  /* insert table with data not conforming to the constraint 'minsalary'
     database manager enforces the constraint for IUD operations */
  
  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC);

  printf("\n    INSERT INTO empl VALUES(1, 'SMITH', 'PHILIP', 1000)\n");
  
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  
  /* display the expected error */
  printf("\n-- The following error report is expected! --");
  EX_STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

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

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt5, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  return rc;
}  /* Cn_CHECK_INFO_Show */

/* show how to use  WITH DEFAULT constraint */
int Cn_WITH_DEFAULT_Show(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */

  struct
  {
    SQLINTEGER ind;
    SQLSMALLINT val;
  }
  salary; /* variable to get data from the SALARY column */

  struct
  {
    SQLINTEGER ind;
    SQLCHAR val[15];
  }
  firstname; /* variable to get data from the FIRSTNAME column */
  
  struct
  {
    SQLINTEGER ind;
    SQLCHAR val[15];
  }
  lastname; /* variable to get data from the FIRSTNAME column */
  
  /* create table */
  SQLCHAR *stmt1 = (SQLCHAR *)"CREATE TABLE emp_sal(lastname VARCHAR(10),"
                              "firstname VARCHAR(10), salary DECIMAL(7, 2) "
                              "WITH DEFAULT 17000.00)";
  /* insert into the table */
  SQLCHAR *stmt2 = (SQLCHAR *)"INSERT INTO emp_sal(lastname, firstname)"
                             "  VALUES('SMITH', 'PHILIP'),"
                             "        ('PARKER', 'JOHN'),"
                             "        ('PEREZ', 'MARIA')";

  /* display the contents of the table */
  SQLCHAR *stmt3 = (SQLCHAR *)"SELECT firstname, lastname, salary"
                              "  FROM emp_sal";

  /* drop the table */
  SQLCHAR *stmt4 = (SQLCHAR *)"DROP TABLE emp_sal";

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFetch\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLGetData\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO SHOW A 'WITH DEFAULT' CONSTRAINT:\n");

  /* set AUTOCOMMIT on */
  cliRC = SQLSetConnectAttr(hdbc,
                            SQL_ATTR_AUTOCOMMIT,
                            (SQLPOINTER)SQL_AUTOCOMMIT_ON,
                            SQL_NTS);
  DBC_HANDLE_CHECK(hdbc, cliRC);
  
  /* create table */ 
  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC);
  
  printf("\n  Directly execute the statement\n"); 
  printf("    CREATE TABLE emp_sal(lastname VARCHAR(10),\n"
         "                         firstname VARCHAR(10),\n"
         "                         "
         "salary DECIMAL(7, 2) WITH DEFAULT 17000.00)\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* free the statement handle */
  cliRC = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);    
 
  /* insert table */
  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC);

  printf("\n    INSERT INTO emp_sal(lastname, firstname)\n"
         "      VALUES('SMITH', 'PHILIP'),\n"
         "            ('PARKER', 'JOHN'),\n"
         "            ('PEREZ', 'MARIA')\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC);
  
  printf("\n    SELECT firstname, lastname, salary FROM emp_sal\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt3, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  printf("    FIRSTNAME  LASTNAME   SALARY  \n");
  printf("    ---------- ---------- --------\n");

  /* fetch each row, and display */
  cliRC = SQLFetch(hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  if (cliRC == SQL_NO_DATA_FOUND)
  {
    printf("\n  Data not found.\n");
  }
  while (cliRC != SQL_NO_DATA_FOUND)
  {
    /* use SQLGetData to get the results */
    /* get data from column 1 */
    cliRC = SQLGetData(hstmt,
                       1,
                       SQL_C_CHAR,
                       lastname.val,
                       15,
                       &lastname.ind);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

    /* get data from column 2 */
    cliRC = SQLGetData(hstmt,
                       2,
                       SQL_C_CHAR,
                       firstname.val,
                       15,
                       &firstname.ind);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

    /* get data from column 3 */
    cliRC = SQLGetData(hstmt,
                       3,
                       SQL_C_SHORT,
                       &salary.val,
                       0,
                       &salary.ind);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

    /* display */
    printf("    %-10s %-10s %-7.2f\n", firstname.val, 
      lastname.val, (float)salary.val);
    
    /* fetch next row */
    cliRC = SQLFetch(hstmt);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
  }

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

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

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt4, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  return rc;
} /* Cn_WITH_DEFAULT_Show */ 

/* display the contents of two tables */ 
int FK_TwoTablesDisplay(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */

  struct
  {
    SQLINTEGER ind;
    SQLCHAR val[4];
  }
  deptno; /* variable to get data from the DEPTNO column */

  struct
  {
    SQLINTEGER ind;
    SQLCHAR val[15];
  }
  deptname; /* variable to get data from the DEPTNAME column */

  struct
  {
    SQLINTEGER ind;
    SQLCHAR val[5];
  }
  empno; /* variable to get data from the EMPNO column */

  struct
  {
    SQLINTEGER ind;
    SQLCHAR val[15];
  }
  empname; /* variable to get data from the EMPNAME column */
  
  struct
  {
    SQLINTEGER ind;
    SQLCHAR val[4];
  }
  dept_no; /* variable to get data from the DEPT_NO column */

  
  SQLCHAR *stmt1 = (SQLCHAR *)"SELECT deptno, deptname FROM tab_dept" ;
  SQLCHAR *stmt2 = (SQLCHAR *)"SELECT empno, empname, dept_no FROM empl";
 
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFetch\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLGetData\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO DISPLAY THE CONTENTS OF THE TABLES:\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); 
  printf("\n    SELECT deptno, deptname FROM tab_dept\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  printf("    DEPTNO  DEPTNAME      \n");
  printf("    ------- --------------\n");

  /* fetch each row, and display */
  cliRC = SQLFetch(hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  if (cliRC == SQL_NO_DATA_FOUND)
  {
    printf("\n  Data not found.\n");
  }
  while (cliRC != SQL_NO_DATA_FOUND)
  {
    /* use SQLGetData to get the results */
    /* get data from column 1 */
    cliRC = SQLGetData(hstmt,
                       1,
                       SQL_C_CHAR,
                       &deptno.val,
                       4,
                       &deptno.ind);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

    /* get data from column 2 */
    cliRC = SQLGetData(hstmt,
                       2,
                       SQL_C_CHAR,
                       deptname.val,
                       15,
                       &deptname.ind);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

    printf("    %-7s %-20s\n", deptno.val, deptname.val);
       
    /* fetch next row */
    cliRC = SQLFetch(hstmt);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
  }

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

  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC); 
  printf("\n    SELECT empno, empname, dept_no FROM empl\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  printf("    EMPNO EMPNAME    DEPT_NO\n");
  printf("    ----- ---------- -------\n");

  /* fetch each row, and display */
  cliRC = SQLFetch(hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  if (cliRC == SQL_NO_DATA_FOUND)
  {
    printf("\n  Data not found.\n");
  }
  while (cliRC != SQL_NO_DATA_FOUND)
  {
    /* use SQLGetData to get the results */
    /* get data from column 1 */
    cliRC = SQLGetData(hstmt,
                       1,
                       SQL_C_CHAR,
                       &empno.val,
                       5,
                       &empno.ind);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

    /* get data from column 2 */
    cliRC = SQLGetData(hstmt,
                       2,
                       SQL_C_CHAR,
                       empname.val,
                       15,
                       &empname.ind);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

    /* get data from column 3 */
    cliRC = SQLGetData(hstmt,
                       3,
                       SQL_C_CHAR,
                       &dept_no.val,
                       4,
                       &dept_no.ind);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

    printf("    %-5s %-10s", empno.val, empname.val);
    if (strcmp((char *) dept_no.val, "\0"))
    {
      printf(" %-3s\n", dept_no.val);
    }
    else
    {
      printf(" -\n");
    }

    /* fetch next row */
    cliRC = SQLFetch(hstmt);
    STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);
    strcpy((char *)dept_no.val,"\0");
  }

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

  return rc;
} /* FK_TwoTablesDisplay */

/* to create foreign key */ 
int FK_Create(SQLHANDLE hdbc, char *ruleClause)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */

  char stmt[384]; /* sql statement */

  sprintf(stmt, "ALTER TABLE empl ADD CONSTRAINT fk_dept "
                   "  FOREIGN KEY(dept_no) "
                   "  REFERENCES tab_dept(deptno) "
                   "  %s ", ruleClause);
  
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO ADD A FOREIGN KEY CONSTRAINT TO THE TABLE:\n");

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

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

  printf("\n  Directly execute the statement\n");
  printf("    ALTER TABLE empl ADD CONSTRAINT fk_dept \n"
         "      FOREIGN KEY(dept_no) \n"
         "      REFERENCES tab_dept(deptno) \n"
         "      %s ", ruleClause);
   
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, (SQLCHAR *)stmt, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  return rc;

} /* FK_Create */

/* drop foreign key */
int FK_Drop(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  SQLCHAR *stmt = (SQLCHAR *)"ALTER TABLE empl DROP CONSTRAINT fk_dept";
  
  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO DROP AN FOREIGN KEY CONSTRAINT:\n");

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

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

  printf("\n    ALTER TABLE empl DROP CONSTRAINT fk_dept\n");
 
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  return rc;
} /* FK_Drop */

/* create two tables */
int FK_TwoTablesCreate(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  
  SQLCHAR *stmt1 = (SQLCHAR *)"CREATE TABLE tab_dept(deptno CHAR(3) NOT NULL,"
                              "                  deptname VARCHAR(20), "
                              "CONSTRAINT pk_dept PRIMARY KEY(deptno))";
  SQLCHAR *stmt2 = (SQLCHAR *)"INSERT INTO tab_dept "
                              "  VALUES('A00', 'ADMINISTRATION'), "
                              "        ('B00', 'DEVELOPMENT'), "
                              "        ('C00', 'SUPPORT') ";
  SQLCHAR *stmt3 = (SQLCHAR *)"CREATE TABLE empl(empno CHAR(4), "
                              "                 empname VARCHAR(10),"
                              "                 dept_no CHAR(3))";
 
  SQLCHAR *stmt4 = (SQLCHAR *)"INSERT INTO empl "
                              "VALUES('0010', 'Smith', 'A00'), "
                              "      ('0020', 'Ngan', 'B00'), "
                              "      ('0030', 'Lu', 'B00'), "
                              "      ('0040', 'Wheeler', 'B00'), "
                              "      ('0050', 'Burke', 'C00'), "
                              "      ('0060', 'Edwards', 'C00'), "
                              "      ('0070', 'Lea', 'C00')  ";

  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO CREATE TWO TABLES:\n");
   
  /* set AUTOCOMMIT on */
  cliRC = SQLSetConnectAttr(hdbc,
                            SQL_ATTR_AUTOCOMMIT,
                            (SQLPOINTER)SQL_AUTOCOMMIT_ON,
                            SQL_NTS);
  DBC_HANDLE_CHECK(hdbc, cliRC);

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

  printf("\n  Directly execute the statement\n");
  printf("    CREATE TABLE tab_dept(deptno CHAR(3) NOT NULL,\n"
         "                      deptname VARCHAR(20),\n"
         "                      CONSTRAINT pk_dept PRIMARY KEY(deptno))\n");
 
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  printf("\n    INSERT INTO tab_dept VALUES('A00', 'ADMINISTRATION'),\n"
         "                           ('B00', 'DEVELOPMENT'),\n"
         "                           ('C00', 'SUPPORT')\n");
 
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  printf("\n    CREATE TABLE empl(empno CHAR(4),\n"
         "                     empname VARCHAR(10),\n"
         "                     dept_no CHAR(3))\n");
 
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt3, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  printf("\n    INSERT INTO empl VALUES('0010', 'Smith', 'A00'),\n"
         "                          ('0020', 'Ngan', 'B00'),\n"
         "                          ('0030', 'Lu', 'B00'),\n"
         "                          ('0040', 'Wheeler', 'B00'),\n"
         "                          ('0050', 'Burke', 'C00'),\n"
         "                          ('0060', 'Edwards', 'C00'),\n"
         "                          ('0070', 'Lea', 'C00')\n");
   
 
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt4, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  return rc; 
} /* FK_TwoTablesCreate */

/* drop tables created for FOREIGN KEY sample functions */
int FK_TwoTablesDrop(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  
  SQLCHAR *stmt1 = (SQLCHAR *)"DROP TABLE tab_dept";

  SQLCHAR *stmt2 = (SQLCHAR *)"DROP TABLE empl";

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO DROP THE TABLES:\n");
   
  /* set AUTOCOMMIT on */
  cliRC = SQLSetConnectAttr(hdbc,
                            SQL_ATTR_AUTOCOMMIT,
                            (SQLPOINTER)SQL_AUTOCOMMIT_ON,
                            SQL_NTS);
  DBC_HANDLE_CHECK(hdbc, cliRC);

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

  printf("\n    DROP TABLE tab_dept\n");
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  printf("\n    DROP TABLE empl\n");
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

/* show how to use FOREIGN key works on insert */
int Cn_FK_OnInsertShow(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  
  SQLCHAR *stmt1 = (SQLCHAR *)"INSERT INTO tab_dept VALUES('D00', 'SALES')";

  SQLCHAR *stmt2 = (SQLCHAR *)"INSERT INTO empl"
                              "  VALUES('0080', 'Pearce', 'E03')";

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO SHOW HOW A FOREIGN KEY WORKS ON INSERT:\n");

  /* display initial tables content */
  rc = FK_TwoTablesDisplay(hdbc);
  
  /* create foreign key */
  rc = FK_Create(hdbc,"");

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

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

  printf("\n    INSERT INTO tab_dept VALUES('D00', 'SALES')\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

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

  printf("\n    INSERT INTO empl VALUES('0080', 'Pearce', 'E03')\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  
  /* display the expected error */
  printf("\n-- The following error report is expected! --");
  EX_STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* display final tables content */
  rc = FK_TwoTablesDisplay(hdbc);
  
  /* rollback transaction */  
  rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK); 
  DBC_HANDLE_CHECK(hdbc, rc); 

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

  /* drop foreign key */
  rc = FK_Drop(hdbc);

  return rc;
} /* Cn_FK_OnInsertShow */

/* show how to use 'ON UPDATE NO ACTION' foreign key constraint */
int  Cn_FK_ON_UPDATE_NO_ACTION_Show(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  
  SQLCHAR *stmt1 = (SQLCHAR *)"UPDATE tab_dept SET deptno = 'E01' "
                              "  WHERE deptno = 'A00'";

  SQLCHAR *stmt2 = (SQLCHAR *)"UPDATE tab_dept SET deptno = "
                              "  CASE "
                              "    WHEN deptno = 'A00' THEN 'B00' "
                              "    WHEN deptno = 'B00' THEN 'A00' "
                              "  END "
                              "  WHERE deptno = 'A00' OR deptno = 'B00' ";

  SQLCHAR *stmt3 = (SQLCHAR *)"UPDATE empl SET dept_no = 'G11' "
                              "  WHERE empname = 'Wheeler' ";

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO SHOW AN 'ON UPDATE NO ACTION' FOREIGN KEY:\n");
  
  /* display initial tables content */
  rc = FK_TwoTablesDisplay(hdbc);
  
  /* create foreign key */
  rc = FK_Create(hdbc, "ON UPDATE NO ACTION");

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

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

  printf("\n    UPDATE tab_dept SET deptno = 'E01' WHERE deptno = 'A00'\n");
  
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  
  /* display the expected error */
  printf("\n-- The following error report is expected! --");
  EX_STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC);
  
  printf("\n    UPDATE tab_dept SET deptno =\n"
         "      CASE\n"
         "        WHEN deptno = 'A00' THEN 'B00'\n"
         "        WHEN deptno = 'B00' THEN 'A00'\n"
         "      END\n"
         "      WHERE deptno = 'A00' OR deptno = 'B00'\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  /* update child table */
  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC);
  
  printf("    UPDATE empl SET dept_no = 'G11' WHERE empname = 'Wheeler'\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt3, SQL_NTS);
  
  /* display the expected error */
  printf("\n-- The following error report is expected! --");
  EX_STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

    /* display final tables content */
  rc = FK_TwoTablesDisplay(hdbc);
  
  /* rollback transaction */  
  rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK); 
  DBC_HANDLE_CHECK(hdbc, rc); 

  /* drop foreign key */
  rc = FK_Drop(hdbc);

  return rc;
} /* Cn_FK_ON_UPDATE_NO_ACTION_Show */

/* show how to use 'ON UPDATE RESTRICT' foreign key constraint */
int Cn_FK_ON_UPDATE_RESTRICT_Show(SQLHANDLE hdbc)
{
  
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  
  SQLCHAR *stmt1 = (SQLCHAR *)"UPDATE tab_dept SET deptno = 'E01' "
                              "  WHERE deptno = 'A00' ";
  
  SQLCHAR *stmt2 = (SQLCHAR *)"UPDATE tab_dept SET deptno = "
                              "  CASE "
                              "    WHEN deptno = 'A00' THEN 'B00' "
                              "    WHEN deptno = 'B00' THEN 'A00' "
                              "  END "
                              "  WHERE deptno = 'A00' OR deptno = 'B00' ";

  SQLCHAR *stmt3 = (SQLCHAR *)"UPDATE empl SET dept_no = 'G11' "
                              "  WHERE empname = 'Wheeler' ";
    
  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO SHOW AN 'ON UPDATE RESTRICT' FOREIGN KEY:\n");  
  
  /* display initial tables content */
  rc = FK_TwoTablesDisplay(hdbc);
  
  /* create foreign key */
  rc = FK_Create(hdbc, "ON UPDATE RESTRICT");

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

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

  printf("    UPDATE tab_dept SET deptno = 'E01' WHERE deptno = 'A00'\n");
  
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  
  /* display the expected error */
  printf("\n-- The following error report is expected! --");
  EX_STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

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

  printf("    UPDATE tab_dept SET deptno =\n"
         "      CASE\n"
         "        WHEN deptno = 'A00' THEN 'B00'\n"
         "        WHEN deptno = 'B00' THEN 'A00'\n"
         "      END\n"
         "      WHERE deptno = 'A00' OR deptno = 'B00'\n");


  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  
  /* display the expected error */
  printf("\n-- The following error report is expected! --");
  EX_STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  /* update child table */
  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC);
  
  printf("    UPDATE empl SET dept_no = 'G11' WHERE empname = 'Wheeler'\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt3, SQL_NTS);
  
  /* display the expected error */
  printf("\n-- The following error report is expected! --");
  EX_STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  /* display final tables content */
  rc = FK_TwoTablesDisplay(hdbc);

  /* rollback transaction */  
  rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK); 
  DBC_HANDLE_CHECK(hdbc, rc); 

  /* drop foreign key */
  rc = FK_Drop(hdbc);

  return rc;
} /* Cn_FK_ON_UPDATE_RESTRICT_Show */

/* show how to use 'ON DELETE CASCADE' foreign key constraint */
int Cn_FK_ON_DELETE_CASCADE_Show(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  
  SQLCHAR *stmt1 = (SQLCHAR *)"DELETE FROM tab_dept WHERE deptno = 'C00' ";
  SQLCHAR *stmt2 = (SQLCHAR *)"DELETE FROM empl WHERE empname = 'Wheeler' ";

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO SHOW AN 'ON DELETE CASCADE' FOREIGN KEY:\n");
    
  /* display initial tables content */
  rc = FK_TwoTablesDisplay(hdbc);
  
  /* create foreign key */
  rc = FK_Create(hdbc, "ON DELETE CASCADE");

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

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

  printf("    DELETE FROM tab_dept WHERE deptno = 'C00'\n");
  
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  /* display content of tables */
  rc = FK_TwoTablesDisplay(hdbc);
  
  /* delete child table */
  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC);

  printf("\n    DELETE FROM empl WHERE empname = 'Wheeler'\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* free the statement handle */
  cliRC = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);    
  
  /* display final tables content */
  rc = FK_TwoTablesDisplay(hdbc);

  /* rollback transaction */  
  rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK); 
  DBC_HANDLE_CHECK(hdbc, rc); 

  /* drop foreign key */
  rc = FK_Drop(hdbc);

  return rc;
} /* Cn_FK_ON_DELETE_CASCADE_Show */

/* show how to use 'ON DELETE SET NULL' foreign key constraint */
int Cn_FK_ON_DELETE_SET_NULL_Show(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  
  SQLCHAR *stmt1 = (SQLCHAR *)"DELETE FROM tab_dept WHERE deptno = 'C00' ";
  SQLCHAR *stmt2 = (SQLCHAR *)"DELETE FROM empl WHERE empname = 'Wheeler' ";
  
  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO SHOW AN 'ON DELETE SET NULL' FOREIGN KEY:\n");
    
  /* display initial tables content */
  rc = FK_TwoTablesDisplay(hdbc);
  
  /* create foreign key */
  rc = FK_Create(hdbc, "ON DELETE SET NULL");

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

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

  printf("    DELETE FROM tab_dept WHERE deptno = 'C00'\n");
  
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

  /* display content of tables */
  rc = FK_TwoTablesDisplay(hdbc);
  
  /* delete child table */
  /* allocate a statement handle */
  cliRC = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  DBC_HANDLE_CHECK(hdbc, cliRC);

  printf("\n    DELETE FROM empl WHERE empname = 'Wheeler'\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* free the statement handle */
  cliRC = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);    
  
  /* display final tables content */
  rc = FK_TwoTablesDisplay(hdbc);

  /* rollback transaction */  
  rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK); 
  DBC_HANDLE_CHECK(hdbc, rc); 
  
  /* drop foreign key */
  rc = FK_Drop(hdbc);

  return rc;
} /* Cn_FK_ON_DELETE_SET_NULL_Show */

/* show how to use 'ON DELETE NO ACTION' foreign key constraint */
int  Cn_FK_ON_DELETE_NO_ACTION_Show(SQLHANDLE hdbc)
{
  SQLRETURN cliRC = SQL_SUCCESS;
  int rc = 0;
  SQLHANDLE hstmt; /* statement handle */
  
  SQLCHAR *stmt1 = (SQLCHAR *)"DELETE FROM tab_dept WHERE deptno = 'C00' ";
  SQLCHAR *stmt2 = (SQLCHAR *)"DELETE FROM empl WHERE empname = 'Wheeler' ";
  
  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE CLI FUNCTIONS\n");
  printf("  SQLAllocHandle\n");
  printf("  SQLExecDirect\n");
  printf("  SQLFreeHandle\n");
  printf("  SQLSetConnectAttr\n");
  printf("TO SHOW AN 'ON DELETE NO ACTION' FOREIGN KEY:\n");
  
  /* display initial tables content */
  rc = FK_TwoTablesDisplay(hdbc);
  
  /* create foreign key */
  rc = FK_Create(hdbc, "ON DELETE NO ACTION");

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

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

  printf("    DELETE FROM tab_dept WHERE deptno = 'C00'\n");
  
  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt1, SQL_NTS);
  
  /* display the expected error */
  printf("\n-- The following error report is expected! --");
  EX_STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

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

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

  printf("\n    DELETE FROM empl WHERE empname = 'Wheeler'\n");

  /* directly execute the statement */
  cliRC = SQLExecDirect(hstmt, stmt2, SQL_NTS);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);

  /* free the statement handle */
  cliRC = SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
  STMT_HANDLE_CHECK(hstmt, hdbc, cliRC);    
  
  /* display final tables content */
  rc = FK_TwoTablesDisplay(hdbc);
  
  /* rollback transaction */  
  rc = SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK); 
  DBC_HANDLE_CHECK(hdbc, rc); 
  
  /* drop foreign key */
  rc = FK_Drop(hdbc);

  return rc;
} /* Cn_FK_ON_DELETE_NO_ACTION_Show */