{"id":16,"date":"2010-02-04T22:05:06","date_gmt":"2010-02-04T22:05:06","guid":{"rendered":"http:\/\/oracle-internals.com\/blog\/?p=16"},"modified":"2014-01-26T22:11:42","modified_gmt":"2014-01-26T22:11:42","slug":"data-access-and-fun-with-upi","status":"publish","type":"post","link":"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/","title":{"rendered":"Data Access APIs\u2013Part 1: Fun with UPI"},"content":{"rendered":"<p>First, I\u2019d like to apologize to our good friend SQLLIB.\u00a0 Those of you who have been working with the Oracle Database for some time will notice that, while it too is a common data access library, I\u2019ve omitted it from this series of posts.<\/p>\n<p>No, it\u2019s not because of some personal vendetta against SQLLIB.\u00a0 In fact, I like SQLLIB (primarily for internals-related reasons), but I\u2019ve excluded it because it\u2019s not a user-oriented data access library and as such, I don\u2019t consider it a first class citizen.\u00a0 And, for those of you who may find yourself asking, \u201cwhat the hell is this SQLLIB thing Jonah\u2019s going on about?\u201d, it\u2019s a library commonly used by the Oracle Precompiler suite,\u00a0<a href=\"https:\/\/web.archive.org\/web\/20131219032901\/http:\/\/download.oracle.com\/docs\/cd\/E11882_01\/appdev.112\/e10825\/pc_01int.htm#sthref42\">not something people develop directly against<\/a>.\u00a0 Though, if there\u2019s enough interest, I may do a post on SQLLIB in the future.<\/p>\n<p>Regardless, in this series of articles, I\u2019m going to discuss and demonstrate the evolution of Oracle\u2019s UPI and OCI data access APIs.\u00a0 The articles are arranged as follows:<\/p>\n<ul>\n<li>Article 1: The User Program Interface (UPI)<\/li>\n<li>Article 2: The Oracle Call Interface Version 7 (OCI7)<\/li>\n<li>Article 3: The Oracle Call Interface Version 8 (OCI8)<\/li>\n<\/ul>\n<p>But, before we get too deep into UPI, we need to start at the beginning.<\/p>\n<p><strong>What is a Data Access Layer?<\/strong><br \/>\nA data access layer is a programming interface which allows developers to write programs capable of interacting with a database.\u00a0 So, whether you\u2019re using\u00a0<a href=\"https:\/\/web.archive.org\/web\/20131219032901\/http:\/\/www.quest.com\/toad-for-oracle\/\">Toad<\/a>,\u00a0<a href=\"https:\/\/web.archive.org\/web\/20131219032901\/http:\/\/www.oracle.com\/technology\/products\/database\/sql_developer\/index.html\">SQL Developer<\/a>,\u00a0<a href=\"https:\/\/web.archive.org\/web\/20131219032901\/http:\/\/download.oracle.com\/docs\/cd\/E11882_01\/server.112\/e10823\/toc.htm\">SQL*Plus<\/a>, or a custom application, each relies on a data access layer to talk to the Oracle Database.\u00a0 However, in this set of articles, we won\u2019t be discussing standard data access APIs like\u00a0<a href=\"https:\/\/web.archive.org\/web\/20131219032901\/http:\/\/en.wikipedia.org\/wiki\/Open_Database_Connectivity\">ODBC<\/a>\u00a0or\u00a0<a href=\"https:\/\/web.archive.org\/web\/20131219032901\/http:\/\/en.wikipedia.org\/wiki\/Open_Database_Connectivity\">JDBC<\/a>.\u00a0 Instead, we\u2019ll be taking a low-level look at proprietary Oracle APIs.<\/p>\n<p>Oracle-supported APIs can be classified into two sets, public and internal-only.<\/p>\n<p><strong>Public Oracle Data Access APIs<\/strong><br \/>\nThese are APIs which Oracle has documented, supported, and wants customers to use.<\/p>\n<p>Public APIs include:<\/p>\n<ul>\n<li>Oracle Call Interface (OCI)<\/li>\n<li>Oracle C++ Call Interface (OCCI)<\/li>\n<li>Standard APIs (ODBC, JDBC, .NET, OLE DB, \u2026)<\/li>\n<\/ul>\n<p><strong>Private, internal-only Oracle Data Access APIs<\/strong><br \/>\nThese are APIs which Oracle uses internally, does not provide external documentation for, and does not want customers using for various reasons.<\/p>\n<p>Private APIs include:<\/p>\n<ul>\n<li>User Program Interface (UPI)<\/li>\n<li>SQL Library Runtime (SQLLIB)<\/li>\n<li><em>Even older stuff than this\u2026<\/em><\/li>\n<\/ul>\n<p>In this article, we\u2019ll focus on the User Program Interface, as it has been the core of Oracle\u2019s data access for some time.<\/p>\n<p><strong>The User Program Interface<\/strong><br \/>\nWhile UPI was once a popular internal-only API used within Oracle, it is in a constant state of being phased out in favor of the Oracle Call Interface (OCI).\u00a0 Now, you may be asking, \u201cwhy are you doing a blog on something that\u2019s being phased out?\u201d\u00a0 Well, I\u2019m doing it for three reasons: 1. Over the years, quite a few people have asked me about it, 2. UPI is still in use, and 3. I think it\u2019s good for people to understand the evolutionary progress of how things got to where they are now.<\/p>\n<p>So, how are we going to see some basic UPI in action?\u00a0 Bring on the example!<\/p>\n<p><strong>A UPI-based Example Program<\/strong><br \/>\nBecause I strongly believe concepts are best demonstrated using an applied method, I\u2019ve taken an old Oracle OCI demonstration program and turned it into a very basic interactive SQL utility that allows you to execute basic SQL queries.\u00a0 The code kinda sucks because I spent very little time cleaning up the original, merged using a different style, don\u2019t do great error checking, and could do a few things more correctly.\u00a0 But, it\u2019s still a basic working example.<\/p>\n<p>However, before we get into the fun stuff, we have to deal with the legal issues.<\/p>\n<p><strong>The Inevitable Disclaimer<\/strong><br \/>\nIt must be noted that I haven\u2019t (yet) worked for Oracle, had access to the source code, or can guarantee proper execution of this on your database version or platform.\u00a0 The following is based on years of personal observation and research and while I\u2019ve tested it on 8i, 9i, 10g, and 11g, I make no guarantees.\u00a0\u00a0<span style=\"color: #ff0000; font-size: small;\">THE SOFTWARE AND EXAMPLES PROVIDED ON THIS PAGE ARE PROVIDED \u201cAS IS\u201d AND WITHOUT WARRANTY OF ANY KIND.\u00a0 THE AUTHOR DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.\u00a0 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES.\u00a0\u00a0<strong>THIS SOFTWARE IS PROVIDED FOR DEMONSTRATION PURPOSES ONLY AND IS NOT ENDORSED, VERIFIED, OR SUPPORTED BY ORACLE CORPORATION.\u00a0 THIS SOFTWARE SHOULD NOT BE USED IN A PRODUCTION ENVIRONMENT UNDER ANY CIRCUMSTANCES.<\/strong><\/span><\/p>\n<p>OK, now we can get to the fun stuff :-)<\/p>\n<p><strong>Prerequisite Knowledge<\/strong><br \/>\nThis blog entry assumes that you have a basic understanding of the C programming language as well as a rough idea of data access concepts such as parsing, binding, defining, and execution.<\/p>\n<p>OK, let\u2019s start!<\/p>\n<p><strong>Including the Oracle C Data Types<\/strong><br \/>\nBefore we can use UPI, we must first include the Oracle platform type definitions, oratypes.h.\u00a0 If you\u2019ve done any OCI programming, you\u2019ve no doubt run across this file.\u00a0 This file, located in $ORACLE_HOME\/rdbms\/public, can easily be included using:<\/p>\n<pre>#include \"oratypes.h\"                      \/* Oracle-declared Platform Types *\/<\/pre>\n<p>Next, we need the C complier to know something about UPI.<\/p>\n<p><strong>Including the UPI Definitions<\/strong><br \/>\nIn order for our program to make use of UPI, we must now include the UPI function prototypes and definitions, which are contained in a file called upidef.h.<\/p>\n<p>While I\u2019ve made several requests for a copy of Oracle\u2019s official upidef.h, they have been true to their internal-only mission and have never graced me with a copy.\u00a0 Consequently, it has taken me almost ten years to collect and research only a subset of the entire API.\u00a0 However, looking (optimistically) toward the future, I\u2019d like to avoid changing anything in my code should I eventually get a copy of the real upidef.h (hint, hint).\u00a0 Accordingly, I\u2019ve named my version of the definitions identically, and included the header file as follows:<\/p>\n<pre>#include \"upidef.h\"                        \/* Jonah's Spiffy UPI Definitions *\/<\/pre>\n<p>Now that we\u2019ve included the UPI definitions and function prototypes, we\u2019re ready to start building our program.\u00a0 The first step is to define an HDA.<\/p>\n<p><strong>Defining UPI\u2019s Host Data Area Structure<\/strong><br \/>\nSimilar to the concept of opaque handles, UPI uses an old technique of having the developer allocate a block of data which is passed back-and-forth to UPI.\u00a0 Internally, UPI maps offsets within this block to members of a C structure.\u00a0 As for definition, we\u2019re going to define an array containing 512 elements of type ub1 (unsigned char\/uint8_t).\u00a0 Depending on the platform and alignment-related issues, the HDA structure may also be allocated using an array of ub1, ub2, or ub4 with array-length changes being made accordingly.<\/p>\n<pre>static ub1      hda[512] = { 0 };                  \/* Host Data Area for UPI *\/<\/pre>\n<p><strong>Defining a Cursor Using UPI<\/strong><br \/>\nIn UPI, cursors are represented simply by a number.\u00a0 Accordingly, we define a cursor as an unsigned word.<\/p>\n<pre>static uword    cnum;                                       \/* Cursor Number *\/<\/pre>\n<p><strong>Logon to Oracle<\/strong><br \/>\nNext, we need to make a connection to the Oracle database.\u00a0 UPI provides a couple different logon functions, but we\u2019ll use upilog.\u00a0 upilog will connect to the orcl database and create a session using scott\/tiger.\u00a0 During this process, the HDA structure will be updated to contain some information about this connection.<\/p>\n<pre>    if (upilog(&amp;hda[0],\r\n               (text *) \"scott\/tiger@orcl\",\r\n               -1, 0,\r\n               -1, 0,\r\n               -1, UPILFDEF, UPILMDEF))<\/pre>\n<p><strong>Opening a Cursor<\/strong><br \/>\nNow that we\u2019re connected to the server, we need to get ready to process a statement.\u00a0 However, before we can process a statement, we must open a cursor.\u00a0 UPI provides the upiopn call for this.<\/p>\n<pre>    if (upiopn(&amp;hda[0], &amp;cnum, -1))<\/pre>\n<p><strong>Parsing a SQL Statement<\/strong><br \/>\nNow that the user has passed-in a query for processing, we need to parse the query.\u00a0 UPI provides several functions for parsing, including the upipse and upiosd functions.\u00a0 In our example, we\u2019ll use upiosd, which parses the SQL statement and associates it with our cursor.\u00a0 In our usage, we\u2019re telling UPI to defer the actual parse [using (ub4) 1] until we call a describe\/execution function.\u00a0 Similarly, rather than specific version 6\/7 mode, we\u2019re telling the system to parse the statement in standard (default) mode [via (sword) 1].<\/p>\n<pre>        if (upiosd(&amp;hda[0], cnum, (text *) sql_statement, -1,\r\n                   (ub4) 1, (sword) 1))<\/pre>\n<p><strong>Describing the Select-List<\/strong><br \/>\nAs we\u2019re allowing the user to enter dynamic SQL queries, we need to describe the select-list prior to defining the output variables and fetching the data.\u00a0 UPI provides several functions for describing the select list, including the upidpr, upidqr, and upidsc functions.\u00a0 In our example, we\u2019ll rely on upidsc, which returns the maximum size, datatype, column name, length of the column name, maximum display size, precision of numeric items, scale of numerics, whether null values are allowed, and a couple other things.<\/p>\n<pre>            ret = upidsc(&amp;hda[0], cnum, pos,\r\n                         &amp;pmxl[pos], &amp;pvll[pos],\r\n                         &amp;perc[pos], &amp;podt[pos], (text *)&amp;coln[pos],\r\n                         &amp;coll[pos], &amp;pmxdisl[pos],\r\n                         &amp;ppre[pos], &amp;pscl[pos],\r\n                         &amp;pnul[pos]);<\/pre>\n<p><strong>Defining Output Variables<\/strong><br \/>\nThe next step is to define the output variables for each select-list item in the query.\u00a0 To define output variables, UPI provides the upidfc, upidfn, and upidfps functions.\u00a0 In the example, we\u2019ll use upidfn, which is a non-piecewise define for a select-list item by position.\u00a0 In our call, we\u2019re making sure to pass reference to our indicator value for this item as well.\u00a0 Failure to do so will result in errors when attempting to fetch a column containing a NULL value.<\/p>\n<pre>            if (upidfn(&amp;hda[0], cnum, (sword) i,\r\n                       obuf[i], coll[i],\r\n                       (sword) SQLT_STR, (b2 *) &amp;oind[i],\r\n                       (text *) 0, (size_t) 0,\r\n                       (ub2 *) 0, (ub2 *) 0,\r\n                       (sword) 0))<\/pre>\n<p><strong>Executing the Query<\/strong><br \/>\nNow that we\u2019ve defined our output variables, we\u2019re ready to execute the query.\u00a0 The order of describe\/define\/execution can be different in different situations.\u00a0 Regardless, UPI provides several calls for execution including, upiefn, upiexe, and upiexn.\u00a0 In our example, we\u2019ll use upiexe.<\/p>\n<pre>    if (upiexe(&amp;hda[0], cnum))<\/pre>\n<p><strong>Fetching Data<\/strong><br \/>\nSo, we\u2019ve defined our output variables and executed the statement.\u00a0 If all has gone well so-far, we can start fetching data.\u00a0 UPI provides several different calls including upifch and upifcn.\u00a0 We could also use upiefn (execute+fetch), but are instead going to use the simplest version, upifch.\u00a0 The upifch function performs a row-at-a-time fetch on our cursor.<\/p>\n<pre>        while (upifch(&amp;hda[0], cnum) == 0)<\/pre>\n<p><strong>Conclusion<\/strong><br \/>\nThis has been a brief example of writing a simple application using Oracle\u2019s UPI data access API.\u00a0 In the next two articles, you\u2019ll see how this same application can be improved and made more descriptive using OCI7 and the current version of OCI, OCI8.<\/p>\n<p><strong>Full Program Listing<\/strong><br \/>\nThe following is the entire source code to the extremely simple interactive SQL client built using the User Program Interface.\u00a0 Given that Oracle still considers UPI proprietary, I will not be attaching nor sending my UPI definitions header file, upidef.h.\u00a0 If you\u2019d like to actually run the program below, you can fairly easily recreate the UPI function prototypes based on the data types and argument ordering provided.<\/p>\n<pre>\/* ========================================================================= *\/\r\n\/* INTERACTIVE SQL EXAMPLE                                                   *\/\r\n\/* Oracle User Program Interface (UPI)                                       *\/\r\n\/*                                                                           *\/\r\n\/*                                  Jonah H. Harris &lt;jonah.harris@gmail.com&gt; *\/\r\n\/*                                          http:\/\/www.oracle-internals.com\/ *\/\r\n\/* ========================================================================= *\/\r\n\r\n\/* Standard Headers *\/\r\n#include &lt;stdio.h&gt;                                \/* Standard C Input\/Output *\/\r\n#include &lt;stdlib.h&gt;                                    \/* Standard C Library *\/\r\n\r\n\/* Oracle-specific Headers *\/\r\n#include &lt;oratypes.h&gt;                      \/* Oracle-declared Platform Types *\/\r\n#include \"upidef.h\"                                    \/* My UPI Definitions *\/\r\n\r\n\/* ========================================================================= *\/\r\n\/* &lt;&lt; DEFINITIONS &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; *\/\r\n\/* ========================================================================= *\/\r\n\r\n#define MAXATTR             1024  \/* Max number of attributes in select-list *\/\r\n\r\n\/* ========================================================================= *\/\r\n\/* &lt;&lt; STATIC VARIABLES &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; *\/\r\n\/* ========================================================================= *\/\r\n\r\nstatic ub1      hda[512] = { 0 };                  \/* Host Data Area for UPI *\/\r\nstatic uword    cnum;                                       \/* Cursor Number *\/\r\n\r\n\/* ========================================================================= *\/\r\n\/* &lt;&lt; FORWARD DECLARATIONS &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; *\/\r\n\/* ========================================================================= *\/\r\n\r\nstatic void upi_error (void);\r\n\r\n\/* ========================================================================= *\/\r\n\/* &lt;&lt; PUBLIC FUNCTIONS &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; *\/\r\n\/* ========================================================================= *\/\r\n\r\nint\r\nmain (int argc, char **argv)\r\n{\r\n    char    sql_statement[256];\r\n    sword   i, pos;\r\n    sword   ret;\r\n    ub1     podt[MAXATTR], ppre[MAXATTR], pnul[MAXATTR];\r\n    ub2     pmxl[MAXATTR], coll[MAXATTR],\r\n            pmxdisl[MAXATTR], pvll[MAXATTR];\r\n    sb1     pscl[MAXATTR];\r\n    sb2     perc[MAXATTR];\r\n    text    coln[MAXATTR][31];\r\n    text   *obuf[MAXATTR];\r\n    sb2     oind[MAXATTR];\r\n\r\n    \/* Logon *\/\r\n    if (upilog(&amp;hda[0],\r\n               (text *) \"scott\/tiger@orcl\",\r\n               -1, 0,\r\n               -1, 0,\r\n               -1, UPILFDEF, UPILMDEF))\r\n    {\r\n        printf(\"Cannot connect as scott. Exiting...\\n\");\r\n        exit(1);\r\n    }\r\n\r\n    \/* Open a cursor *\/\r\n    if (upiopn(&amp;hda[0], &amp;cnum, -1))\r\n    {\r\n        printf(\"couldn't open cursor!\\n\");\r\n        upi_error();\r\n        exit(1);\r\n    }\r\n\r\n    \/* Main loop *\/\r\n    for (;;)\r\n    {\r\n        ub4 rnum = 0;   \/* A generic record number counter *\/\r\n\r\n        printf(\"\\nEnter a query or \\\"exit\\\"&gt; \");\r\n        fgets(sql_statement, sizeof(sql_statement), stdin);\r\n\r\n        \/* If user typed, \"exit\", bail! *\/\r\n        if (strncmp(sql_statement, \"exit\", 4) == 0)\r\n            break;\r\n\r\n        \/* Parse the statement *\/\r\n        if (upiosd(&amp;hda[0], cnum, (text *) sql_statement, -1,\r\n                   (ub4) 1, (sword) 1))\r\n        {\r\n            upi_error();\r\n            continue;\r\n        }\r\n\r\n        \/* Iterate over each attribute in the select-list *\/\r\n        for (pos = 1; pos &lt; MAXATTR; pos++)\r\n        {\r\n            coll[pos] = sizeof(coln[pos]);\r\n\r\n            \/* Describe the select-list item *\/\r\n            ret = upidsc(&amp;hda[0], cnum, pos,\r\n                         &amp;pmxl[pos], &amp;pvll[pos],\r\n                         &amp;perc[pos], &amp;podt[pos], (text *)&amp;coln[pos],\r\n                         &amp;coll[pos], &amp;pmxdisl[pos],\r\n                         &amp;ppre[pos], &amp;pscl[pos],\r\n                         &amp;pnul[pos]);\r\n\r\n            if (coll[pos] &gt; sizeof(coln[pos]))\r\n                coll[pos] = sizeof(coln[pos]);\r\n\r\n            \/* Make sure this string is null-terminated *\/\r\n            coln[pos][coll[pos]] = '\\0';\r\n\r\n            if (ret)\r\n            {\r\n                if (ret == 1007)\r\n                    break;\r\n                upi_error();\r\n                continue;\r\n            }\r\n        }\r\n\r\n        \/*\r\n         * Print out the total count and the names of the select-list\r\n         * items, column sizes, and datatype codes.\r\n         *\/\r\n        pos--;\r\n        printf(\"\\nThere were %d select-list items.\\n\", pos);\r\n        printf(\"Item name                     Length   Datatype\\n\");\r\n        printf(\"\\n\");\r\n        for (i = 1; i &lt;= pos; i++)\r\n        {\r\n            printf(\"%*.*s\", coll[i], coll[i], coln[i]);\r\n            printf(\"%*c\", 31 - coll[i], ' ');\r\n            printf(\"%6d   %8d\\n\", coll[i], podt[i]);\r\n\r\n            \/* Allocate the output *\/\r\n            obuf[i] = (text *) malloc(coll[i]+1);\r\n            if (obuf[i] == NULL)\r\n            {\r\n                fprintf(stderr, \"Failed to allocate %u bytes!\\n\",\r\n                    coll[i]+1);\r\n                exit(EXIT_FAILURE);\r\n            }\r\n\r\n            \/* Define the output parameter *\/\r\n            if (upidfn(&amp;hda[0], cnum, (sword) i,\r\n                       obuf[i], coll[i],\r\n                       (sword) SQLT_STR, (b2 *) &amp;oind[i],\r\n                       (text *) 0, (size_t) 0,\r\n                       (ub2 *) 0, (ub2 *) 0,\r\n                       (sword) 0))\r\n            {\r\n                upi_error();\r\n                break;\r\n            }\r\n        }\r\n\r\n        \/* Execute *\/\r\n        if (upiexe(&amp;hda[0], cnum))\r\n        {\r\n            upi_error();\r\n            continue;\r\n        }\r\n\r\n        \/* Fetch &amp; Display *\/\r\n        while (upifch(&amp;hda[0], cnum) == 0)\r\n        {\r\n            printf(\"\\n-- RECORD %04u -------------------------\\n\", ++rnum);\r\n            for (i = 1; i &lt;= pos; i++)\r\n            {\r\n                printf(\"%s\", coln[i]);\r\n                printf(\"%*c \", 31 - coll[i], ':');\r\n                printf(\"%s\\n\", (oind[i]) ? \"NULL\" : obuf[i]);\r\n            }\r\n        }\r\n\r\n        \/* Display the summary *\/\r\n        printf(\"\\n%u record%s returned.\\n\", rnum, (rnum == 1) ? \"\" : \"s\");\r\n\r\n        \/* Cleanup *\/\r\n        for (i = 1; i &lt;= pos; i++)\r\n            if (obuf[i] != NULL)\r\n                free(obuf[i]);\r\n\r\n    } \/* for (;;) *\/\r\n\r\n    \/* Close the cursor *\/\r\n    upicls(&amp;hda[0], cnum);\r\n\r\n    \/* Logoff *\/\r\n    upilof(&amp;hda[0]);\r\n\r\n    \/* Bail! *\/\r\n    exit(EXIT_SUCCESS);\r\n\r\n} \/* main() *\/\r\n\r\n\/* ========================================================================= *\/\r\n\/* &lt;&lt; PRIVATE FUNCTIONS &gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; *\/\r\n\/* ========================================================================= *\/\r\n\r\nstatic void\r\nupi_error (void)\r\n{\r\n    text msg[512] = { 0 };\r\n\r\n    fprintf(stderr, \"\\nOracle ERROR\\n\");\r\n    upigem(&amp;hda[0], (text *) &amp;msg);\r\n    fprintf(stderr, \"%s\", msg);\r\n    fflush(stderr);\r\n\r\n} \/* upi_error() *\/<\/pre>\n<p>UPI is fun! :)<\/p>\n","protected":false},"excerpt":{"rendered":"<p>First, I\u2019d like to apologize to our good friend SQLLIB.\u00a0 Those of you who have been working with the Oracle Database for some time will notice that, while it too is a common data access library, I\u2019ve omitted it from this series of posts. No, it\u2019s not because of some personal vendetta against SQLLIB.\u00a0 In [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v22.8 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Data Access APIs\u2013Part 1: Fun with UPI - Oracle Internals<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Data Access APIs\u2013Part 1: Fun with UPI - Oracle Internals\" \/>\n<meta property=\"og:description\" content=\"First, I\u2019d like to apologize to our good friend SQLLIB.\u00a0 Those of you who have been working with the Oracle Database for some time will notice that, while it too is a common data access library, I\u2019ve omitted it from this series of posts. No, it\u2019s not because of some personal vendetta against SQLLIB.\u00a0 In [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/\" \/>\n<meta property=\"og:site_name\" content=\"Oracle Internals\" \/>\n<meta property=\"article:published_time\" content=\"2010-02-04T22:05:06+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2014-01-26T22:11:42+00:00\" \/>\n<meta name=\"author\" content=\"Jonah Harris\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@jonahharris\" \/>\n<meta name=\"twitter:site\" content=\"@jonahharris\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Jonah Harris\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/#article\",\"isPartOf\":{\"@id\":\"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/\"},\"author\":{\"name\":\"Jonah Harris\",\"@id\":\"http:\/\/oracle-internals.com\/blog\/#\/schema\/person\/549d9c522c3960b062618b600bb762a4\"},\"headline\":\"Data Access APIs\u2013Part 1: Fun with UPI\",\"datePublished\":\"2010-02-04T22:05:06+00:00\",\"dateModified\":\"2014-01-26T22:11:42+00:00\",\"mainEntityOfPage\":{\"@id\":\"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/\"},\"wordCount\":1761,\"commentCount\":0,\"publisher\":{\"@id\":\"http:\/\/oracle-internals.com\/blog\/#\/schema\/person\/549d9c522c3960b062618b600bb762a4\"},\"inLanguage\":\"en-US\"},{\"@type\":\"WebPage\",\"@id\":\"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/\",\"url\":\"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/\",\"name\":\"Data Access APIs\u2013Part 1: Fun with UPI - Oracle Internals\",\"isPartOf\":{\"@id\":\"http:\/\/oracle-internals.com\/blog\/#website\"},\"datePublished\":\"2010-02-04T22:05:06+00:00\",\"dateModified\":\"2014-01-26T22:11:42+00:00\",\"breadcrumb\":{\"@id\":\"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"http:\/\/oracle-internals.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Data Access APIs\u2013Part 1: Fun with UPI\"}]},{\"@type\":\"WebSite\",\"@id\":\"http:\/\/oracle-internals.com\/blog\/#website\",\"url\":\"http:\/\/oracle-internals.com\/blog\/\",\"name\":\"Oracle Internals\",\"description\":\"Researching the Inner Workings of the World&#039;s Most Powerful Database\",\"publisher\":{\"@id\":\"http:\/\/oracle-internals.com\/blog\/#\/schema\/person\/549d9c522c3960b062618b600bb762a4\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"http:\/\/oracle-internals.com\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"http:\/\/oracle-internals.com\/blog\/#\/schema\/person\/549d9c522c3960b062618b600bb762a4\",\"name\":\"Jonah Harris\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"http:\/\/oracle-internals.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"http:\/\/1.gravatar.com\/avatar\/a6d16ed0f510e8de0929f129471dc1e5?s=96&d=mm&r=g\",\"contentUrl\":\"http:\/\/1.gravatar.com\/avatar\/a6d16ed0f510e8de0929f129471dc1e5?s=96&d=mm&r=g\",\"caption\":\"Jonah Harris\"},\"logo\":{\"@id\":\"http:\/\/oracle-internals.com\/blog\/#\/schema\/person\/image\/\"},\"sameAs\":[\"https:\/\/www.linkedin.com\/in\/jonahharris\/\",\"https:\/\/x.com\/jonahharris\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Data Access APIs\u2013Part 1: Fun with UPI - Oracle Internals","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/","og_locale":"en_US","og_type":"article","og_title":"Data Access APIs\u2013Part 1: Fun with UPI - Oracle Internals","og_description":"First, I\u2019d like to apologize to our good friend SQLLIB.\u00a0 Those of you who have been working with the Oracle Database for some time will notice that, while it too is a common data access library, I\u2019ve omitted it from this series of posts. No, it\u2019s not because of some personal vendetta against SQLLIB.\u00a0 In [&hellip;]","og_url":"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/","og_site_name":"Oracle Internals","article_published_time":"2010-02-04T22:05:06+00:00","article_modified_time":"2014-01-26T22:11:42+00:00","author":"Jonah Harris","twitter_card":"summary_large_image","twitter_creator":"@jonahharris","twitter_site":"@jonahharris","twitter_misc":{"Written by":"Jonah Harris","Est. reading time":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/#article","isPartOf":{"@id":"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/"},"author":{"name":"Jonah Harris","@id":"http:\/\/oracle-internals.com\/blog\/#\/schema\/person\/549d9c522c3960b062618b600bb762a4"},"headline":"Data Access APIs\u2013Part 1: Fun with UPI","datePublished":"2010-02-04T22:05:06+00:00","dateModified":"2014-01-26T22:11:42+00:00","mainEntityOfPage":{"@id":"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/"},"wordCount":1761,"commentCount":0,"publisher":{"@id":"http:\/\/oracle-internals.com\/blog\/#\/schema\/person\/549d9c522c3960b062618b600bb762a4"},"inLanguage":"en-US"},{"@type":"WebPage","@id":"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/","url":"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/","name":"Data Access APIs\u2013Part 1: Fun with UPI - Oracle Internals","isPartOf":{"@id":"http:\/\/oracle-internals.com\/blog\/#website"},"datePublished":"2010-02-04T22:05:06+00:00","dateModified":"2014-01-26T22:11:42+00:00","breadcrumb":{"@id":"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/"]}]},{"@type":"BreadcrumbList","@id":"http:\/\/oracle-internals.com\/blog\/2010\/02\/04\/data-access-and-fun-with-upi\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http:\/\/oracle-internals.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Data Access APIs\u2013Part 1: Fun with UPI"}]},{"@type":"WebSite","@id":"http:\/\/oracle-internals.com\/blog\/#website","url":"http:\/\/oracle-internals.com\/blog\/","name":"Oracle Internals","description":"Researching the Inner Workings of the World&#039;s Most Powerful Database","publisher":{"@id":"http:\/\/oracle-internals.com\/blog\/#\/schema\/person\/549d9c522c3960b062618b600bb762a4"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"http:\/\/oracle-internals.com\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"http:\/\/oracle-internals.com\/blog\/#\/schema\/person\/549d9c522c3960b062618b600bb762a4","name":"Jonah Harris","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"http:\/\/oracle-internals.com\/blog\/#\/schema\/person\/image\/","url":"http:\/\/1.gravatar.com\/avatar\/a6d16ed0f510e8de0929f129471dc1e5?s=96&d=mm&r=g","contentUrl":"http:\/\/1.gravatar.com\/avatar\/a6d16ed0f510e8de0929f129471dc1e5?s=96&d=mm&r=g","caption":"Jonah Harris"},"logo":{"@id":"http:\/\/oracle-internals.com\/blog\/#\/schema\/person\/image\/"},"sameAs":["https:\/\/www.linkedin.com\/in\/jonahharris\/","https:\/\/x.com\/jonahharris"]}]}},"_links":{"self":[{"href":"http:\/\/oracle-internals.com\/blog\/wp-json\/wp\/v2\/posts\/16"}],"collection":[{"href":"http:\/\/oracle-internals.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/oracle-internals.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/oracle-internals.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/oracle-internals.com\/blog\/wp-json\/wp\/v2\/comments?post=16"}],"version-history":[{"count":3,"href":"http:\/\/oracle-internals.com\/blog\/wp-json\/wp\/v2\/posts\/16\/revisions"}],"predecessor-version":[{"id":26,"href":"http:\/\/oracle-internals.com\/blog\/wp-json\/wp\/v2\/posts\/16\/revisions\/26"}],"wp:attachment":[{"href":"http:\/\/oracle-internals.com\/blog\/wp-json\/wp\/v2\/media?parent=16"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/oracle-internals.com\/blog\/wp-json\/wp\/v2\/categories?post=16"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/oracle-internals.com\/blog\/wp-json\/wp\/v2\/tags?post=16"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}