プライマリ・コンテンツに移動
Pro*C/C++プログラマーズ・ガイド
12c リリース1(12.1)
B71397-03
目次へ移動
目次
索引へ移動
索引

前
次

サンプル・プログラム

次の2つのプログラムは、demoディレクトリにあります。

ansidyn1.pc

このプログラムは、ANSI動的SQLを使用したSQL文の処理方法を示します。このSQL文は実行時まで不明です。これは、ANSI動的SQLを使用するための最も簡単な(ただし、最も効率的というわけではない)方法を紹介することを目的としています。ここでは、ANSIと互換性のある値セマンティクスおよびANSI型コードを使用しています。ANSI SQLSTATEは、エラー番号用に使用されています。記述子名はリテラルです。すべての入力および出力には、ANSI可変キャラクタ・タイプが使用されます。

このプログラムでは、自分のユーザー名とパスワードを使用してORACLEに接続した後、SQL文を入力します。埋込み型ではなく通常のSQL構文を使用して有効なSQLまたはPL/SQL文を入力し、各文の終わりにセミコロンを付けてください。入力した文が処理されます。問合せのときはフェッチされた行が表示されます。

複数行の文を入力できます。最大1023文字まで入力できます。変数のサイズには制限があり、MAX_VAR_LENが255に定義されています。このプログラムでは、バインド変数は40、選択リスト項目も40まで処理できます。DML RETURNING句およびユーザー定義型は、値セマンティクスではサポートされていません。

次のように、mode = ansiに設定してプログラムをプリコンパイルします。

proc mode=ansi ansidyn1

mode=ansiに指定すると、動的およびtype_codeがANSIに設定されます。

/*******************************************************************
ANSI Dynamic Demo 1:  ANSI Dynamic SQL with value semantics,
                                   literal descriptor names
                                   and ANSI type codes

This program demonstates using ANSI Dynamic SQL to process SQL
statements which are not known until runtime.  It is intended to
demonstrate the simplest (though not the most efficient) approach
to using ANSI Dynamic SQL.  It uses ANSI compatible value semantics
and ANSI type codes. ANSI Sqlstate is used for error numbers. 
Descriptor names are literals. All input and output is through ANSI the
varying character type.

The program connects you to ORACLE using your username and password,
then prompts you for a SQL statement.  Enter legal SQL or PL/SQL 
statements using regular, not embedded, SQL syntax and terminate each 
statement with a seimcolon.  Your statement will be processed.  If it
is a query, the fetched rows are displayed.  

You can enter multiline statements.  The limit is 1023 characters.
There is a limit on the size of the variables, MAX_VAR_LEN, defined as 255.
This program processes up to 40 bind variables and 40 select-list items.
DML returning statments and user defined types are not supported with 
value semantics.

Precompile the program with mode=ansi, for example:
 
proc mode=ansi ansidyn1

Using mode=ansi will set dynamic and type_code to ansi.

*******************************************************************/

#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#include <stdlib.h>
#include <sqlcpr.h>

#define MAX_OCCURENCES 40
#define MAX_VAR_LEN    255
#define MAX_NAME_LEN   31

#ifndef NULL
#define NULL  0
#endif


/* Prototypes */
#if defined(__STDC__)
  void sql_error(void);
  int oracle_connect(void);
  int get_dyn_statement(void);
  int process_input(void);
  int process_output(void);
  void help(void);
#else
  void sql_error(/*_ void _*/);
  int oracle_connect(/*_ void _*/);
  int get_dyn_statement(/* void _*/);
  int process_input(/*_ void _*/);
  int process_output(/*_ void _*/);
  void help(/*_ void _*/);
#endif

EXEC SQL INCLUDE sqlca;

char SQLSTATE[6];

/* global variables */
EXEC SQL BEGIN DECLARE SECTION;
 char    dyn_statement[1024];
 char SQLSTATE[6];
EXEC SQL END DECLARE SECTION;




/* Define a buffer to hold longjmp state info. */
jmp_buf jmp_continue;

/* A global flag for the error routine. */
int parse_flag = 0;
/* A global flag to indicate statement is a select */
int select_found;   

void main()
{

    /* Connect to the database. */
    if (oracle_connect() != 0)
        exit(1);

    EXEC SQL WHENEVER SQLERROR DO sql_error();

    /* Allocate the input and output descriptors. */
    EXEC SQL ALLOCATE DESCRIPTOR 'input_descriptor';
    EXEC SQL ALLOCATE DESCRIPTOR 'output_descriptor';

    /* Process SQL statements. */
    for (;;) 
    {
        (void) setjmp(jmp_continue);

        /* Get the statement.  Break on "exit". */
        if (get_dyn_statement() != 0)
            break;

        /* Prepare the statement and declare a cursor. */
        parse_flag = 1;     /* Set a flag for sql_error(). */
        EXEC SQL PREPARE S FROM :dyn_statement;
        parse_flag = 0;     /* Unset the flag. */

        EXEC SQL DECLARE C CURSOR FOR S;

        /* Call the function that processes the input. */
        if (process_input())
            exit(1);
 
        /* Open the cursor and execute the statement. */
        EXEC SQL OPEN C USING DESCRIPTOR 'input_descriptor';

        /* Call the function that processes the output. */
        if (process_output())
            exit(1);

        /* Close the cursor. */
        EXEC SQL CLOSE C;

    }   /* end of for(;;) statement-processing loop */


    /* Deallocate the descriptors */
    EXEC SQL DEALLOCATE DESCRIPTOR 'input_descriptor';
    EXEC SQL DEALLOCATE DESCRIPTOR 'output_descriptor';

    EXEC SQL WHENEVER SQLERROR CONTINUE;
    EXEC SQL COMMIT WORK;
    puts("\nHave a good day!\n");

    EXEC SQL WHENEVER SQLERROR DO sql_error();
    return;
}



int get_dyn_statement()
{
    char *cp, linebuf[256];
    int iter, plsql;

    for (plsql = 0, iter = 1; ;)
    {
        if (iter == 1)
        {
            printf("\nSQL> ");
            dyn_statement[0] = '\0';
            select_found = 0;
        }
        
        fgets(linebuf, sizeof linebuf, stdin);

        cp = strrchr(linebuf, '\n');
        if (cp && cp != linebuf)
            *cp = ' ';
        else if (cp == linebuf)
            continue;

        if ((strncmp(linebuf, "SELECT", 6) == 0) ||
            (strncmp(linebuf, "select", 6) == 0))
        {
            select_found=1;;
        }

        if ((strncmp(linebuf, "EXIT", 4) == 0) ||
            (strncmp(linebuf, "exit", 4) == 0))
        {
            return -1;
        }

        else if (linebuf[0] == '?' ||
            (strncmp(linebuf, "HELP", 4) == 0) ||
            (strncmp(linebuf, "help", 4) == 0))
        {
            help();
            iter = 1;
            continue;
        }

        if (strstr(linebuf, "BEGIN") ||
            (strstr(linebuf, "begin")))
        {
            plsql = 1;
        }

        strcat(dyn_statement, linebuf);

        if ((plsql && (cp = strrchr(dyn_statement, '/'))) ||
            (!plsql && (cp = strrchr(dyn_statement, ';'))))
        {
            *cp = '\0';
            break;
        }
        else
        {
            iter++;
            printf("%3d  ", iter);
        }
    }
    return 0;
}


int process_input()
{
    int i;
    EXEC SQL BEGIN DECLARE SECTION;
      char name[31];
      int  input_count, input_len, occurs, ANSI_varchar_type;
      char input_buf[MAX_VAR_LEN];
    EXEC SQL END DECLARE SECTION;

    EXEC SQL DESCRIBE INPUT S USING DESCRIPTOR 'input_descriptor';
    EXEC SQL GET DESCRIPTOR 'input_descriptor' :input_count = COUNT; 
       
    ANSI_varchar_type=12;
    for (i=0; i < input_count; i++)
    {
        occurs = i +1;                       /* occurence is 1 based */
        EXEC SQL GET DESCRIPTOR 'input_descriptor' 
                 VALUE :occurs :name = NAME;
        printf ("\nEnter value for input variable %*.*s:  ", 10,31, name);
        fgets(input_buf, sizeof(input_buf), stdin);
        input_len = strlen(input_buf) - 1;  /* get rid of new line */
        input_buf[input_len] = '\0';        /* null terminate */
        EXEC SQL SET DESCRIPTOR 'input_descriptor'
                 VALUE :occurs TYPE = :ANSI_varchar_type, 
                               LENGTH = :input_len,
                               DATA = :input_buf;
    }
    return(sqlca.sqlcode);
}


int process_output()
{
   int i, j;
   EXEC SQL BEGIN DECLARE SECTION;
     int output_count, occurs, type, len, col_len;
     short indi;
     char data[MAX_VAR_LEN], name[MAX_NAME_LEN];
   EXEC SQL END DECLARE SECTION;
   if (!select_found)
       return(0);   

   EXEC SQL DESCRIBE OUTPUT S USING DESCRIPTOR 'output_descriptor';
   
   EXEC SQL GET DESCRIPTOR 'output_descriptor' :output_count = COUNT;


   printf ("\n");
   type = 12;            /* ANSI VARYING character type */
   len = MAX_VAR_LEN;    /* use the max allocated length */
   for (i = 0; i < output_count; i++)
    {
        occurs = i + 1;
        EXEC SQL GET DESCRIPTOR 'output_descriptor' VALUE :occurs
                 :name = NAME;
        printf("%-*.*s ", 9,9, name);
        EXEC SQL SET DESCRIPTOR 'output_descriptor' VALUE :occurs 
                 TYPE = :type, LENGTH = :len;
    }   
    printf("\n");

    /* FETCH each row selected and print the column values. */
    EXEC SQL WHENEVER NOT FOUND GOTO end_select_loop;

    for (;;)
    {
        EXEC SQL FETCH C INTO DESCRIPTOR 'output_descriptor';
        for (i=0; i < output_count; i++)
          {
            occurs = i + 1;
            EXEC SQL GET DESCRIPTOR 'output_descriptor' VALUE :occurs
                 :data = DATA, :indi = INDICATOR;
            if (indi == -1)       
              printf("%-*.*s ", 9,9, "NULL");
            else
              printf("%-*.*s ", 9,9, data);  /* simplified output formatting */ 
                          /* truncation will occur, but columns will line up */
          }                             
         printf ("\n");
    }
end_select_loop:
    return(0);
}



void help()
{
    puts("\n\nEnter a SQL statement or a PL/SQL block at the SQL> prompt.");
    puts("Statements can be continued over several lines, except");
    puts("within string literals.");
    puts("Terminate a SQL statement with a semicolon.");
    puts("Terminate a PL/SQL block (which can contain embedded semicolons)");
    puts("with a slash (/).");
    puts("Typing \"exit\" (no semicolon needed) exits the program.");
    puts("You typed \"?\" or \"help\" to get this message.\n\n");
}


void sql_error()
{
    /* ORACLE error handler */
    printf("\n\nANSI sqlstate: %s: ", SQLSTATE);
    printf ("\n\n%.70s\n",sqlca.sqlerrm.sqlerrmc);
    if (parse_flag)
        printf
        ("Parse error at character offset %d in SQL statement.\n",
           sqlca.sqlerrd[4]);

    EXEC SQL WHENEVER SQLERROR CONTINUE;
    EXEC SQL ROLLBACK WORK;
    longjmp(jmp_continue, 1);
}


int oracle_connect()
{
    EXEC SQL BEGIN DECLARE SECTION;
        VARCHAR  username[128];
        VARCHAR  password[32];
    EXEC SQL END DECLARE SECTION;

    printf("\nusername: ");
    fgets((char *) username.arr, sizeof username.arr, stdin);
    username.arr[strlen((char *) username.arr)-1] = '\0';
    username.len = (unsigned short)strlen((char *) username.arr);

    printf("password: ");
    fgets((char *) password.arr, sizeof password.arr, stdin);
    password.arr[strlen((char *) password.arr) - 1] = '\0';
    password.len = (unsigned short)strlen((char *) password.arr);


    EXEC SQL WHENEVER SQLERROR GOTO connect_error;

    EXEC SQL CONNECT :username IDENTIFIED BY :password;

    printf("\nConnected to ORACLE as user %s.\n", username.arr);

    return 0;

connect_error:
    fprintf(stderr, "Cannot connect to ORACLE as user %s\n", username.arr);
    return -1;
}

ansidyn2.pc

このプログラムは、ANSI動的SQLを使用したSQL文の処理方法を示します。このSQL文は実行時まで不明です。バッチ処理および参照セマンティクスのOracle拡張機能が使用されます。

このプログラムでは、自分のユーザー名とパスワードを使用してORACLEに接続した後、SQL文を入力します。埋込み型ではなく対話型のSQL構文を使用して有効なSQLまたはPL/SQL文を入力し、各文の終わりにセミコロンを付けてください。入力した文が処理されます。問合せのときはフェッチされた行が表示されます。

複数行の文を入力できます。最大1023文字まで入力できます。変数のサイズには制限があり、MAX_VAR_LENが255に定義されています。このプログラムでは、バインド変数は40、選択リスト項目も40まで処理できます。

次のように、dynamic = ansiに設定してプログラムをプリコンパイルします。

proc dynamic=ansi ansidyn2

/*******************************************************************
ANSI Dynamic Demo 2:  ANSI Dynamic SQL with reference semantics,
                           batch processing and global descriptor
                           names in host variables
                           
This program demonstates using ANSI Dynamic SQL to process SQL
statements which are not known until runtime.  It uses the Oracle
extensions for batch processing and reference semantics.

The program connects you to ORACLE using your username and password,
then prompts you for a SQL statement.  Enter legal SQL or PL/SQL 
statement using interactive, not embedded, SQL syntax, terminating the 
statement with a seimcolon.  Your statement will be processed.  If it
is a query, the fetched rows are displayed. 

If your statement has input bind variables (other than in a where clause),
the program will ask for an input array size and then allow you to enter 
that number of input values. If your statment has output, the program will
ask you for an output array size and will do array fetchng using that value.
It will also output the rows fetched in one batch together, so using a small
value for the output array size will improve the look of the output.  
For example, connected as scott/tiger, try select empno, ename from emp
with an output array size of 4;

You can enter multiline statements.  The limit is 1023 characters.
There is a limit on the size of the variables, MAX_VAR_LEN, defined as 255.
This program processes up to 40 bind variables and 40 select-list items.

Precompile with program with dynamic=ansi, for example:
 
proc dynamic=ansi ansidyn2

*******************************************************************/

#include <stdio.h>
#include <string.h>
#include <setjmp.h>
#include <stdlib.h>
#include <sqlcpr.h>


#define MAX_OCCURENCES  40
#define MAX_ARRSZ      100
#define MAX_VAR_LEN    255
#define MAX_NAME_LEN    31

#ifndef NULL
#define NULL  0
#endif


/* Prototypes */
#if defined(__STDC__)
  void sql_error(void);
  int oracle_connect(void);
  int get_dyn_statement(void);
  int process_input(void);
  int process_output(void);
  void rows_processed(void);
  void help(void);
#else
  void sql_error(/*_ void _*/);
  int oracle_connect(/*_ void _*/);
  int get_dyn_statement(/* void _*/);
  int process_input(/*_ void _*/);
  int process_output(/*_ void _*/);
  void rows_processed(/*_ void _*/);
  void help(/*_ void _*/);
#endif

EXEC SQL INCLUDE sqlca;

/* global variables */
char    dyn_statement[1024];                      /* statement variable     */
EXEC SQL VAR dyn_statement IS STRING(1024);

char  indesc[]="input_descriptor";                /* descriptor names       */
char outdesc[]="output_descriptor";
char   input[MAX_OCCURENCES][MAX_ARRSZ][MAX_VAR_LEN +1 ],    /* data areas */
      output[MAX_OCCURENCES][MAX_ARRSZ][MAX_VAR_LEN + 1];

short outindi[MAX_OCCURENCES][MAX_ARRSZ];        /* output indicators      */
short *iptr;

int   in_array_size;     /* size of input batch, that is, number of rows */
int   out_array_size;    /* size of input batch, that is, number of rows */
int   max_array_size=MAX_ARRSZ;   /* maximum arrays size used for allocates */

char *dml_commands[] = {"SELECT", "select", "INSERT", "insert",
                        "UPDATE", "update", "DELETE", "delete"};

int select_found, cursor_open = 0;

/* Define a buffer to hold longjmp state info. */
jmp_buf jmp_continue;

/* A global flag for the error routine. */
int parse_flag = 0;

void main()
{

    /* Connect to the database. */
    if (oracle_connect() != 0)
        exit(1);

    EXEC SQL WHENEVER SQLERROR DO sql_error();

    /* Allocate the input and output descriptors. */
    EXEC SQL FOR :max_array_size
             ALLOCATE DESCRIPTOR GLOBAL :indesc;
    EXEC SQL FOR :max_array_size
             ALLOCATE DESCRIPTOR GLOBAL :outdesc;

    /* Process SQL statements. */
    for (;;) 
    {
        (void) setjmp(jmp_continue);

        /* Get the statement.  Break on "exit". */
        if (get_dyn_statement() != 0)
            break;

        /* Prepare the statement and declare a cursor. */
        parse_flag = 1;     /* Set a flag for sql_error(). */
        EXEC SQL PREPARE S FROM :dyn_statement;
        parse_flag = 0;     /* Unset the flag. */

        EXEC SQL DECLARE C CURSOR FOR S;

        /* Call the function that processes the input. */
        if (process_input())
            exit(1);

        /* Open the cursor and execute the statement. */
        EXEC SQL FOR :in_array_size
            OPEN C USING DESCRIPTOR GLOBAL :indesc;
        cursor_open = 1;
 
        /* Call the function that processes the output. */
        if (process_output())
            exit(1);

        /* Tell user how many rows were processed. */
        rows_processed();

    }   /* end of for(;;) statement-processing loop */

 
    /* Close the cursor. */
    if (cursor_open)
      EXEC SQL CLOSE C;

    /* Deallocate the descriptors */
    EXEC SQL DEALLOCATE DESCRIPTOR GLOBAL :indesc;
    EXEC SQL DEALLOCATE DESCRIPTOR GLOBAL :outdesc;

    EXEC SQL WHENEVER SQLERROR CONTINUE;
    EXEC SQL COMMIT WORK RELEASE;
    puts("\nHave a good day!\n");

    EXEC SQL WHENEVER SQLERROR DO sql_error();
    return;
}



int get_dyn_statement()
{
    char *cp, linebuf[256];
    int iter, plsql;

    for (plsql = 0, iter = 1; ;)
    {
        if (iter == 1)
        {
            printf("\nSQL> ");
            dyn_statement[0] = '\0';
            select_found = 0;
        }
        
        fgets(linebuf, sizeof linebuf, stdin);

        cp = strrchr(linebuf, '\n');
        if (cp && cp != linebuf)
            *cp = ' ';
        else if (cp == linebuf)
            continue;

        if ((strncmp(linebuf, "SELECT", 6) == 0) ||
            (strncmp(linebuf, "select", 6) == 0))
        {
            select_found=1;;
        }

        if ((strncmp(linebuf, "EXIT", 4) == 0) ||
            (strncmp(linebuf, "exit", 4) == 0))
        {
            return -1;
        }

        else if (linebuf[0] == '?' ||
            (strncmp(linebuf, "HELP", 4) == 0) ||
            (strncmp(linebuf, "help", 4) == 0))
        {
            help();
            iter = 1;
            continue;
        }

        if (strstr(linebuf, "BEGIN") ||
            (strstr(linebuf, "begin")))
        {
            plsql = 1;
        }

        strcat(dyn_statement, linebuf);

        if ((plsql && (cp = strrchr(dyn_statement, '/'))) ||
            (!plsql && (cp = strrchr(dyn_statement, ';'))))
        {
            *cp = '\0';
            break;
        }
        else
        {
            iter++;
            printf("%3d  ", iter);
        }
    }
    return 0;
}


int process_input()
{
    int i, j;
    char name[31];
    int  input_count, input_len= MAX_VAR_LEN;
    int  occurs, string_type = 5;
    int  string_len;
    char arr_size[3];

    EXEC SQL DESCRIBE INPUT S USING DESCRIPTOR GLOBAL :indesc;
    EXEC SQL GET DESCRIPTOR GLOBAL :indesc :input_count = COUNT; 

    if (input_count > 0 && !select_found )
       {     /* get input array size */
          printf ("\nEnter value for input array size (max is %d) :  ", 
                           max_array_size);
        fgets(arr_size, 4, stdin);
        in_array_size = atoi(arr_size); 
       }
    else
       { 
         in_array_size = 1;
       }
    for (i=0; i < input_count; i++)
    {
        occurs = i +1;                       /* occurence is 1 based */
        EXEC SQL GET DESCRIPTOR GLOBAL :indesc 
                 VALUE :occurs :name = NAME;

        for (j=0; j < in_array_size; j++)
        {
          if (in_array_size == 1)
            printf ("\nEnter value for input variable %*.*s:  ",10,31, name);
          else 
            printf ("\nEnter %d%s value for input variable %*.*s:  ",
               j +1, ((j==0) ?  "st" :  (j==1) ? "nd" : (j==2) ? "rd" :"th"),
                      10,31, name);
          fgets(input[i][j], sizeof(input[i][j]), stdin);
          string_len = strlen(input[i][j]);
          input[i][j][string_len - 1 ] = '\0';   /* change \n to \0 */
        }
        EXEC SQL SET DESCRIPTOR GLOBAL :indesc
                 VALUE :occurs TYPE = :string_type, LENGTH = :input_len;
        EXEC SQL FOR :in_array_size
                 SET DESCRIPTOR GLOBAL :indesc
                     VALUE :occurs  REF DATA = :input[i];
    }

    return(sqlca.sqlcode);
}


int process_output()
{
   int i, j;
   int output_count, occurs;
   int type, output_len= MAX_VAR_LEN;
   char name[MAX_OCCURENCES][MAX_NAME_LEN];
   int rows_this_fetch=0, cumulative_rows=0;
   char arr_size[3];
   if (!select_found)
      return(0);   
   EXEC SQL DESCRIBE OUTPUT S USING DESCRIPTOR GLOBAL :outdesc;
   
   EXEC SQL GET DESCRIPTOR GLOBAL :outdesc :output_count = COUNT;
  
   if (output_count > 0 )
      {
        printf ("\nEnter value for output array size (max is %d) :  ", 
                       max_array_size);
        fgets(arr_size, 4, stdin);
        out_array_size = atoi(arr_size); 
      }
   if (out_array_size < 1)    /* must have at least one */
       out_array_size = 1;

   printf ("\n");
   
   for (i = 0; i < output_count; i++)
   {
      occurs = i + 1;
      EXEC SQL GET DESCRIPTOR GLOBAL :outdesc VALUE :occurs
               :type = TYPE, :name[i] = NAME;
      occurs = i + 1;                         /* occurence is one based */
      type = 5;  /* force all data to be null terminated character */
      EXEC SQL SET DESCRIPTOR GLOBAL :outdesc VALUE :occurs 
               TYPE = :type, LENGTH = :output_len;
   
      iptr = (short *)&outindi[i]; /* no mult-dimension non-char host vars */
      EXEC SQL FOR :out_array_size
               SET DESCRIPTOR GLOBAL :outdesc VALUE :occurs 
               REF DATA = :output[i], REF INDICATOR = :iptr;
   }   
   
   
   
   EXEC SQL WHENEVER NOT FOUND GOTO end_select_loop;
   
   /* print the column headings */
   for (j=0; j < out_array_size; j++)
      for (i=0; i < output_count; i++)
         printf("%-*.*s ", 9,9, name[i]);
   printf("\n");
   
   /* FETCH each row selected and print the column values. */
   for (;;)
   {
      EXEC SQL FOR :out_array_size 
              FETCH C INTO DESCRIPTOR GLOBAL :outdesc;
      rows_this_fetch = sqlca.sqlerrd[2] - cumulative_rows;
      cumulative_rows = sqlca.sqlerrd[2];
      if (rows_this_fetch)
      for (j=0; j < out_array_size && j < rows_this_fetch; j++)
      {           /* output by columns using simplified formatting */
         for (i=0; i < output_count; i++)
           {                              
                if (outindi[i][j] == -1)       
                   printf("%-*.*s ", 9, 9, "NULL");
               else
                  printf("%-*.*s ", 9, 9, output[i][j]);  /* simplified */
                              /* output formatting may cause truncation */
                              /* but columns will line up */
           } 
      }
       printf ("\n");
   }

end_select_loop:
   /* print any unprinted rows */
   rows_this_fetch = sqlca.sqlerrd[2] - cumulative_rows;
   cumulative_rows = sqlca.sqlerrd[2];
   if (rows_this_fetch)
     for (j=0; j < out_array_size && j < rows_this_fetch; j++)
       {           /* output by columns using simplified formatting */
         for (i=0; i < output_count; i++)
           {                              
              if (outindi[i][j] == -1)       
                   printf("%-*.*s ",9, 9, "NULL");
               else
                  printf("%-*.*s ", 9, 9, output[i][j]); 
            } 
        }
   return(0);
}

void rows_processed()
{  
   int i;
   for (i = 0; i < 8; i++)
     {
       if (strncmp(dyn_statement, dml_commands[i], 6) == 0)
         {
            printf("\n\n%d row%c processed.\n", sqlca.sqlerrd[2],
                       sqlca.sqlerrd[2] == 1 ? ' ' : 's');
            break;
         }
     }
   return;
}


void help()
{
    puts("\n\nEnter a SQL statement or a PL/SQL block at the SQL> prompt.");
    puts("Statements can be continued over several lines, except");
    puts("within string literals.");
    puts("Terminate a SQL statement with a semicolon.");
    puts("Terminate a PL/SQL block (which can contain embedded semicolons)");
    puts("with a slash (/).");
    puts("Typing \"exit\" (no semicolon needed) exits the program.");
    puts("You typed \"?\" or \"help\" to get this message.\n\n");
}


void sql_error()
{
    /* ORACLE error handler */
    printf ("\n\n%.70s\n",sqlca.sqlerrm.sqlerrmc);
    if (parse_flag)
        printf
        ("Parse error at character offset %d in SQL statement.\n",
           sqlca.sqlerrd[4]);

    EXEC SQL WHENEVER SQLERROR CONTINUE;
    EXEC SQL ROLLBACK WORK;
    longjmp(jmp_continue, 1);
}


int oracle_connect()
{
    EXEC SQL BEGIN DECLARE SECTION;
        VARCHAR  username[128];
        VARCHAR  password[32];
    EXEC SQL END DECLARE SECTION;

    printf("\nusername: ");
    fgets((char *) username.arr, sizeof username.arr, stdin);
    username.arr[strlen((char *) username.arr)-1] = '\0';
    username.len = (unsigned short)strlen((char *) username.arr);

    printf("password: ");
    fgets((char *) password.arr, sizeof password.arr, stdin);
    password.arr[strlen((char *) password.arr) - 1] = '\0';
    password.len = (unsigned short)strlen((char *) password.arr);


    EXEC SQL WHENEVER SQLERROR GOTO connect_error;

    EXEC SQL CONNECT :username IDENTIFIED BY :password;

    printf("\nConnected to ORACLE as user %s.\n", username.arr);

    return 0;

connect_error:
    fprintf(stderr, "Cannot connect to ORACLE as user %s\n", username.arr);
    return -1;
}