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

  • On-demand Event Event Date: December 18, 2014 The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this webcast and join industry experts as …

  • On-demand Event Event Date: October 29, 2014 It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this webcast, Gene Kim discusses these survey findings and shares woeful tales of artifact management gone wrong! Gene also shares examples of how high-performing DevOps …

Most Popular Programming Stories

More for Developers

RSS Feeds