Faster and Friendlier Access to Oracle's OCI API

Add Oracle Access to Your App in the Blink of an Eye

Vincent Rogier's OCILIB is a free, open source, and platform-independent library, written in C, that accesses Oracle Databases for you. OCILIB's design goals include:

  • Encapsulating the powerful (but complex) OCI API
  • Hiding unnecessary complexity inherent in OCI
  • Providing a simple, readable, and reusable API
  • Offering 350 simple and straightforward APIs

OCILIB is written in pure ISO standard C and will build under any C90 compliant compiler. Certified platforms include Windows, Linux, HP/UX, Solaris, and AIX. Supported compilers are MS Visual Studio, Gnu GCC, MinGW, IBM's XLC, and CC. OCILIB works with Oracle versions 8i, 9i, 10g, and 11g. Because OCILIB is LGPL-licensed, you are free to use it in commercial products without worry.

For those of you who aren't keen on recompiling open source projects, Rogier supplies a nice ZIPped archive with WIN32 and WIN64 binaries that will be enough to get most people started just fine. But if you do need custom DLLs, for whatever reason, there are handy Visual Studio 2005 and Visual Studio 2008 project files right at hand.

An exhaustive list of features would be far longer than I can possibly include in this article, so I'll just give you what I feel are the "top 10" benefits of OCILIB (in no particular order!).

  1. Integrated smart define and fetch mechanism
  2. Full Unicode support
  3. Multi-row fetching
  4. Binding array Interface for fast and massive bulk operations
  5. Connection pooling (saves on resources)
  6. PL/SQL blocks
  7. LOBs and FILEs support
  8. Provides Hash tables, portable threads, and mutexes API
  9. Can load OCI libs at runtime (no Oracle libraries required at compile time)
  10. Can be compiled as a static lib or a shared (import) library

Getting Started

Take a look at the "Hello World" type example for OCILIB so you can see what it takes to make a simple connection to a database and then get some kind of SQL Select statement working:

 1 #include "ocilib.h"
 2
 3 int main(int argc, char *argv[])
 4 {
 5    OCI_Connection* cn;
 6    OCI_Statement* st;
 7    OCI_Resultset* rs;
 8
 9    OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT);
10    cn = OCI_ConnectionCreate("db", "user", "pwd",
                                OCI_SESSION_DEFAULT);
11    st = OCI_StatementCreate(cn);
12
13    OCI_ExecuteStmt(st, "select table_name,
                      num_rows from user_tables order by 1");
14    rs = OCI_GetResultset(st);
15
16    while (OCI_FetchNext(rs)) {
17       printf("table %-30s : %10i rows\n", OCI_GetString(rs,1),
                OCI_GetInt(s, 2));
18    }
19
20    OCI_Cleanup();
21    return EXIT_SUCCESS;
22 }

The setup starts in Lines 9-11 where you call OCI_Initialize(). You could have supplied a glogal error handler and an Oracle home path if desired there. You then can set up a connection with OCI_ConnectionCreate() with the usual database, username, and password params. At this point, you're ready to create a Dynamic SQL statement with OCI_StatementCreate() and OCI_ExecuteStmt(). For the example, you are simply querying the system catalogs to get a list of tables; this should work in ANY Oracle database, no matter how humble.

In the second half of the program, Lines 14-18, you establish a Result Set (in other words, make the output of a query available to the app) and then fetch results one by one. The OCI_GetString() and OCI_GetInt() pull the results from the current row in the Result Set, indexed by column number. At the end, OCI_Cleanup() frees all pending connection resources.

Connection Pooling with Threads

Okay, the first example is a bit pedestrian, but you must admit that you sure got a lot done with the 10 or so lines of code in the "meat" of the program. Now, try something more challenging—connection pooling with threads in only 40 lines of code!

 1 #include "ocilib.h"
 2
 3 #define MAX_THREADS 50
 4 #define MAX_CONN    10
 5
 6 void worker(OCI_Thread *thread, void *data)
 7 {
 8    OCI_Connection *cn =
         OCI_ConnPoolGetConnection((OCI_ConnPool *) data);
 9    char buf[100];
10
11    OCI_Immediate(cn, "select sysdate from dual",
                    OCI_ARG_TEXT, buf);
12    printf("Thread ID : %6d - Date : %s\n",
             OCI_HandleGetThreadID(thread), buf);
13
14    OCI_ConnectionFree(cn);
15 }
16
17 int main(void)
18 {
19    OCI_Thread *th[MAX_THREADS];
20    OCI_ConnPool *pool;
21    int i;
22
23    if (!OCI_Initialize(NULL, NULL, OCI_ENV_DEFAULT |
                          OCI_ENV_THREADED))
24    return EXIT_FAILURE;
25
26    pool = OCI_ConnPoolCreate("db", "user", "pwd",
27       OCI_SESSION_DEFAULT, 0, MAX_CONN, 1);
28
29    for (i = 0; i < MAX_THREADS; i++) {
30       th[i] = OCI_ThreadCreate();
31       OCI_ThreadRun(th[i], worker, pool);
32    }
33
34    for (i = 0; i < MAX_THREADS; i++) {
35       OCI_ThreadJoin(th[i]);
36       OCI_ThreadFree(th[i]);
37    }
38
39    OCI_Cleanup();
40    return EXIT_SUCCESS;
41 }

Faster and Friendlier Access to Oracle's OCI API

Starting off in main(), Lines #17-28 rehash what you did in the first example except that you did add OCI_ENV_THREADED to OCI_Initialize() this time around. Next, you're going to initialize the connection pool that all the threads will draw from. Back in the bad old days, developers had to write their own connection pooling code. In fact, if you are using Oracle 8i, OCILIB will do this for you. For Oracle 9i (and later), OCILIB uses the native connection pooling behind-the-scenes.

26    pool = OCI_ConnPoolCreate("db", "user", "pwd",
27       OCI_SESSION_DEFAULT, 0, MAX_CONN, 1);

The main parameters of interest are the last three: minimum number of connections to open, maximum connections to open, and next increment. For example, you could ask to start with four connections, a maximum of 32 open, and to increment by 8 each time you need more (and you're under the 32 open limit).

You'll skip directly to the thread creating and spawning in Lines #29-32.

29    for (i = 0; i < MAX_THREADS; i++) {
30       th[i] = OCI_ThreadCreate();
31       OCI_ThreadRun(th[i], worker, pool);
32    }

OCI_ThreadCreate() just initializes your thread handle, which you are storing in the th[] array for convenience sake. Next, you spawn the new thread (process) with OCI_ThreadRun() passing in the thread handle, pointer to a thread function to execute, and an arbitrary argument. It makes sense to pass in the connection handle (named "pool") that you are going to be sharing amongst all the happy threads.

Now, you must visualize several instances of the worker() function starting up, each with a unique thread handle and a copy of the pool handle. However, before the worker thread can do anything useful, it's got to get its own connection handle via OCI_ConnPoolGetConnection(). Your simple-minded app blithely assumes that it will always have enough resources available to get the job done. A real-world app would probably use OCI_ConnPoolSetNoWait(FALSE) to stall out the thread until a connection was freed up. It's also possible to manage connections by timeout using OCI_ConnPoolSetTimeout() to reap connections that have been idled for a specified duration.

At last, when the thread has completed its work in Lines #11-14, it can free its resources and await termination. Meanwhile, back in the main() program, the original program thread is in a rather simple loop where it is merely waiting on each thread to complete and freeing their resources in the same order in which they were launched. OCI_ThreadJoin() is a blocking call, so the main thread in fact sits and does nothing until each thread finishes and then the function returns. If the thread had already been dead, before OCI_ThreadJoin() was called, I believe it might return FALSE, but in your case you don't care to know how or when the child thread departed. All-in-all, the thread and pooling system strikes me as both simple in conception and sophisticated in operation.

Conclusion

It's not every day that I say this, but I can emphatically say that OCILIB implements an SQL database interface that is exactly the way I would design it myself and I would be hard pressed to come up with a better solution for C programmers. Of course, you have barely scratched the surface of what OCILIB could do for you. It would take probably a dozen articles to adequately cover topics such as working with Internal Large Objects (LOBs) and External Large Objects (FILEs), hash tables, binding arrays, fetching multiple rules, PL/SQL integration, and much much more. If I have piqued your curiosity in any way, my goal here has been achieved.



About the Author

Victor Volkman

Victor Volkman has been writing for C/C++ Users Journal and other programming journals since the late 1980s. He is a graduate of Michigan Tech and a faculty advisor board member for Washtenaw Community College CIS department. Volkman is the editor of numerous books, including C/C++ Treasure Chest and is the owner of Loving Healing Press. He can help you in your quest for open source tools and libraries, just drop an e-mail to sysop@HAL9K.com.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • CentreCorp is a fully integrated and diversified property management and real estate service company, specializing in the "shopping center" segment, and is one of the premier retail service providers in North America. Company executives travel a great deal, carrying a number of traveling laptops with critical current business data, and no easy way to back up to the network outside the office. Read this case study to learn how CentreCorp implemented a suite of business continuity services that included …

Most Popular Programming Stories

More for Developers

RSS Feeds