/**********************************************************************
* FILENAME :    CallSPWithInOutParam.c
*
*
* DESCRIPTION :
*               Simple ODBC example to SELECT data from a table via a
*               stored procedure which shows how to use return values,
*               an input paramater and an output parameter.
*
*               Illustrates the most basic call with the 2 types of
*               parameter and a return value, in the form :
*
*               {? = CALL Stored_Procedure (?, ?)}
*
* ODBC USAGE :
*               Drops and recreates a procedure 'InOutRet_Params'
*               Confirms the procedure exists via SQLProcedures ()
*               While record id not zero
*                   Prompt user for record id
*                   Executes the procedure using SQLExecDirect()
*                   For each result set:
*                       calls SQLNumResultCols() to establish no of
*                       columns in result set
*                       Loops using SQLFetch() until SQL_NO_DATA returned
*                       to obtain result set rows
*                       Calls SQLMoreResults() for next result set, if exist
*                   prints return value and output param value returned by
*                   the procedure.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <sql.h>
#include <sqlext.h>
#include <string.h>

#include "util.c"
#include "CallSPUtil.c"

int main () {

    SQLHENV  henv  = SQL_NULL_HENV;   	// Environment
    SQLHDBC  hdbc  = SQL_NULL_HDBC;   	// Connection handle
    SQLHSTMT hstmt = SQL_NULL_HSTMT;  	// Statement handle
    SQLRETURN retcode;			// Return status

    SQLCHAR * strCallSP   = "{? = CALL InOutRet_Params (?, ?)}";
    char    * strProcName = "InOutRet_Params";

    SQLSMALLINT columns; 	       // Number of columns in result-set
    int i, count;

    SQLINTEGER	pPersonID=-1;

    // SQLBindParameter variables.
    SWORD  RetParam = 1, OutParam = 1;
    SQLLEN cbRetParam = SQL_NTS, cbOutParam = SQL_NTS;

    // Allocate an environment handle
    retcode=SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
    CHECK_ERROR(retcode, "SQLAllocHandle(ENV)", henv, SQL_HANDLE_ENV);

    // Set ODBC version
    retcode=SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,
                          (void *) SQL_OV_ODBC3, 0);
    CHECK_ERROR(retcode, "SQLSetEnvAttr(SQL_ATTR_ODBC_VERSION)",
                henv, SQL_HANDLE_ENV);

    // Allocate a connection handle
    retcode=SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
    CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_DBC)",
                hdbc, SQL_HANDLE_DBC);

    // DSN
    retcode=SQLDriverConnect(hdbc, NULL, "DSN=DATASOURCE;", SQL_NTS, NULL, 0,
                             NULL, SQL_DRIVER_COMPLETE);
    CHECK_ERROR(retcode, "SQLDriverConnect(DATASOURCE)",
                hdbc, SQL_HANDLE_DBC);

    retcode = DropProcedure (hdbc, strProcName);
    retcode = CreateProcedure (hdbc, strProcName);
    retcode = ListProcedure (hdbc, strProcName);

    // Allocate a statement handle
    retcode=SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
    CHECK_ERROR(retcode, "SQLAllocHandle(SQL_HANDLE_STMT)",
                hstmt, SQL_HANDLE_STMT);

    // Bind the output parameter to variable RetParam.
    retcode = SQLBindParameter(hstmt, 1, SQL_PARAM_OUTPUT, SQL_C_SSHORT,
                               SQL_INTEGER, 0, 0, &RetParam, 0, &cbRetParam);
    CHECK_ERROR(retcode, "SQLBindParameter(SQL_PARAM_OUTPUT)",
                hstmt, SQL_HANDLE_STMT);

    // Bind input parameter
    retcode = SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_LONG,
                               SQL_INTEGER, 0, 0, &pPersonID, 0, NULL);
    CHECK_ERROR(retcode, "SQLBindParameter(SQL_PARAM_INPUT)",
                hstmt, SQL_HANDLE_STMT);

    // Bind the output parameter to variable OutParam.
    retcode = SQLBindParameter(hstmt, 3, SQL_PARAM_OUTPUT, SQL_C_SSHORT,
                               SQL_INTEGER, 0, 0, &OutParam, 0, &cbOutParam);
    CHECK_ERROR(retcode, "SQLBindParameter(SQL_PARAM_INPUT)",
                hstmt, SQL_HANDLE_STMT);

    retcode = SQLPrepare (hstmt, strCallSP, SQL_NTS);
    CHECK_ERROR(retcode, "SQLPrepare(SQL_HANDLE_STMT)",
                hstmt, SQL_HANDLE_STMT);

    SQLLEN  indicator, RowCount;;
    char *buf=malloc (255);

    while (pPersonID != 0) {
        // Get Person ID
        getInt ("\nPerson Id ", (int *) &pPersonID, 'N', 0);
        if (pPersonID==0) goto exit;

    	retcode = SQLExecute (hstmt);
        CHECK_ERROR(retcode, "SQLExecute(SQL_HANDLE_STMT)",
                    hstmt, SQL_HANDLE_STMT);

    	// For a more generic example, use SQLDescribeCol here to
        // find column names ...

    	printf ("\nPersonID   Firstname     Surname    "
                "Address       City");
    	printf ("\n--------   ---------     -------    "
                "-------       ----");

        do {
            // SQLNumResultCols() returns number of columns in result set.
            // if non zero use SQLFetch until SQL_NO_DATA returned
            retcode=SQLNumResultCols(hstmt, &columns);
           	CHECK_ERROR(retcode, "SQLNumResultCols()",
                        hstmt, SQL_HANDLE_STMT);
            printf ("\nColumns : %i", columns);
            if (columns > 0) {
                printf ("\nStart Fetch ...");
                while (SQLFetch(hstmt) != SQL_NO_DATA) {
            	    // Loop through the columns
            	    memset (buf, ' ', 255);
                    printf ("\n");
                    for (i = 1; i <= columns; i++) {
                        // retrieve column data as a string
                        retcode = SQLGetData(hstmt, i, SQL_C_CHAR,
                                             buf, 255, &indicator);
                        if (SQL_SUCCEEDED(retcode)) {
                            // Handle null columns
                            if (indicator == SQL_NULL_DATA)
                                strcpy (buf, "NULL");
                            buf=rtrim(buf, ' ');
                            if (i==1)
                                printf("%-8s ", buf);
                            else
                                printf("%-12s ", buf);
            		}
            	    }
                }
                printf ("\nEnd Fetch ...\n");
            }
            else {
                // SQLRowCount returns number of rows affected by INSERT,
                // UPDATE, DELETE or number of rows returned by a SELECT
                retcode=SQLRowCount(hstmt, &RowCount);
            	CHECK_ERROR(retcode, "SQLRowCount()", hstmt, SQL_HANDLE_STMT);
                printf ("\nRow count is : %i\n", (int) RowCount);
            }
        } while (SQLMoreResults(hstmt) == SQL_SUCCESS);

        printf("Return Parameter  : %d\n", RetParam);
        printf("Number of Records : %d\n", OutParam);
    }


exit:

    free (buf);

    printf ("\nComplete.\n");

    // Free handles
    // Statement
    if (hstmt != SQL_NULL_HSTMT)
        SQLFreeHandle(SQL_HANDLE_STMT, hstmt);

    // Connection
    if (hdbc != SQL_NULL_HDBC) {
        SQLDisconnect(hdbc);
        SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
    }

    // Environment
    if (henv != SQL_NULL_HENV)
        SQLFreeHandle(SQL_HANDLE_ENV, henv);

    return 0;
}

Further information