/****************************************************************************
** (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: tbread.sqc 
**    
** SAMPLE: How to read tables 
**
** SQL STATEMENTS USED:
**         DECLARE CURSOR 
**         OPEN
**         FETCH
**         CLOSE
**         PREPARE
**         DESCRIBE
**         SELECT
**
**                           
*****************************************************************************
**
** 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 BEGIN DECLARE SECTION;
  short deptnumb;
  short maxDeptnumb;
  char deptname[15];
  char orgDeptname[15];
  short manager;
  char division[11];
  char location[14];
  char deptno[4];
  char departmentDeptname[30];
  char mgrno[7];
  short mgrnoInd;
  char admrdept[4];
  char departmentLocation[17];
  short departmentLocationInd;
  char job[9];
  short jobInd;
  short edlevel;
  short edlevelInd;
  double comm;
  double commSum;
  short commSumInd;
  char rcteDeptno[4];
  char rcteDeptname[30];
  char rcteAdmrdept[4];
  char strStmt[100];
  double avgSalary;
EXEC SQL END DECLARE SECTION;

/* methods to perform SELECT */
int TbSelectUsingFetchIntoHostVariables(void);
int TbSelectUsingFetchIntoSQLDA(void);
int TbSelectUsingDeclareCursor(void);
int TbSelectUsingPrepareDeclareCursor(void);
int TbSelectUsingPrepareDescribeDeclareCursor(void);

/* the support functions for TbSelectUsingPrepareDescribeDeclareCursor */
int SqldaInit(struct sqlda **, int);
int ColTitlesDisplay(struct sqlda *);
short DisplayDataLenGet(short, short);
int RowDataMemoryAlloc(struct sqlda *);
int RowDataDisplay(struct sqlda *);
int CellDataDisplay(char *, short, short *, short, short);
int RowDataMemoryFree(struct sqlda *);

/* SELECT types */
int TbMostSimpleSubselect(void);
int TbBasicSubselect(void);
int TbGroupBySubselect(void);
int TbSubselect(void);
int TbRowSubselect(void);
int TbFullselect(void);
int TbSelectStatement(void);
int TbBasicSubselectFromMultipleTables(void);
int TbBasicSubselectFromJoinedTable(void);
int TbBasicSubselectUsingSubquery(void);
int TbBasicSubselectUsingCorrelatedSubquery(void);
int TbSubselectUsingGroupingSets(void);
int TbSubselectUsingRollup(void);
int TbSubselectUsingCube(void);
int TbSelectStatementUsingRecursiveCommonTableExpression(void);
int TbSelectUsingQuerySampling(void);

/* support functions for SELECT types */
int OrgTbContentDisplay(void);
int DepartmentTbContentDisplay(void);
int EmployeeTbPartialContentDisplay(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 READ TABLES.\n");

  printf("\n  THE SAMPLE DEMONSTRATES:\n");
  printf("    - METHODS TO PERFORM SELECT\n");
  printf("    - SELECT TYPES\n");
  printf("  ALL METHODS APPLY TO ALL TYPES, EXCEPT FOR THE\n");
  printf("  'TbRowSubselect' TYPE WHERE THE FOLLOWING METHODS\n");
  printf("  DESCRIBED IN THE dbuse SAMPLE APPLY:\n");
  printf("    StaticStmtWithHostVarsInvoke\n");
  printf("    CompoundStmtInvoke\n");

  printf("\n  METHODS TO PERFORM SELECT.\n");
  printf("    SELECT HAS TWO IMPORTANT PARTS:\n");
  printf("      -DECLARE CURSOR\n");
  printf("      -FETCH CURSOR\n");
  printf("    THERE ARE THREE METHODS TO DECLARE A CURSOR:\n");
  printf("      -SIMPLE DECLARE CURSOR\n");
  printf("      -PREPARE-DECLARE CURSOR\n");
  printf("      -PREPARE-DESCRIBE-DECLARE CURSOR\n");
  printf("    THERE ARE TWO METHODS TO FETCH A CURSOR:\n");
  printf("      -FETCH INTO HOST VARIABLES\n");
  printf("      -FETCH INTO SQLDA\n");

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

  /* methods to perform SELECT */
  printf("\n***************************************\n");
  printf("    METHODS TO PERFORM SELECT\n");
  printf("***************************************\n");
  rc = TbSelectUsingFetchIntoHostVariables();
  rc = TbSelectUsingFetchIntoSQLDA();
  rc = TbSelectUsingDeclareCursor();
  rc = TbSelectUsingPrepareDeclareCursor();
  rc = TbSelectUsingPrepareDescribeDeclareCursor();

  /* select types */
  printf("\n***************************************\n");
  printf("    SELECT TYPES\n");
  printf("**************************************\n");
  rc = TbMostSimpleSubselect();
  rc = TbBasicSubselect();
  rc = TbGroupBySubselect();
  rc = TbSubselect();
  rc = TbRowSubselect();
  rc = TbFullselect();
  rc = TbSelectStatement();
  rc = TbBasicSubselectFromMultipleTables();
  rc = TbBasicSubselectFromJoinedTable();
  rc = TbBasicSubselectUsingSubquery();
  rc = TbBasicSubselectUsingCorrelatedSubquery();
  rc = TbSubselectUsingGroupingSets();
  rc = TbSubselectUsingRollup();
  rc = TbSubselectUsingCube();
  rc = TbSelectStatementUsingRecursiveCommonTableExpression();
  rc = TbSelectUsingQuerySampling();
  
  /* disconnect from the database */
  rc = DbDisconn(dbAlias);
  if (rc != 0)
  {
    return rc;
  }

  return 0;
} /* main */

int TbSelectUsingFetchIntoHostVariables(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SELECT USING FETCH INTO HOST VARIABLES.\n");

  printf("\n  Perform:\n");
  printf("    SELECT deptnumb, deptname FROM org\n");
  printf("\n  Results:\n");
  printf("    DEPTNUMB DEPTNAME\n");
  printf("    -------- --------------\n");

  /* declare cursor */
  EXEC SQL DECLARE c0 CURSOR FOR SELECT deptnumb, deptname FROM org;

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

  /* fetch cursor */
  EXEC SQL FETCH c0 INTO :deptnumb, :deptname;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %8d %-14s\n", deptnumb, deptname);

    EXEC SQL FETCH c0 INTO :deptnumb, :deptname;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbSelectUsingFetchIntoHostVariables */

int TbSelectUsingFetchIntoSQLDA(void)
{
  int rc = 0;
  struct sqlca sqlca;
  struct sqlda *pSqlda = NULL;
  short deptnumbInd = 0;
  short deptnameInd = 0;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SELECT USING FETCH INTO SQLDA.\n");

  printf("\n  Perform:\n");
  printf("    SELECT deptnumb, deptname FROM org\n");
  printf("\n  Results:\n");
  printf("    DEPTNUMB DEPTNAME\n");
  printf("    -------- --------------\n");

  /* declare cursor */
  EXEC SQL DECLARE c1 CURSOR FOR SELECT deptnumb, deptname FROM org;

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

  /* initialize SQLDA structure */
  pSqlda = (struct sqlda *)malloc(SQLDASIZE(2)); /* two variables */
  strncpy(pSqlda->sqldaid, "SQLDA   ", sizeof(pSqlda->sqldaid));
  pSqlda->sqldabc = (sqlint32)SQLDASIZE(2);
  pSqlda->sqln = 2; /* two variables */
  pSqlda->sqld = 2;

  pSqlda->sqlvar[0].sqltype = SQL_TYP_NSMALL;
  pSqlda->sqlvar[0].sqllen = sizeof(short);
  pSqlda->sqlvar[0].sqldata = (char *)&deptnumb;
  pSqlda->sqlvar[0].sqlind = (short *)&deptnumbInd;

  pSqlda->sqlvar[1].sqltype = SQL_TYP_NCSTR;
  pSqlda->sqlvar[1].sqllen = 15;
  pSqlda->sqlvar[1].sqldata = (char *)&deptname;
  pSqlda->sqlvar[1].sqlind = (short *)&deptnameInd;

  /* fetch cursor */
  EXEC SQL FETCH c1 USING DESCRIPTOR :*pSqlda;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %8d %-14s\n", deptnumb, deptname);

    EXEC SQL FETCH c1 USING DESCRIPTOR :*pSqlda;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  /* free memory allocated */
  free(pSqlda);

  return 0;
} /* TbSelectUsingFetchIntoSQLDA */

int TbSelectUsingDeclareCursor(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SELECT USING DECLARE CURSOR.\n");

  printf("\n  Perform:\n");
  printf("    SELECT deptnumb, deptname FROM org\n");
  printf("\n  Results:\n");
  printf("    DEPTNUMB DEPTNAME\n");
  printf("    -------- --------------\n");

  /* declare cursor */
  EXEC SQL DECLARE c2 CURSOR FOR SELECT deptnumb, deptname FROM org;

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

  /* fetch cursor */
  EXEC SQL FETCH c2 INTO :deptnumb, :deptname;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %8d %-14s\n", deptnumb, deptname);

    EXEC SQL FETCH c2 INTO :deptnumb, :deptname;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbSelectUsingDeclareCursor */

int TbSelectUsingPrepareDeclareCursor(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  PREPARE\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SELECT USING PREPARE-DECLARE CURSOR.\n");

  printf("\n  Perform:\n");
  printf("    SELECT deptnumb, deptname FROM org\n");
  printf("\n  Results:\n");
  printf("    DEPTNUMB DEPTNAME\n");
  printf("    -------- --------------\n");

  strcpy(strStmt, "SELECT deptnumb, deptname FROM org");

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

  /* declare cursor */
  EXEC SQL DECLARE c3 CURSOR FOR stmt1;

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

  /* fetch cursor */
  EXEC SQL FETCH c3 INTO :deptnumb, :deptname;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %8d %-14s\n", deptnumb, deptname);

    EXEC SQL FETCH c3 INTO :deptnumb, :deptname;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbSelectUsingPrepareDeclareCursor */

int TbSelectUsingPrepareDescribeDeclareCursor(void)
{
  int rc = 0;
  struct sqlca sqlca;
  struct sqlda *pSqlda = NULL;
  int numCols;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  PREPARE\n");
  printf("  DESCRIBE\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SELECT USING PREPARE-DESCRIBE-DECLARE CURSOR.\n");

  strcpy(strStmt, "SELECT * FROM staff WHERE id <= 30");
  printf("\n  Perform:\n");
  printf("    %s\n", strStmt);
  printf("\n");

  /* -------------------------------------- */
  /* determine the number of output columns */
  /* -------------------------------------- */

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

  rc = SqldaInit(&pSqlda, 1);
  if (rc != 0)
  {
    return rc;
  }

  EXEC SQL DESCRIBE stmt2 INTO :*pSqlda;
  if (sqlca.sqlcode != 0 &&
      sqlca.sqlcode != SQL_RC_W236 &&
      sqlca.sqlcode != SQL_RC_W237 &&
      sqlca.sqlcode != SQL_RC_W238 && sqlca.sqlcode != SQL_RC_W239)
  {
    EMB_SQL_CHECK("statement -- describe");
  }

  numCols = (int)pSqlda->sqld;
  free(pSqlda);

  /* ---------------- */
  /* invoke statement */
  /* ---------------- */

  if (numCols > 0)
  {
    if (sqlca.sqlcode == 0 || sqlca.sqlcode == SQL_RC_W236)
    {
      rc = SqldaInit(&pSqlda, numCols);
      if (rc != 0)
      {
        return rc;
      }
    }
    if (sqlca.sqlcode == SQL_RC_W237 ||
        sqlca.sqlcode == SQL_RC_W238 ||
        sqlca.sqlcode == SQL_RC_W239)
    {
      rc = SqldaInit(&pSqlda, numCols * 2);
      if (rc != 0)
      {
        return rc;
      }
    }

    EXEC SQL DESCRIBE stmt2 INTO :*pSqlda;
    EMB_SQL_CHECK("statement -- describe");

    rc = ColTitlesDisplay(pSqlda);

    EXEC SQL DECLARE c4 CURSOR FOR stmt2;

    EXEC SQL OPEN c4;
    EMB_SQL_CHECK("cursor -- open");

    rc = RowDataMemoryAlloc(pSqlda);
    if (rc != 0)
    {
      return rc;
    }

    EXEC SQL FETCH c4 USING DESCRIPTOR :*pSqlda;
    EMB_SQL_CHECK("cursor -- fetch");

    while (sqlca.sqlcode != 100)
    {
      rc = RowDataDisplay(pSqlda);

      EXEC SQL FETCH c4 USING DESCRIPTOR :*pSqlda;
      EMB_SQL_CHECK("cursor -- fetch");
    }

    EXEC SQL CLOSE c4;
    EMB_SQL_CHECK("cursor -- close");

    rc = RowDataMemoryFree(pSqlda);
    free(pSqlda);
  }

  return 0;
} /* TbSelectUsingPrepareDescribeDeclareCursor */

int SqldaInit(struct sqlda **ppSqlda, int size)
{
  int rc = 0;

  *ppSqlda = (struct sqlda *)malloc(SQLDASIZE(size));
  if (*ppSqlda == NULL)
  {
    return 1;
  }

  memcpy((*ppSqlda)->sqldaid, "SQLDA   ", 8);
  (*ppSqlda)->sqldabc = (sqlint32) SQLDASIZE(size);
  (*ppSqlda)->sqln = size;
  (*ppSqlda)->sqld = 0;

  return 0;
} /* SqldaInit */

int ColTitlesDisplay(struct sqlda *pSqlda)
{
  int rc = 0;
  short colNb;
  short numCols;
  short colDataLen;
  short colTitleLen;
  short colWidth;
  char colTitle[129];

  numCols = pSqlda->sqld;
  for (colNb = 0; colNb < numCols; colNb++)
  {
    colDataLen = DisplayDataLenGet(pSqlda->sqlvar[colNb].sqltype,
                                   pSqlda->sqlvar[colNb].sqllen);

    colTitleLen = pSqlda->sqlvar[colNb].sqlname.length;
    colWidth = max(colDataLen, colTitleLen);

    strcpy(colTitle, pSqlda->sqlvar[colNb].sqlname.data);
    printf("%-*s", colWidth, colTitle);
    printf(" | ");
  }
  printf("\n");

  return 0;
} /* ColTitlesDisplay */

short DisplayDataLenGet(short sqlDataType, short sqlDataSize)
{
  short displayDataLen;

  switch (sqlDataType)
  {
    case SQL_TYP_DATE:
    case SQL_TYP_NDATE:
    case SQL_TYP_TIME:
    case SQL_TYP_NTIME:
    case SQL_TYP_STAMP:
    case SQL_TYP_NSTAMP:
    case SQL_TYP_VARCHAR:
    case SQL_TYP_NVARCHAR:
    case SQL_TYP_CHAR:
    case SQL_TYP_NCHAR:
    case SQL_TYP_LONG:
    case SQL_TYP_NLONG:
    case SQL_TYP_CSTR:
    case SQL_TYP_NCSTR:
    case SQL_TYP_LSTR:
    case SQL_TYP_NLSTR:
      displayDataLen = sqlDataSize;
      break;
    case SQL_TYP_FLOAT:
    case SQL_TYP_NFLOAT:
      /* 3 chars for every byte +  */
      /* 1 char for sign +         */
      /* 1 digit for decimal point */
      displayDataLen = (short)(3 * sqlDataSize + 1 + 1);
      break;
    case SQL_TYP_INTEGER:
    case SQL_TYP_NINTEGER:
    case SQL_TYP_SMALL:
    case SQL_TYP_NSMALL:
      /* 3 chars for every byte + */
      /* 1 char for sign */
      displayDataLen = (short)(3 * sqlDataSize + 1);
      break;
    case SQL_TYP_DECIMAL:
    case SQL_TYP_NDECIMAL:
      displayDataLen = (short)(((char *)&sqlDataSize)[0]) + /* precision */
                       (short)1 + /* decimal point */
                       (short)1; /* sign */
      break;
    case SQL_TYP_CGSTR:
    case SQL_TYP_NCGSTR:
    case SQL_TYP_VARGRAPH:
    case SQL_TYP_NVARGRAPH:
    case SQL_TYP_GRAPHIC:
    case SQL_TYP_NGRAPHIC:
    case SQL_TYP_LONGRAPH:
    case SQL_TYP_NLONGRAPH:
      displayDataLen = 30;
      break;
    case SQL_TYP_BLOB:
    case SQL_TYP_NBLOB:
    case SQL_TYP_CLOB:
    case SQL_TYP_NCLOB:
    case SQL_TYP_DBCLOB:
    case SQL_TYP_NDBCLOB:
      displayDataLen = 30;
      break;
    default:
      displayDataLen = 0;
      break;
  }

  return displayDataLen;
} /* DisplayDataLenGet */

int RowDataMemoryAlloc(struct sqlda *pSqlda)
{
  int rc = 0;
  short colNb;
  short numCols;
  unsigned int memSize;
  short precision;

  numCols = pSqlda->sqld;
  for (colNb = 0; colNb < numCols; colNb++)
  {
    /* allocate sqlind memory */
    if (pSqlda->sqlvar[colNb].sqltype & 1)
    {
      pSqlda->sqlvar[colNb].sqlind = (short *)malloc(sizeof(short));
      if (pSqlda->sqlvar[colNb].sqlind == NULL)
      {
        return 1;
      }
      memset(pSqlda->sqlvar[colNb].sqlind, '\0', sizeof(short));
    }
    /* allocate sqldata memory */
    switch (pSqlda->sqlvar[colNb].sqltype)
    {
      case SQL_TYP_DATE:
      case SQL_TYP_NDATE:
      case SQL_TYP_TIME:
      case SQL_TYP_NTIME:
      case SQL_TYP_STAMP:
      case SQL_TYP_NSTAMP:
      case SQL_TYP_VARCHAR:
      case SQL_TYP_NVARCHAR:
      case SQL_TYP_CHAR:
      case SQL_TYP_NCHAR:
      case SQL_TYP_LONG:
      case SQL_TYP_NLONG:
      case SQL_TYP_CSTR:
      case SQL_TYP_NCSTR:
      case SQL_TYP_LSTR:
      case SQL_TYP_NLSTR:
        pSqlda->sqlvar[colNb].sqltype = SQL_TYP_NCSTR;
        memSize = (unsigned int)pSqlda->sqlvar[colNb].sqllen;
        break;
      case SQL_TYP_FLOAT:
      case SQL_TYP_NFLOAT:
      case SQL_TYP_INTEGER:
      case SQL_TYP_NINTEGER:
      case SQL_TYP_SMALL:
      case SQL_TYP_NSMALL:
        memSize = (unsigned int)pSqlda->sqlvar[colNb].sqllen;
        break;
      case SQL_TYP_DECIMAL:
      case SQL_TYP_NDECIMAL:
        precision = (short)(((char *)&(pSqlda->sqlvar[colNb].sqllen))[0]);
        memSize = (unsigned int)(precision / 2 + 1);
        break;
      case SQL_TYP_CGSTR:
      case SQL_TYP_NCGSTR:
      case SQL_TYP_VARGRAPH:
      case SQL_TYP_NVARGRAPH:
      case SQL_TYP_GRAPHIC:
      case SQL_TYP_NGRAPHIC:
      case SQL_TYP_LONGRAPH:
      case SQL_TYP_NLONGRAPH:
        memSize = (unsigned int)(2 * pSqlda->sqlvar[colNb].sqllen);
        break;
      case SQL_TYP_BLOB:
      case SQL_TYP_NBLOB:
      case SQL_TYP_CLOB:
      case SQL_TYP_NCLOB:
        memSize = GETSQLDALONGLEN(pSqlda, colNb);
        break;
      case SQL_TYP_DBCLOB:
      case SQL_TYP_NDBCLOB:
        memSize = 2 * GETSQLDALONGLEN(pSqlda, colNb);
        break;
      default:
        memSize = 0;
        break;
    }
    pSqlda->sqlvar[colNb].sqldata = (char *)malloc(memSize);
    if (pSqlda->sqlvar[colNb].sqldata == NULL)
    {
      return 1;
    }
    memset(pSqlda->sqlvar[colNb].sqldata, '\0', memSize);
  }

  return 0;
} /* RowDataMemoryAlloc */

int RowDataDisplay(struct sqlda *pSqlda)
{
  int rc = 0;
  short colNb;
  short numCols;
  short colDataLen;
  short colTitleLen;
  short colWidth;

  numCols = pSqlda->sqld;
  for (colNb = 0; colNb < numCols; colNb++)
  {
    colDataLen = DisplayDataLenGet(pSqlda->sqlvar[colNb].sqltype,
                                   pSqlda->sqlvar[colNb].sqllen);

    colTitleLen = pSqlda->sqlvar[colNb].sqlname.length;
    colWidth = max(colDataLen, colTitleLen);

    rc = CellDataDisplay(pSqlda->sqlvar[colNb].sqldata,
                         pSqlda->sqlvar[colNb].sqltype,
                         pSqlda->sqlvar[colNb].sqlind,
                         pSqlda->sqlvar[colNb].sqllen,
                         colWidth);
    printf(" | ");
  }
  printf("\n");

  return 0;
} /* RowDataDisplay */

int CellDataDisplay(char *sqldata,
                    short sqltype,
                    short *sqlind,
                    short sqllen,
                    short colWidth)
{
  int rc = 0;
  char buf[100];
  struct lstr
  {
    char len;
    char *data;
  } *pLstr;
  int i;
  short precision;
  short scale;
  short numBytes;
  short top;
  short bottom;
  short byteNb;
  short digNb;
  struct lob
  {
    sqlint32 len;
    char *data;
  } *pLob;

  if (sqltype & 1 && *sqlind < 0)
  {
    printf("%-*s", colWidth, " -");
  }
  else
  {
    switch (sqltype)
    {
      case SQL_TYP_DATE:
      case SQL_TYP_NDATE:
      case SQL_TYP_TIME:
      case SQL_TYP_NTIME:
      case SQL_TYP_STAMP:
      case SQL_TYP_NSTAMP:
      case SQL_TYP_VARCHAR:
      case SQL_TYP_NVARCHAR:
      case SQL_TYP_CHAR:
      case SQL_TYP_NCHAR:
      case SQL_TYP_LONG:
      case SQL_TYP_NLONG:
      case SQL_TYP_CSTR:
      case SQL_TYP_NCSTR:
        printf("%-*.*s", colWidth, colWidth, sqldata);
        break;
      case SQL_TYP_LSTR:
      case SQL_TYP_NLSTR:
        pLstr = (struct lstr *)sqldata;
        for (i = 0; i < (int)pLstr->len; i++)
        {
          printf("%c", (pLstr->data)[i]);
        }
        while (i < colWidth)
        {
          printf(" ");
          i = i + 1;
        }
        break;
      case SQL_TYP_FLOAT:
      case SQL_TYP_NFLOAT:
        printf("%*f", colWidth, *((double *)sqldata));
        break;
      case SQL_TYP_INTEGER:
      case SQL_TYP_NINTEGER:
        printf("%*ld", colWidth, *((long *)sqldata));
        break;
      case SQL_TYP_SMALL:
      case SQL_TYP_NSMALL:
        printf("%*d", colWidth, *((short *)sqldata));
        break;
      case SQL_TYP_DECIMAL:
      case SQL_TYP_NDECIMAL:
        precision = (short)(((char *)&sqllen)[0]);
        scale = (short)(((char *)&sqllen)[1]);

        /* adjust the precision to odd value */
        if ((precision % 2) == 0)
        {
          precision = precision + 1;
        }

        /* calculate numBytes */
        numBytes = (short)(precision + 1) / 2;

        /* determine the sign using bottom of the last byte */
        bottom = *(sqldata + numBytes - 1) & 0x000F;
        i = 0;
        if (bottom == 0x000D || bottom == 0x000B)
        {
          buf[i] = '-';
          i++;
        }
        else
        {
          buf[i] = ' ';
          i++;
        }

        /* prepare the decimal number */
        digNb = 0;
        if (digNb == precision - scale)
        {
          buf[i] = '.';
          i++;
        }

        /* (top + bottom) from first (numBytes - 1) bytes ... */
        for (byteNb = 0; byteNb < numBytes - 1; byteNb = byteNb + 1)
        {
          top = *(sqldata + byteNb) & 0x00F0;
          top = top >> 4;
          bottom = *(sqldata + byteNb) & 0x000F;
          buf[i] = top + '0';
          i++;
          digNb++;
          if (digNb == precision - scale)
          {
            buf[i] = '.';
            i++;
          }
          buf[i] = bottom + '0';
          i++;
          digNb++;
          if (digNb == precision - scale)
          {
            buf[i] = '.';
            i++;
          }
        }

        /* ... and top of the last byte (bottom is the sign) */
        top = *(sqldata + byteNb) & 0x00F0;
        top = top >> 4;
        buf[i] = top + '0';
        i++;
        digNb++;
        if (digNb == precision - scale)
        {
          buf[i] = '.';
          i++;
        }

        /* display decimal number */
        buf[i] = '\0';
        printf("%*s", colWidth, buf);
        break;
      case SQL_TYP_CGSTR:
      case SQL_TYP_NCGSTR:
      case SQL_TYP_VARGRAPH:
      case SQL_TYP_NVARGRAPH:
      case SQL_TYP_GRAPHIC:
      case SQL_TYP_NGRAPHIC:
      case SQL_TYP_LONGRAPH:
      case SQL_TYP_NLONGRAPH:
        sprintf(buf, "Graphic data length: %5ld", sqllen);
        printf("%-*s", colWidth, buf);
        break;
      case SQL_TYP_BLOB:
      case SQL_TYP_NBLOB:
      case SQL_TYP_CLOB:
      case SQL_TYP_NCLOB:
      case SQL_TYP_DBCLOB:
      case SQL_TYP_NDBCLOB:
        pLob = (struct lob *)sqldata;
        if (*sqlind == 0)
        {
          sprintf(buf, "LOB length: %10ld", (long)pLob->len);
        }
        else
        {
          sprintf(buf, "Truncated LOB length: %10ld", (long)pLob->len);
        }
        printf("%-*s", colWidth, buf);
        break;
      default:
        printf("%-*s", colWidth, "?");
        break;
    }
  }

  return 0;
} /* CellDataDisplay */

int RowDataMemoryFree(struct sqlda *pSqlda)
{
  int rc = 0;
  short colNb;

  for (colNb = 0; colNb < pSqlda->sqld; colNb++)
  {
    free(pSqlda->sqlvar[colNb].sqldata);
    if (pSqlda->sqlvar[colNb].sqltype & 1)
    {
      free(pSqlda->sqlvar[colNb].sqlind);
    }
  }

  return 0;
} /* RowDataMemoryFree */

int TbMostSimpleSubselect(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM A SIMPLE SUBSELECT.\n");

  /* display ORG table content */
  rc = OrgTbContentDisplay();

  printf("\n  Perform:\n");
  printf("    SELECT deptnumb, deptname FROM org\n");
  printf("\n  Results:\n");
  printf("    DEPTNUMB DEPTNAME\n");
  printf("    -------- --------------\n");

  /* declare cursor */
  EXEC SQL DECLARE c5 CURSOR FOR SELECT deptnumb, deptname FROM org;

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

  /* fetch cursor */
  EXEC SQL FETCH c5 INTO :deptnumb, :deptname;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %8d %-14s\n", deptnumb, deptname);

    EXEC SQL FETCH c5 INTO :deptnumb, :deptname;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbMostSimpleSubselect */

int TbBasicSubselect(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SUBSELECT USING A WHERE CLAUSE.\n");

  /* display ORG table content */
  rc = OrgTbContentDisplay();

  printf("\n  Perform:\n");
  printf("    SELECT deptnumb, deptname FROM org WHERE deptnumb < 30\n");
  printf("\n  Results:\n");
  printf("    DEPTNUMB DEPTNAME\n");
  printf("    -------- --------------\n");

  /* declare cursor */
  EXEC SQL DECLARE c6 CURSOR FOR
    SELECT deptnumb, deptname FROM org WHERE deptnumb < 30;

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

  /* fetch cursor */
  EXEC SQL FETCH c6 INTO :deptnumb, :deptname;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %8d %-14s\n", deptnumb, deptname);

    EXEC SQL FETCH c6 INTO :deptnumb, :deptname;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbBasicSubselect */

int TbGroupBySubselect(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM 'GROUP BY' SUBSELECT.\n");

  /* display ORG table content */
  rc = OrgTbContentDisplay();

  printf("\n  Perform:\n");
  printf("    SELECT division, MAX(deptnumb) FROM org GROUP BY division\n");
  printf("\n  Results:\n");
  printf("    DIVISION   MAX(DEPTNUMB)\n");
  printf("    ---------- --------------\n");

  /* declare cursor */
  EXEC SQL DECLARE c7 CURSOR FOR
    SELECT division, MAX(deptnumb) FROM org GROUP BY division;

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

  /* fetch cursor */
  EXEC SQL FETCH c7 INTO :division, :maxDeptnumb;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %-10s %14d\n", division, maxDeptnumb);

    EXEC SQL FETCH c7 INTO :division, :maxDeptnumb;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbGroupBySubselect */

int TbSubselect(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SUBSELECT.\n");

  /* display ORG table content */
  rc = OrgTbContentDisplay();

  printf("\n  Perform:\n");
  printf("    SELECT division, MAX(deptnumb)\n");
  printf("      FROM org\n");
  printf("      WHERE location NOT IN 'New York'\n");
  printf("      GROUP BY division HAVING division LIKE '%%ern'\n");
  printf("\n  Results:\n");
  printf("    DIVISION   MAX(DEPTNUMB)\n");
  printf("    ---------- --------------\n");

  /* declare cursor */
  EXEC SQL DECLARE c8 CURSOR FOR
    SELECT division, MAX(deptnumb)
      FROM org
      WHERE location NOT IN 'New York'
      GROUP BY division HAVING division LIKE '%ern';

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

  /* fetch cursor */
  EXEC SQL FETCH c8 INTO :division, :maxDeptnumb;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %-10s %14d\n", division, maxDeptnumb);

    EXEC SQL FETCH c8 INTO :division, :maxDeptnumb;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbSubselect */

int TbRowSubselect(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM 'ROW' SUBSELECT.\n");

  /* display ORG table content */
  rc = OrgTbContentDisplay();

  printf("\n  Perform:\n");
  printf("    SELECT deptnumb, deptname INTO :deptnumb, :deptname\n");
  printf("      FROM org\n");
  printf("      WHERE location = 'New York'\n");
  printf("\n  Results:\n");
  printf("    DEPTNUMB DEPTNAME\n");
  printf("    -------- --------------\n");

  /* declare cursor */
  EXEC SQL
    SELECT deptnumb, deptname INTO :deptnumb, :deptname
      FROM org
      WHERE location = 'New York';

  printf("    %8d %-14s\n", deptnumb, deptname);

  return 0;
} /* TbRowSubselect */

int TbFullselect(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM FULLSELECT.\n");

  /* display ORG table content */
  rc = OrgTbContentDisplay();

  printf("\n  Perform:\n");
  printf("    SELECT deptnumb, deptname\n");
  printf("      FROM org\n");
  printf("      WHERE deptnumb < 20 UNION VALUES(7, 'New Deptname')\n");
  printf("\n  Results:\n");
  printf("    DEPTNUMB DEPTNAME\n");
  printf("    -------- --------------\n");

  /* declare cursor */
  EXEC SQL DECLARE c9 CURSOR FOR
    SELECT deptnumb, deptname
      FROM org
      WHERE deptnumb < 20 UNION VALUES(7, 'New Deptname');

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

  /* fetch cursor */
  EXEC SQL FETCH c9 INTO :deptnumb, :deptname;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %8d %-14s\n", deptnumb, deptname);

    EXEC SQL FETCH c9 INTO :deptnumb, :deptname;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbFullselect */

int TbSelectStatement(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SELECT STATEMENT.\n");

  /* display ORG table content */
  rc = OrgTbContentDisplay();

  printf("\n  Perform:\n");
  printf("    WITH new_org(new_deptnumb, new_deptname)\n");
  printf("      AS(SELECT deptnumb, deptname\n");
  printf("           FROM org\n");
  printf("           UNION VALUES(7, 'New Dept 1'),\n");
  printf("                       (77, 'New Dept 2'),\n");
  printf("                       (777, 'New Dept 3'))\n");
  printf("      SELECT new_deptnumb, new_deptname\n");
  printf("        FROM new_org\n");
  printf("        WHERE new_deptnumb > 70\n");
  printf("        ORDER BY new_deptname\n");
  printf("\n  Results:\n");
  printf("    NEW_DEPTNUMB NEW_DEPTNAME\n");
  printf("    ------------ ------------\n");

  /* declare cursor */
  EXEC SQL DECLARE c10 CURSOR FOR
    WITH new_org(new_deptnumb, new_deptname)
      AS(SELECT deptnumb, deptname
           FROM org
           UNION VALUES(7, 'New Dept 1'),
                       (77, 'New Dept 2'),
                       (777, 'New Dept 3'))
      SELECT new_deptnumb, new_deptname
        FROM new_org
        WHERE new_deptnumb > 70
        ORDER BY new_deptname;

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

  /* fetch cursor */
  EXEC SQL FETCH c10 INTO :deptnumb, :deptname;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %12d %-18s\n", deptnumb, deptname);

    EXEC SQL FETCH c10 INTO :deptnumb, :deptname;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbSelectStatement */

int TbBasicSubselectFromMultipleTables(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SUBSELECT FROM MULTIPLE TABLES.\n");

  /* display ORG table content */
  rc = OrgTbContentDisplay();

  /* display DEPARTMENT table content */
  rc = DepartmentTbContentDisplay();

  printf("\n  Perform:\n");
  printf("    SELECT deptnumb, o.deptname, deptno, d.deptname\n");
  printf("      FROM org o, department d\n");
  printf("      WHERE deptnumb <= 15 AND deptno LIKE '%%11'\n");
  printf("\n  Results:\n");
  printf("    DEPTNUMB ORG.DEPTNAME   DEPTNO DEPARTMENT.DEPTNAME\n");
  printf("    -------- -------------- ------ ---------------------\n");

  /* declare cursor */
  EXEC SQL DECLARE c11 CURSOR FOR
    SELECT deptnumb, o.deptname, deptno, d.deptname
      FROM org o, department d
      WHERE deptnumb <= 15 AND deptno LIKE '%11';

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

  /* fetch cursor */
  EXEC SQL FETCH c11 INTO :deptnumb, :orgDeptname, :deptno,
                          :departmentDeptname;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %8d %-14s %-6s %-14s\n",
           deptnumb, orgDeptname, deptno, departmentDeptname);

    EXEC SQL FETCH c11 INTO :deptnumb, :orgDeptname, :deptno,
                            :departmentDeptname;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbBasicSelectFromMultipleTables */

int TbBasicSubselectFromJoinedTable(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SUBSELECT FROM A JOINED TABLE.\n");

  /* display ORG table content */
  rc = OrgTbContentDisplay();

  /* display DEPARTMENT table content */
  rc = DepartmentTbContentDisplay();

  printf("\n  Perform:\n");
  printf("    SELECT deptnumb, manager, deptno, mgrno\n");
  printf("      FROM org INNER JOIN department\n");
  printf("      ON manager = INTEGER(mgrno)\n");
  printf("      WHERE deptnumb BETWEEN 20 AND 100\n");
  printf("\n  Results:\n");
  printf("    DEPTNUMB MANAGER DEPTNO MGRNO\n");
  printf("    -------- ------- ------ ------\n");

  /* declare cursor */
  EXEC SQL DECLARE c12 CURSOR FOR
    SELECT deptnumb, manager, deptno, mgrno
      FROM org INNER JOIN department
      ON manager = INTEGER(mgrno)
      WHERE deptnumb BETWEEN 20 AND 100;

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

  /* fetch cursor */
  EXEC SQL FETCH c12 INTO :deptnumb, :manager, :deptno, :mgrno;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %8d %7d %-6s %-6s\n", deptnumb, manager, deptno, mgrno);

    EXEC SQL FETCH c12 INTO :deptnumb, :manager, :deptno, :mgrno;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbBasicSubselectFromJoinedTable */

int TbBasicSubselectUsingSubquery(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SUBSELECT USING SUBQUERY.\n");

  /* display ORG table content */
  rc = OrgTbContentDisplay();

  printf("\n  Perform:\n");
  printf("    SELECT deptnumb, deptname\n");
  printf("      FROM org\n");
  printf("      WHERE deptnumb < (SELECT AVG(deptnumb) FROM org)\n");
  printf("\n  Results:\n");
  printf("    DEPTNUMB DEPTNAME\n");
  printf("    -------- --------------\n");

  /* declare cursor */
  EXEC SQL DECLARE c13 CURSOR FOR
    SELECT deptnumb, deptname
      FROM org
      WHERE deptnumb < (SELECT AVG(deptnumb) FROM org);

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

  /* fetch cursor */
  EXEC SQL FETCH c13 INTO :deptnumb, :deptname;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %8d %-14s\n", deptnumb, deptname);

    EXEC SQL FETCH c13 INTO :deptnumb, :deptname;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbBasicSubselectUsingSubquery */

int TbBasicSubselectUsingCorrelatedSubquery(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SUBSELECT USING CORRELATED SUBQUERY.\n");

  /* display ORG table content */
  rc = OrgTbContentDisplay();

  printf("\n  Perform:\n");
  printf("    SELECT deptnumb, deptname\n");
  printf("      FROM org o1\n");
  printf("      WHERE deptnumb > (SELECT AVG(deptnumb)\n");
  printf("                          FROM org o2\n");
  printf("                          WHERE o2.division = o1.division)\n");
  printf("\n  Results:\n");
  printf("    DEPTNUMB DEPTNAME\n");
  printf("    -------- --------------\n");

  /* delcare cursor */
  EXEC SQL DECLARE c14 CURSOR FOR
    SELECT deptnumb, deptname
      FROM org o1
      WHERE deptnumb > (SELECT AVG(deptnumb)
                          FROM org o2
                          WHERE o2.division = o1.division);

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

  /* fetch cursor */
  EXEC SQL FETCH c14 INTO :deptnumb, :deptname;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %8d %-14s\n", deptnumb, deptname);

    EXEC SQL FETCH c14 INTO :deptnumb, :deptname;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbBasicSubselectUsingCorrelatedSubquery */

int TbSubselectUsingGroupingSets(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SUBSELECT USING GROUPING SETS.\n");

  rc = EmployeeTbPartialContentDisplay();

  printf("\n  Perform:\n");
  printf("    SELECT job, edlevel, SUM(comm)\n");
  printf("      FROM employee\n");
  printf("      WHERE job IN('DESIGNER', 'FIELDREP')\n");
  printf("      GROUP BY GROUPING SETS((job, edlevel), (job))\n");
  printf("\n  Results:\n");
  printf("    JOB      EDLEVEL SUM(COMM)\n");
  printf("    -------- ------- ----------\n");

  /* declare cursor */
  EXEC SQL DECLARE c15 CURSOR FOR
    SELECT job, edlevel, SUM(comm)
      FROM employee
      WHERE job IN('DESIGNER', 'FIELDREP')
      GROUP BY GROUPING SETS((job, edlevel), (job));

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

  /* fetch cursor */
  EXEC SQL FETCH c15 INTO :job:jobInd, :edlevel:edlevelInd,
                          :commSum:commSumInd;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    if (jobInd >= 0)
    {
      printf("    %-8s", job);
    }
    else
    {
      printf("    %-8s", "-");
    }
    if (edlevelInd >= 0)
    {
      printf(" %7d", edlevel);
    }
    else
    {
      printf(" %7s", "-");
    }
    if (commSumInd >= 0)
    {
      printf(" %10.2f", commSum);
    }
    else
    {
      printf(" %10s", "-");
    }
    printf("\n");

    EXEC SQL
      FETCH c15 INTO :job:jobInd, :edlevel:edlevelInd, :commSum:commSumInd;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbSubselectUsingGroupingSets */

int TbSubselectUsingRollup(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SUBSELECT USING ROLLUP.\n");

  rc = EmployeeTbPartialContentDisplay();

  printf("\n  Perform:\n");
  printf("    SELECT job, edlevel, SUM(comm)\n");
  printf("      FROM employee\n");
  printf("      WHERE job IN('DESIGNER', 'FIELDREP')\n");
  printf("      GROUP BY ROLLUP(job, edlevel)\n");
  printf("\n  Results:\n");
  printf("    JOB      EDLEVEL SUM(COMM)\n");
  printf("    -------- ------- ----------\n");

  /* declare cursor */
  EXEC SQL DECLARE c16 CURSOR FOR
    SELECT job, edlevel, SUM(comm)
      FROM employee
      WHERE job IN('DESIGNER', 'FIELDREP')
      GROUP BY ROLLUP(job, edlevel);

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

  /* fetch cursor */
  EXEC SQL FETCH c16 INTO :job:jobInd, :edlevel:edlevelInd,
                          :commSum:commSumInd;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    if (jobInd >= 0)
    {
      printf("    %-8s", job);
    }
    else
    {
      printf("    %-8s", "-");
    }
    if (edlevelInd >= 0)
    {
      printf(" %7d", edlevel);
    }
    else
    {
      printf(" %7s", "-");
    }
    if (commSumInd >= 0)
    {
      printf(" %10.2f", commSum);
    }
    else
    {
      printf(" %10s", "-");
    }
    printf("\n");

    EXEC SQL FETCH c16
      INTO :job:jobInd, :edlevel:edlevelInd, :commSum:commSumInd;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbSubselectUsingRollup */

int TbSubselectUsingCube(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SUBSELECT USING CUBE.\n");

  rc = EmployeeTbPartialContentDisplay();

  printf("\n  Perform:\n");
  printf("    SELECT job, edlevel, SUM(comm)\n");
  printf("      FROM employee\n");
  printf("      WHERE job IN('DESIGNER', 'FIELDREP')\n");
  printf("      GROUP BY CUBE(job, edlevel)\n");
  printf("\n  Results:\n");
  printf("    JOB      EDLEVEL SUM(COMM)\n");
  printf("    -------- ------- ----------\n");

  /* declare cursor */
  EXEC SQL DECLARE c17 CURSOR FOR
    SELECT job, edlevel, SUM(comm)
      FROM employee
      WHERE job IN('DESIGNER', 'FIELDREP')
      GROUP BY CUBE(job, edlevel);

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

  /* fetch cursor */
  EXEC SQL FETCH c17 INTO :job:jobInd, :edlevel:edlevelInd,
                          :commSum:commSumInd;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    if (jobInd >= 0)
    {
      printf("    %-8s", job);
    }
    else
    {
      printf("    %-8s", "-");
    }
    if (edlevelInd >= 0)
    {
      printf(" %7d", edlevel);
    }
    else
    {
      printf(" %7s", "-");
    }
    if (commSumInd >= 0)
    {
      printf(" %10.2f", commSum);
    }
    else
    {
      printf(" %10s", "-");
    }
    printf("\n");

    EXEC SQL
      FETCH c17 INTO :job:jobInd, :edlevel:edlevelInd, :commSum:commSumInd;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbSubselectUsingCube */

int TbSelectStatementUsingRecursiveCommonTableExpression(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM SELECT STATEMENT\n");
  printf("USING RECURSIVE COMMON TABLE EXPRESSION.\n");

  /* display DEPARTMENT table content */
  rc = DepartmentTbContentDisplay();

  printf("\n  Perform:\n");
  printf("    WITH rcte_department(deptno, deptname, admrdept)\n");
  printf("      AS(SELECT root.deptno, root.deptname, root.admrdept\n");
  printf("           FROM department root\n");
  printf("           WHERE root.deptname = 'SUPPORT SERVICES'\n");
  printf("           UNION ALL");
  printf(" SELECT child.deptno, child.deptname, child.admrdept\n");
  printf("                       ");
  printf("FROM department child, rcte_department parent\n");
  printf("                       WHERE child.admrdept = parent.deptno)\n");
  printf("      SELECT * FROM rcte_department\n");
  printf("\n  Results:\n");
  printf("    DEPTNO DEPTNAME                     ADMRDEPT\n");
  printf("    ------ ---------------------------- --------\n");

  /* cursor -- declare */
  EXEC SQL DECLARE c18 CURSOR FOR
    WITH rcte_department(deptno, deptname, admrdept)
      AS(SELECT root.deptno, root.deptname, root.admrdept
           FROM department root
           WHERE root.deptname = 'SUPPORT SERVICES'
           UNION ALL SELECT child.deptno, child.deptname, child.admrdept
                       FROM department child, rcte_department parent
                       WHERE child.admrdept = parent.deptno)
      SELECT * FROM rcte_department;

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

  /* fetch cursor */
  EXEC SQL FETCH c18 INTO :rcteDeptno, :rcteDeptname, :rcteAdmrdept;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %-6s %-28s %-8s\n", rcteDeptno, rcteDeptname, rcteAdmrdept);

    EXEC SQL FETCH c18 INTO :rcteDeptno, :rcteDeptname, :rcteAdmrdept;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* TbSelectStatementUsingRecursiveCommonTableExpression */

int OrgTbContentDisplay(void)
{
  struct sqlca sqlca;

  printf("\n  SELECT * FROM org\n");
  printf("    ");
  printf("DEPTNUMB DEPTNAME       MANAGER DIVISION   LOCATION\n");
  printf("    ");
  printf("-------- -------------- ------- ---------- --------------\n");

  EXEC SQL DECLARE c19 CURSOR FOR SELECT * FROM org;

  EXEC SQL OPEN c19;
  EMB_SQL_CHECK("cursor -- open");

  EXEC SQL FETCH c19 INTO :deptnumb, :deptname, :manager, :division,
                          :location;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %8d %-14s %7d %-10s %-14s\n",
           deptnumb, deptname, manager, division, location);

    EXEC SQL FETCH c19 INTO :deptnumb, :deptname, :manager, :division,
                            :location;
    EMB_SQL_CHECK("cursor -- fetch");
  }

  EXEC SQL CLOSE c19;
  EMB_SQL_CHECK("cursor -- close");

  return 0;
} /* OrgTableContentDisplay */

int DepartmentTbContentDisplay(void)
{
  struct sqlca sqlca;

  printf("\n  SELECT * FROM department\n");
  printf("    ");
  printf("DEPTNO DEPTNAME                     MGRNO  ADMRDEPT LOCATION\n");
  printf("    ");
  printf("------ ---------------------------- ------ -------- --------\n");

  EXEC SQL DECLARE c20 CURSOR FOR SELECT * FROM department;

  EXEC SQL OPEN c20;
  EMB_SQL_CHECK("cursor -- open");

  EXEC SQL FETCH c20 INTO :deptno, :departmentDeptname,
                          :mgrno:mgrnoInd, :admrdept,
                          :departmentLocation:departmentLocationInd;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %-6s %-28s", deptno, departmentDeptname);
    if (mgrnoInd >= 0)
    {
      printf(" %-6s", mgrno);
    }
    else
    {
      printf(" -     ");
    }
    printf(" %-8s", admrdept);
    if (departmentLocationInd >= 0)
    {
      printf(" %-16s", departmentLocation);
    }
    else
    {
      printf(" -     ");
    }
    printf("\n");

    EXEC SQL FETCH c20
      INTO :deptno, :departmentDeptname, :mgrno:mgrnoInd, :admrdept,
           :departmentLocation:departmentLocationInd;
    EMB_SQL_CHECK("cursor -- fetch");
  }

  EXEC SQL CLOSE c20;
  EMB_SQL_CHECK("cursor -- close");

  return 0;
} /* DepartmentTbContentDisplay */

int EmployeeTbPartialContentDisplay(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n  Perform:\n");
  printf("    SELECT job, edlevel, comm\n");
  printf("      FROM employee\n");
  printf("      WHERE job IN('DESIGNER', 'FIELDREP')\n");
  printf("\n  Results:\n");
  printf("    JOB      EDLEVEL COMM\n");
  printf("    -------- ------- ----------\n");

  /* declare cursor */
  EXEC SQL DECLARE c21 CURSOR FOR
    SELECT job, edlevel, comm
      FROM employee
      WHERE job IN('DESIGNER', 'FIELDREP');

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

  /* fetch cursor */
  EXEC SQL FETCH c21 INTO :job, :edlevel, :comm;
  EMB_SQL_CHECK("cursor -- fetch");

  while (sqlca.sqlcode != 100)
  {
    printf("    %-8s %7d %10.2f\n", job, edlevel, comm);

    EXEC SQL FETCH c21 INTO :job, :edlevel, :comm;
    EMB_SQL_CHECK("cursor -- fetch");
  }

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

  return 0;
} /* EmployeeTbPartialContentDisplay */

int TbSelectUsingQuerySampling(void)
{
  int rc = 0;
  struct sqlca sqlca;

  printf("\n-----------------------------------------------------------");
  printf("\nUSE THE SQL STATEMENTS:\n");
  printf("  DECLARE CURSOR\n");
  printf("  OPEN\n");
  printf("  FETCH\n");
  printf("  CLOSE\n");
  printf("TO PERFORM A SELECT USING QUERY SAMPLING.\n");
 
  printf("\nCOMPUTING AVG(SALARY) WITHOUT SAMPLING \n");
  printf("\n  Perform:\n");
  printf("    SELECT AVG(salary) FROM employee \n");
  printf("\n  Results:\n");
  printf("    AVG SALARY\n");
  printf("    ----------\n");
     
  /* declare cursor */
  EXEC SQL DECLARE c22 CURSOR FOR SELECT AVG(salary) FROM employee;

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

  /* fetch cursor */
  EXEC SQL FETCH c22 INTO :avgSalary;
  EMB_SQL_CHECK("cursor -- fetch");

  if (sqlca.sqlcode != 100)
    printf("    %.2f \n", avgSalary);

  /* close cursor */
  EXEC SQL CLOSE c22;
  EMB_SQL_CHECK("cursor -- close");
  
  printf("\nCOMPUTING AVG(SALARY) WITH QUERY SAMPLING");
  printf("\n  - ROW LEVEL SAMPLING ");
  printf("\n  - BLOCK LEVEL SAMPLING \n");
  printf("\n  ROW LEVEL SAMPLING : USE THE KEYWORD 'BERNOULLI'\n");
  printf("\nFOR A SAMPLING PERCENTAGE OF P, EACH ROW OF THE TABLE IS\n");
  printf("SELECTED FOR THE INCLUSION IN THE RESULT WITH A PROBABILITY\n");
  printf("OF P/100, INDEPENDENTLY OF THE OTHER ROWS IN T\n"); 
  
  printf("\n  Perform:\n");
  printf("    SELECT AVG(salary) FROM employee TABLESAMPLE BERNOULLI(25)");
  printf(" REPEATABLE(5)\n");
  printf("\n  Results:\n");
  printf("    AVG SALARY\n");
  printf("    ----------\n");
     
  /* declare cursor */
  EXEC SQL DECLARE c23 CURSOR FOR SELECT AVG(salary) FROM employee 
    TABLESAMPLE BERNOULLI(25) REPEATABLE(5);

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

  /* fetch cursor */
  EXEC SQL FETCH c23 INTO :avgSalary;
  EMB_SQL_CHECK("cursor -- fetch");

  if (sqlca.sqlcode != 100)
    printf("    %.2f \n", avgSalary);

  /* close cursor */
  EXEC SQL CLOSE c23;
  EMB_SQL_CHECK("cursor -- close");
  
  printf("\n\n  BLOCK LEVEL SAMPLING : USE THE KEYWORD 'SYSTEM'\n");
  printf("\nFOR A SAMPLING PERCENTAGE OF P, EACH ROW OF THE TABLE IS\n");
  printf("SELECTED FOR INCLUSION IN THE RESULT WITH A PROBABILITY\n");
  printf("OF P/100, NOT NECESSARILY INDEPENDENTLY OF THE OTHER ROWS\n"); 
  printf("IN T, BASED UPON AN IMPLEMENTATION-DEPENDENT ALGORITHM\n");
  
  printf("\n  Perform:\n");
  printf("    SELECT AVG(salary) FROM employee TABLESAMPLE SYSTEM(50)");
  printf(" REPEATABLE(1234)\n");
  printf("\n  Results:\n");
  printf("    AVG SALARY\n");
  printf("    ----------\n");
     
  /* declare cursor */
  EXEC SQL DECLARE c24 CURSOR FOR SELECT AVG(salary)FROM employee 
    TABLESAMPLE SYSTEM(50) REPEATABLE(1234);

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

  /* fetch cursor */
  EXEC SQL FETCH c24 INTO :avgSalary;
  EMB_SQL_CHECK("cursor -- fetch");
  
  if (sqlca.sqlcode != 100)
    printf("    %.2f \n", avgSalary);

  /* close cursor */
  EXEC SQL CLOSE c24;
  EMB_SQL_CHECK("cursor -- close");
  
  printf("\nREPEATABLE CLAUSE ENSURES THAT REPEATED EXECUTIONS OF THAT\n");
  printf("TABLE REFERENCE WILL RETURN IDENTICAL RESULTS FOR THE SAME \n");
  printf("VALUE OF THE REPEAT ARGUMENT (IN PARENTHESIS). \n");
  
  return 0;
} /* TbSelectUsingQuerySampling */