2015년 4월 29일 수요일

OCI Prepare - Bind rowid


OCILIB supports the Oracle ROWID type through C scalar string types (dtext).
The maximum size of an ROWID buffer is defined by the constant OCI_SIZE_ROWID
Example
#include "ocilib.h"

int main(void)
{
    OCI_Connection *cn;
    OCI_Statement *st1, *st2;
    OCI_Resultset *rs;

    char rowid[OCI_SIZE_ROWID+1];
    int value;

    if (!OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT))
        return EXIT_FAILURE;

    cn  = OCI_ConnectionCreate("db", "usr", "pwd", OCI_SESSION_DEFAULT);
 
    st1 = OCI_StatementCreate(cn);
    st2 = OCI_StatementCreate(cn);

    OCI_Prepare(st1, "update test_fetch set code = :i where rowid = :s");
    OCI_BindInt(st1, ":i", &value);
    OCI_BindString(st1, ":s", rowid, sizeof(rowid)-1);

    OCI_ExecuteStmt(st2, "select code, rowid from test_fetch for update");

    rs = OCI_GetResultset(st2);

    while (OCI_FetchNext(rs))
    {
        value = OCI_GetInt(rs, 1);
        strcpy(rowid, OCI_GetString(rs, 2));
   
        /* updating value with some computation ... */
        value = (value + 4 ) % 2;

        OCI_Execute(st1);
    }

    OCI_Commit(cn);

    OCI_Cleanup();

    return EXIT_SUCCESS;
}

OCI returning - Register

Detailed Description

OCILIB supports the Oracle feature 'Returning into' for DML statements.
Let's Oracle talk about this features:
'Using the RETURNING clause with a DML statement allows you to essentially combine two SQL statements into one, possibly saving you a server round-trip. This is accomplished by adding an extra clause to the traditional UPDATE, INSERT, and DELETE statements. The extra clause effectively adds a query to the DML statement. In the OCI, the values are returned to the application through the use of OUT bind variables.'
OCILIB implements this features by providing a set of functions that allows to register output placeholders for the returned values. Once the DML is executed with OCI_Execute(), the output returned data is available through a regular resultset object that can be fetched.
Note:
Array binding interface is also supported with 'returning into' DML statement. Every iteration (or row of given arrays) generates an resultset object. Once a resultset is fetched, the next on can be retrieved with OCI_GetNextResultset()
Note:
OCI_Long are not supported for 'returning into' clause .This is a limitation imposed by Oracle.
OCI_Column objects retrieved from output OCI_Resultset have the following particularities:
  • their names are the provided bind names to the DML statement (by example, ':out1'). So any call to the functions OCI_GetXXX2() should be aware of it
  • The columns detailed SQL attributes might be not all set or accurate. By example, the scale and precision are not set, the SQL type is the one chosen by OCILIB regarding the OCILIB object datatype and might be slightly different from the real one.
Example
#include "ocilib.h"

int main(void)
{
    OCI_Connection *cn;
    OCI_Statement  *st;
    OCI_Resultset  *rs;

    if (!OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT))
        return EXIT_FAILURE;

    cn = OCI_ConnectionCreate("db", "usr", "pwd", OCI_SESSION_DEFAULT);
    st = OCI_StatementCreate(cn);

    OCI_Prepare(st, "update products set code = code+10 returning code into :i");
    OCI_RegisterInt(st, ":i");
    OCI_Execute(st);
  
    rs = OCI_GetResultset(st);

    while (OCI_FetchNext(rs))
        printf("%i\n", OCI_GetInt(rs, 1));
 
    printf("count : %i\n", OCI_GetRowCount(rs));
  
    OCI_Commit(cn);
    OCI_Cleanup();

    return EXIT_SUCCESS;
}

OCI Queue async - Advanced Queuing

Detailed Description

OCILIB supports Oracle Advanced Queues features
Let's Oracle talk about this features !
Oracle Queues (from Oracle Streams - Advanced Queuing User's Guide)
Oracle Streams AQ provides database-integrated message queuing functionality. It is built on top of Oracle Streams and leverages the functions of Oracle Database so that messages can be stored persistently, propagated between queues on different computers and databases, and transmitted using Oracle Net Services and HTTP(S). Because Oracle Streams AQ is implemented in database tables, all operational benefits of high availability, scalability, and reliability are also applicable to queue data. Standard database features such as recovery, restart, and security are supported by Oracle Streams AQ. You can use database development and management tools such as Oracle Enterprise Manager to monitor queues. Like other database tables, queue tables can be imported and exported.
OCILIB implementation
OCILIB provides a (nearly) full C implementation of Advanced Queues available in Oracle OCI and proposes the following datatypes :
  • OCI_Msg : Implementation of message to enqueue/dequeue from/to queues
  • OCI_Enqueue : Implementation of enqueuing process
  • OCI_Dequeue : Implementation of dequeuing process
  • OCI_Agent : Implementation of Advanced queues Agents
Note that the only AQ features not supported yet by OCILIB are :
  • Payloads of type AnyData
  • Enqueuing/dequeuing arrays of messages
  • Optionnal delivery mode introduced in 10gR2
OCILIB provides as well a C API to administrate queues and queue tables initially reserved to PL/SQL and Java (wrappers around PL/SQL calls). This API, based on internal PL/SQL calls wrapping the DBMS_AQADM packages procedures, allow the following actions :
  • create, alter, drop and purge queue tables (OCI_QueueTableXXX calls)
  • create, alter, drop, start, stop queues (OCI_QueueXXX calls)
Note that the user connected to the database needs particular privileges to manipulate or administrate queues (See Oracle Streams - Advanced Queuing User's Guide for more informations on these privileges)
Example
#include "ocilib.h"

int main(int argc, char *argv[])
{
    OCI_Connection *con;
    OCI_Enqueue    *enq;
    OCI_Dequeue    *deq;
    OCI_Msg        *msg;
    OCI_TypeInfo   *inf;
    OCI_Object     *obj;
   
    OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT);
     
    con = OCI_ConnectionCreate("db", "usr", "pwd", OCI_SESSION_DEFAULT);

    inf = OCI_TypeInfoGet(con, "MY_MESSAGE", OCI_TIF_TYPE);
    
    enq = OCI_EnqueueCreate(inf, "my_queue");
    deq = OCI_DequeueCreate(inf, "my_queue");
    
    msg = OCI_MsgCreate(inf);
    obj = OCI_ObjectCreate(con, inf);

    OCI_ObjectSetString(obj, "TITLE", "NEXT MEETING");
    OCI_ObjectSetString(obj, "CONTENT", "12:00 PM IN STARBUCKS");

    OCI_MsgSetObject(msg, obj);

    OCI_EnqueuePut(enq, msg);

    OCI_MsgFree(msg);
    OCI_ObjectFree(obj);

    OCI_Commit(con);
    
    msg = OCI_DequeueGet(deq);
    obj = OCI_MsgGetObject(msg);

    printf("MSG '%s' => %s\n",  OCI_ObjectGetString(obj, "TITLE"),
                                OCI_ObjectGetString(obj, "CONTENT"));

    OCI_EnqueueFree(enq);
    OCI_DequeueFree(deq);
    OCI_ConnectionFree(con);

    OCI_Cleanup();

   return EXIT_SUCCESS;
}

OCI Subscribtion register

Detailed Description

OCILIB supports Oracle 10gR2 feature Database Change Notifications (DCN) also named Continuous Query Notifications (CQN)
This features allows a client application to register notifications when some changes are made in a database :
  • Database status changes : startup and shutdown
  • Database objects changes :
    • DDL changes : alter or drop actions
    • DML changes : insert, delete, update actions
This feature can be really useful in applications that hold a cache of data. Instead of refreshing data periodically by connecting to the server, the application could only refresh modified data when necessary or perform specific tasks depending on the events. It saves application time, network traffic and can help the design of the application logic.
The database status change notification is also interesting to be informed of instance startup / shutdown
Check Oracle documentation for more details about this feature
Note:
No active database connection is required to receive the notifications as they are handled by the Oracle client using a dedicated socket connection to the server
Dabatase changes
The client application can be notified of any database status change (single DB or multiple DB in a RAC environment).
Object changes
The notifications of object changes are based on the registration of a query ('select' SQL statement).
Oracle server will notify of any changes of any object that is part of the statement result set.
Registering a statement will notify about any changes on its result set rows performed after the registration of the query.
The query can be a simple 'select * from table' or a complex query involving many tables and/or criteria in the where clause.
For Object changes, the notification can be at :
  • At Object level : only the object name (schema + object) is given
  • At row level : same that object level + RowID of the altered row
Warning:
Trying to use this features with a client/server version < 10gR2 will raise an error
Example
#include "ocilib.h"

#ifdef _WINDOWS
  #define sleep(x) Sleep(x*1000)
#endif

#define wait_for_events() sleep(5)

void event_handler(OCI_Event *event);
void error_handler(OCI_Error *err);

int main(void)
{
    OCI_Connection   *con;
    OCI_Subscription *sub;
    OCI_Statement    *st;
 
    printf("=> Initializing OCILIB in event mode...\n\n");

    if (!OCI_Initialize(error_handler, NULL, OCI_ENV_EVENTS))
        return EXIT_FAILURE;

    printf("=> Connecting to usr@db...\n\n");

    con = OCI_ConnectionCreate("db", "usr", "pwd", OCI_SESSION_DEFAULT);
    
    OCI_SetAutoCommit(con, TRUE);
 
    printf("=> Creating statement...\n\n");

    st  = OCI_StatementCreate(con);

    printf("=> Creating tables...\n\n");

    OCI_ExecuteStmt(st, "create table table1(code number)");
    OCI_ExecuteStmt(st, "create table table2(str varchar2(10))");

    printf("=> Registering subscription...\n\n");

    sub = OCI_SubscriptionRegister(con, "sub-00", OCI_CNT_ALL, event_handler, 5468, 0);

    printf("=> Adding queries to be notified...\n\n");

    OCI_Prepare(st, "select * from table1");
    OCI_SubscriptionAddStatement(sub, st);

    OCI_Prepare(st, "select * from table2");
    OCI_SubscriptionAddStatement(sub, st);

    printf("=> Executing some DDL operation...\n\n");

    OCI_ExecuteStmt(st, "alter table table1 add price number");

    wait_for_events();
 
    printf("=> Executing some DML operation...\n\n");

    OCI_ExecuteStmt(st, "insert into table1 values(1, 10.5)");
    OCI_ExecuteStmt(st, "insert into table2 values('shoes')");

    OCI_ExecuteStmt(st, "update table1 set price = 13.5 where code = 1");
    OCI_ExecuteStmt(st, "delete from table2 ");

    wait_for_events();

    printf("=> Droping tables...\n\n");

    OCI_ExecuteStmt(st, "drop table table1");
    OCI_ExecuteStmt(st, "drop table table2");

    wait_for_events();

    printf("=> Disconnecting from DB...\n\n");

    OCI_ConnectionFree(con);

    printf("=> Stopping the remote database...\n\n");

    OCI_DatabaseShutdown("db", "sys", "sys",   
                         OCI_SESSION_SYSDBA,
                         OCI_DB_SDM_FULL,
                         OCI_DB_SDF_IMMEDIATE);

    wait_for_events();;

    printf("=> Starting the remote database...\n\n");

    OCI_DatabaseStartup("db", "sys", "sys",   
                         OCI_SESSION_SYSDBA,
                         OCI_DB_SPM_FULL,
                         OCI_DB_SPF_FORCE,
                         NULL);

    wait_for_events();

    printf("=> Unregistering subscription...\n\n");

    OCI_SubscriptionUnregister(sub);

    printf("=> Cleaning up OCILIB resources...\n\n");

    OCI_Cleanup();

    printf("=> Done...\n\n");

    return EXIT_SUCCESS;
}

void error_handler(OCI_Error *err)
{
    int         err_type = OCI_ErrorGetType(err);
    const char *err_msg  = OCI_ErrorGetString(err);

    printf("** %s - %s\n", err_type == OCI_ERR_WARNING ? "Warning" : "Error", err_msg);
}

void event_handler(OCI_Event *event)
{
    unsigned int type     = OCI_EventGetType(event);
    unsigned int op       = OCI_EventGetOperation(event);
    OCI_Subscription *sub = OCI_EventGetSubscription(event);

    printf("** Notification      : %s\n\n", OCI_SubscriptionGetName(sub));
    printf("...... Database      : %s\n",   OCI_EventGetDatabase(event));

    switch (type)
    {
        case OCI_ENT_STARTUP:
            printf("...... Event         : Startup\n");
            break;
        case OCI_ENT_SHUTDOWN:
            printf("...... Event         : Shutdown\n");
            break;
        case OCI_ENT_SHUTDOWN_ANY:
            printf("...... Event         : Shutdown any\n");
            break;
        case OCI_ENT_DROP_DATABASE:
            printf("...... Event         : drop database\n");
            break;
        case OCI_ENT_DEREGISTER:
            printf("...... Event         : deregister\n");
            break;
         case OCI_ENT_OBJECT_CHANGED:
            
            printf("...... Event         : object changed\n");
            printf("........... Object   : %s\n", OCI_EventGetObject(event));
      
            switch (op)
            {
                case OCI_ONT_INSERT:
                    printf("........... Action   : insert\n");
                    break;
                case OCI_ONT_UPDATE:
                    printf("........... Action   : update\n");
                    break;
                case OCI_ONT_DELETE:
                    printf("........... Action   : delete\n");
                    break;
                case OCI_ONT_ALTER:
                    printf("........... Action   : alter\n");
                    break;
                case OCI_ONT_DROP:
                    printf("........... Action   : drop\n");
                    break;
            }
                    
            if (op < OCI_ONT_ALTER)
                printf("........... Rowid    : %s\n",  OCI_EventGetRowid(event));
        
            break;
    }
    
    printf("\n");
}

OCI Hash table

Detailed Description

OCILIB uses hash tables internally for index/name columns mapping.
OCILIB makes public its hash table�s implementation public for general purpose uses.
OCI_HashTable objects manage string keys / values that can be :
  • integers
  • strings
  • pointers
This hash table implementation :
  • handle collisions
  • allows multiple values per key
Internal conception
  • The hash table is composed of an array of slots.
  • Each slot can hold a linked list of entries (one per key)
  • Each entry can hold a linked list of values
Note:
  • The internal hash function computes the index in the array where the entry has to be inserted/looked up.
Collisions are handled by chaining method.
#include "ocilib.h"

int main(void)
{
    int i, n;
    OCI_Connection *cn;
    OCI_Statement  *st;
    OCI_Resultset  *rs;
    OCI_HashTable *table;
    OCI_HashEntry *e;
    OCI_HashValue *v;

    if (!OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT))
        return EXIT_FAILURE;

    cn    = OCI_ConnectionCreate("db", "usr", "pwd", OCI_SESSION_DEFAULT);
    st    = OCI_StatementCreate(cn);
    table = OCI_HashCreate(256, OCI_HASH_INTEGER);

    /* fill the hash table with data from DB */

    OCI_ExecuteStmt(st, "select code, name from products");
                  
    rs = OCI_GetResultset(st);

    while (OCI_FetchNext(rs))
        OCI_HashAddInt(table, OCI_GetString2(rs, "name"), OCI_GetInt2(rs, "code"));

    printf("%d row(s) fetched\n", OCI_GetRowCount(rs));

    /* lookup an entry */
   
    printf("code for %s is : %d\n", "Cars", OCI_HashGetInt(table, "Cars"));

    /* browse the hash table */

    n = OCI_HashGetSize(table);

    for (i = 0; i < n; i++)
    {
        e = OCI_HashGetEntry(table, i);

        while (e != NULL)
        {
            printf (">key: '%s'\n", e->key);

            v = e->values;

            while (v != NULL)
            {
                printf ("..... value : '%i'\n", v->value.num);
                v = v->next;
            }

            e = e->next;
        }
    }

    /* destroy table */

    OCI_HashFree(table);
    
    OCI_Cleanup();
 
    return EXIT_SUCCESS;
}

OCI executeStmt, immediate - with format

Detailed Description

OCILIB offers some smart routines that takes a variable number of arguments in order to minimize OCILIB function calls and reduce the amount of code lines
On Windows platforms, the target programming language must support the __cdecl calling convention
Note:
OCI_Immediate() and OCI_ImmediateFmt() support all OCILIB supported types for output result, except :
  • OCI_Long
  • OCI_Statement If a query output result contains one of these unsupported types, the function returns FALSE
In the parameter list, every output placeholder MUST be preceded by an integer parameter that indicates the type of the placeholder in order to handle correctly the given pointer.
Possible values for indicating placeholders type :
  • OCI_ARG_SHORT ------> short *
  • OCI_ARG_USHORT -----> unsigned short *
  • OCI_ARG_INT --------> int *
  • OCI_ARG_UINT -------> unsigned int*
  • OCI_ARG_BIGINT -----> big_int *
  • OCI_ARG_BIGUINT ----> unsigned big_int *
  • OCI_ARG_DOUBLE ----> double *
  • OCI_ARG_TEXT -------> dtext *
  • OCI_ARG_RAW --------> void *
  • OCI_ARG_DATETIME ---> OCI_Date *
  • OCI_ARG_LOB --------> OCI_Lob *
  • OCI_ARG_FILE -------> OCI_File *
  • OCI_ARG_TIMESTAMP --> OCI_Timstamp *
  • OCI_ARG_INTERVAL ---> OCI_Interval *
  • OCI_ARG_OBJECT -----> OCI_Object *
  • OCI_ARG_COLLECTION -> OCI_Coll *
  • OCI_ARG_REF --------> OCI_Ref *
Note:
For output strings and Raws, returned data is copied to the given buffer instead of returning a pointer the real data. So these buffers must be big enough to hold the column content. No size check is performed.
  • For strings, only the real string is copied.
  • For Raws, the number of bytes copied is the column size
Warning:
Input parameters for formatted function only support a restricted set of datatypes !
Supported input identifiers :
  • 's' : (dtext *) ----------> input string (quotes are added)
  • 'm' : (dtext *) ----------> metadata string (no quotes added)
  • 't' : (OCI_Date *) -------> Date
  • 'p' : (OCI_Timestamp *) --> timestamp
  • 'v' : (OCI_Interval *) ---> Interval
  • 'i' : (int) --------------> signed 32 bits integer
  • 'u' : (unsigned int) -----> unsigned 32 bits integer
  • 'li' : (big_int) ----------> signed 64 bits integer
  • 'lu' : (big_uint) ---------> unsigned 64 bits integer
  • 'hi' : (short) ------------> signed 16 bits integer
  • 'hu' : (unsigned short) ---> unsigned 16 bits integer
  • 'g' : (double ) ----------> Numerics
  • 'r' : (OCI_Ref *) --------> Reference
  • 'o' : (OCI_Object *) -----> Object (not implemented yet)
  • 'c' : (OCI_Coll *) -------> collection (not implemented yet)
Example
#include "ocilib.h"

int main(void)
{
    OCI_Connection *cn;
    OCI_Statement  *st;
    OCI_Resultset  *rs;
 
    int code = 1;
    char name[50];
  
    if (!OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT))
        return EXIT_FAILURE;

    cn = OCI_ConnectionCreate("db", "usr", "pwd", OCI_SESSION_DEFAULT);
    st = OCI_StatementCreate(cn);
 
    /* sql format with params ----------------------------------------------- */

    OCI_ExecuteStmtFmt(st, "select article from test_fetch where code = %i", code);

    rs = OCI_GetResultset(st);

    while (OCI_FetchNext(rs))
        printf("article : %s\n", OCI_GetString(rs, 1));

    /* sql immediate (parse, exec, one fetch) ------------------------------- */

    OCI_Immediate(cn, "select code, article from test_fetch where code = 1", 
                  OCI_ARG_INT, &code, OCI_ARG_TEXT, name);

    printf("article : %s - code %i\n", name, code);

    /* sql immediate (parse, exec, one fetch) with params ------------------- */

    
    OCI_ImmediateFmt(cn, "select article from test_fetch where code = %i", 
                          code, OCI_ARG_TEXT, name);

    printf("article : %s\n", name);

    OCI_Cleanup();
 
    return EXIT_SUCCESS;
}

OCI Fetch file

#include "ocilib.h"

int main(void)
{
    OCI_Connection *cn;
    OCI_Statement *st;
    OCI_Resultset *rs;
    OCI_File *file;
    char buffer[256];
    int n;

    if (!OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT))
        return EXIT_FAILURE;

    cn = OCI_ConnectionCreate("db", "usr", "pwd", OCI_SESSION_DEFAULT);
    st = OCI_StatementCreate(cn);

    file = OCI_FileCreate(cn, OCI_CFILE);

    OCI_FileSetName(file, "MYDIR", "MyfileName");

    /* check if faile exists */

    if (OCI_FileExists(file))
    {
         printf("file size : %d\n", OCI_FileGetSize(file));
         printf("file dir  : %s\n", OCI_FileGetDirectory(file));
         printf("file name : %s\n", OCI_FileGetName(file));
    }

    /* bind for inserting into table */

    OCI_Prepare(st, "insert into my_bfile_table(code, value) values (1, :bfile)");
    OCI_BindFile(st, ":bfile", file);
    OCI_Execute(st);
    OCI_Commit(cn);

    /* free local file object */

    OCI_FileFree(file),

    /* fetch bfile data from table */

    OCI_ExecuteStmt(st, "select code, value from my_bfile_table");

    rs = OCI_GetResultset(st);

    while (OCI_FetchNext(rs))
    {
        file = OCI_GetFile(rs, 2);

        OCI_FileOpen(file);

        printf("file size  %d\n", OCI_FileGetSize(file));
        printf("file dir   %s\n", OCI_FileGetDirectory(file));
        printf("file name  %s\n", OCI_FileGetName(file));

        while (n = OCI_FileRead(file, buffer, sizeof(buffer)-1))
        {
            buffer[n] = 0;
            printf(buffer);
        }

        OCI_FileClose(file);
    }

    OCI_Cleanup();

    return EXIT_SUCCESS;
}

OCI fetch struct



#include "ocilib.h"

typedef struct product_t
{
    int        code;
    char      *name;
    double     price;
    OCI_Date  *creation;
} product_t;

typedef struct product_ind_t
{
    boolean    code;
    boolean    name;
    boolean    price;
    boolean    creation;
} product_ind_t;


int main(void)
{
    OCI_Connection *cn;
    OCI_Statement  *st;
    OCI_Resultset  *rs;

    product_t     prd;
    product_ind_t ind;

    char buf[100];

    int i = 0;

    if (!OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT))
        return EXIT_FAILURE;

    cn  = OCI_ConnectionCreate("db", "usr", "pwd", OCI_SESSION_DEFAULT);
    st  = OCI_StatementCreate(cn);

    OCI_ExecuteStmt(st, "select * from products");

    rs = OCI_GetResultset(st);

    OCI_SetStructNumericType(rs, 1,  OCI_NUM_INT);
    OCI_SetStructNumericType(rs, 3,  OCI_NUM_DOUBLE);
 
    while (OCI_FetchNext(rs))
    {
        i++;

        OCI_GetStruct(rs, &prd, &ind);

        OCI_DateToText(prd.creation, "DD-MM-YYYY", 100, buf);
       
        printf("row #%d              \n"
               "...prd.code     : %d \n"
               "...prd.name     : %s \n"
               "...prd.price    : %g \n"
               "...prd.creation : %s \n"
               "                     \n",
               i, prd.code, prd.name, prd.price, buf
              );
    }

    printf("\n\n%d row(s) fetched\n", OCI_GetRowCount(rs));

    OCI_Cleanup();

    return EXIT_SUCCESS;
}

OCI Prepare and execute - prepare_excute 1step

First, call OCI_StatementCreate() to allocate a statement handle. Then :
These two steps can be done together by calling OCI_ExecuteStmt() that prepares and executes in one go.

Example
#include "ocilib.h"

int main(void)
{
    OCI_Connection *cn;
    OCI_Statement  *st;
  
    if (!OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT))
        return EXIT_FAILURE;

    cn = OCI_ConnectionCreate("db", "usr", "pwd", OCI_SESSION_DEFAULT);
    st = OCI_StatementCreate(cn);
 
    /* prepare and execute in 2 steps */

    OCI_Prepare(st, "delete from test_fetch where code > 10");
    OCI_Execute(st);

    /* prepare/execute in 1 step */

    OCI_ExecuteStmt(st, "delete from test_fetch where code > 1");

    printf("%d row deleted", OCI_GetAffectedRows(st));

    OCI_Commit(cn);

    OCI_Cleanup();
 
    return EXIT_SUCCESS;
}

OCI Error handler - enable warnings



Example of warning handling
#include "ocilib.h"

void err_handler(OCI_Error *err)
{
    int   err_type = OCI_ErrorGetType(err);
    char *err_msg  = OCI_ErrorGetString(err);

    printf("%s - %s\n", err_type == OCI_ERR_WARNING ? "warning" : "error", err_msg);
}

int main(void)
{
    OCI_Connection *cn;

    if (!OCI_Initialize(err_handler, NULL, OCI_ENV_DEFAULT))
        return EXIT_FAILURE;

    OCI_EnableWarnings(TRUE);

    cn = OCI_ConnectionCreate("db", "usr", "expired_pwd_in_grace_period", 
                              OCI_SESSION_DEFAULT);

    /* ... application code here ... */

    OCI_Cleanup();

 
    return EXIT_SUCCESS;
}