Difference between revisions of "OpenDBX/C API/Usage"

From Linuxnetworks
< OpenDBX‎ | C API
Jump to: navigation, search
(Starting queries)
(Processing results: odbx_field_value can return NULL, not odbx_field_length)
 
(32 intermediate revisions by 3 users not shown)
Line 5: Line 5:
  
 
Using the interface is fairly easy and pretty straight forward:
 
Using the interface is fairly easy and pretty straight forward:
Connect to your database, send your query, retrieve the result set and process the row values. After you've done that you can continue to send queries. If you've finished your job, you have to disconnect from the database and clean up all resources.
+
Connect to your database, send your query, retrieve the result set, process the row values and clean up the fetched result set. After you've done that you can continue to send queries. If you've finished your job, you have to disconnect from the database and free all resources.
  
Sometimes there may occur an error somewhere, which you will be informed by a return value of the called function smaller than zero. The meanings of the error codes are listed in odbx.h and you get a textual explanation of it if you feed odbx_error() with the return value. A return value of -1 is handled a little bit special internally because then the native database library is asked directly what's wrong
+
Sometimes there may occur an error somewhere, which you will be informed by a return value of the called function smaller than zero. The meanings of the error codes are listed in odbx.h and you get a textual explanation of it if you feed [[OpenDBX/API/odbx_error|odbx_error]]() with the return value. A return value of -1 is handled a little bit special internally because then the native database library is asked directly what's wrong.
 +
 
 +
A simple test program might be:
 +
 
 +
#include <odbx.h>
 +
#include <stdio.h>
 +
 +
int main( int argc, char* argv[] )
 +
{
 +
    odbx_t* handle;
 +
 +
    if( odbx_init( &handle, "mysql", "127.0.0.1", "" ) < 0 )
 +
    {
 +
        printf( "Error in odbx_init\n" );
 +
        return 1;
 +
    }
 +
 +
    printf( "Initialized connection\n" );
 +
 +
    if( odbx_finish( handle ) < 0 )
 +
    {
 +
        printf( "Error in odbx_finish\n" );
 +
        return 1;
 +
    }
 +
 +
    return 0;
 +
}
 +
 
 +
As soon as you've implemented something you want to test, you want compile and run your program to see if it is successful. Compilation and linking of your program are done with
 +
 
 +
gcc -o myprog myprog.c -lopendbx
 +
 
 +
This call the GNU compiler and translates the C code in "myprog.c" into an binary object called "myprog.o". In the second step it creates an executable named "myprog" and links it against the OpenDBX library ("-lopendbx"). Now you can test and see what happens.
  
 
== Dealing with connections ==
 
== Dealing with connections ==
  
You need to setup a connection to your database before you can send queries to the DataBase Management System. Before the connection can be established, the chosen backend must be loaded and all necessary data structures initialized. You don't have to worry about specific details because odbx_init() handles everything for you:
+
You need connect to your database before you can send queries to the DataBase Management System (DBMS). Before the connection can be established, the chosen backend must be loaded and all necessary data structures initialized. You don't have to worry about specific details because odbx_init() handles everything for you:
  
 
  int err;
 
  int err;
Line 22: Line 54:
 
  }
 
  }
  
SQLite is a little bit different because there is no dedicated server you can connect to. Instead, the database is a file the SQLite library is operating on. Therefore you have to specify the path to the directory (without the file name but with trailing slash or backslash) as third parameter instead. The last parameter doesn't matter in this case. Most other backends will use their default port if the fourth parameter of odbx_init() is empty.
+
The values for the ''host'' and ''port'' arguments of [[OpenDBX/API/odbx_init|odbx_init]]() respectively the ''database'', ''who'' and ''cred'' arguments of [[OpenDBX/API/odbx_bind|odbx_bind]]() may be dependent on the database backend you are using. Please refer to the [[OpenDBX/Configuration|OpenDBX backend configuration]] page and the documentation of the database backend to get further information.
  
Opening the connection to the database is done by calling odbx_bind_simple() with the initialized handle, the database name and the credentials. The backend will either open an ip connection to the DBMS and authenticates itself by using the user name and password or the SQLite backend opens a file directly. The SQLite library also doesn't use the credentials because it relies on the access control provided by the file system.
+
Connecting to the database is done by calling [[OpenDBX/API/odbx_bind|odbx_bind]]() with the initialized handle, the database name and the credentials. The backend will open an connection to the DBMS and authenticates itself by using the user name and password.
  
  if( ( err = odbx_bind_simple( handle, "testdb", "testuser", "testpwd" ) ) < 0 )
+
  if( ( err = odbx_bind( handle, "testdb", "testuser", "testpwd", ODBX_BIND_SIMPLE ) ) < 0 )
 
  {
 
  {
     fprintf( stderr, "odbx_bind_simple(): %s\n", odbx_error( handle, err ) );
+
     fprintf( stderr, "odbx_bind(): %s\n", odbx_error( handle, err ) );
 
     odbx_finish( handle );
 
     odbx_finish( handle );
 
     return err;
 
     return err;
 
  }
 
  }
  
Sometimes it may be necessary to switch to another database or reauthenticate using different credentials. You can do this by calling odbx_bind_simple() again, but you have to close the connection with odbx_unbind() before.
+
Sometimes it may be necessary to connect to a different database or re-authenticate using different credentials. You can do this by calling [[OpenDBX/API/odbx_bind|odbx_bind]]() again, but you have to close the connection with [[OpenDBX/API/odbx_unbind|odbx_unbind]]() before.
  
 
  if( ( err = odbx_unbind( handle ) ) < 0 )
 
  if( ( err = odbx_unbind( handle ) ) < 0 )
Line 42: Line 74:
 
  }
 
  }
  
I also strongly recommend to unbind before you terminate your program. Finally call odbx_finish() to release all resources and disconnect from the database.
+
I also strongly recommend to unbind before you terminate your program. Finally call [[OpenDBX/API/odbx_finish|odbx_finish]]() to release all resources and disconnect from the database.
  
  odbx_finish( handle );
+
  if( ( err = odbx_finish( handle ) ) < 0 )
 +
{
 +
    fprintf( stderr, "odbx_finish(): %s\n", odbx_error( handle, err ) );
 +
    return err;
 +
}
  
 
== Handling options and capabilities ==
 
== Handling options and capabilities ==
  
If you want to check for library info or use non-standard options like encrypted data transfer or multi-statement support (provided they are supported), this can be changed after calling odbx_init() and before odbx_bind_simple() by using odbx_{get,set}_options(). The function odbx_get_option() returns info about the library and its implemented functionality while odbx_set_option() can change the default behavior of the library.
+
If you want to check for library info or use non-standard options like encrypted data transfer or multi-statement support (provided they are supported), this can be changed after calling [[OpenDBX/API/odbx_init|odbx_init]]() and before [[OpenDBX/API/odbx_bind|odbx_bind]]() by using odbx_{get,set}_option(). The function [[OpenDBX/API/odbx_get_option|odbx_get_option]]() returns info about the library and its implemented functionality while [[OpenDBX/API/odbx_set_option|odbx_set_option]]() can change the default behavior of the library.
  
 
  int safe;
 
  int safe;
Line 68: Line 104:
 
  }
 
  }
  
Capabilities are implemented sets of functions serving a specific purpose, e.g. handling prepared statements. By calling odbx_capabilities() you can find out what sets of functionality are supported by the backend and you can do this any time after successfully invoking odbx_init(). Each set - the basic set is only the default one which is always available - is fully implemented by the backend, so you don't have to worry that the one or another function might be missing. Currently only ODBX_CAP_BASIC is defined.
+
Capabilities are implemented sets of functions serving a specific purpose, e.g. handling large objects. By calling [[OpenDBX/API/odbx_capabilities|odbx_capabilities]]() you can find out what sets of functionality are supported by the backend and you can do this any time after successfully invoking [[OpenDBX/API/odbx_init|odbx_init]](). Each set - the basic set is only the default one which is always available - is fully implemented by the backend, so you don't have to worry that the one or another function might be missing.
  
  if( ( err = odbx_capabilities( handle, ODBX_CAP_BASIC ) ) < 0 )
+
  if( ( err = odbx_capabilities( handle, ODBX_CAP_LO ) ) < 0 )
 
  {
 
  {
 
     fprintf( stderr, "odbx_capabilites(): %s\n", odbx_error( handle, err ) );
 
     fprintf( stderr, "odbx_capabilites(): %s\n", odbx_error( handle, err ) );
Line 77: Line 113:
 
  }
 
  }
  
== Starting queries ==
+
== Executing statements ==
  
 
Before you are sending queries to your database, you should be aware of the security issues you will be facing. If you will use any kind of user input in your statements, you will potentially be vulnerable to the injection of code and this can lead to disastrous effects. Imagine for example the following simple statement:
 
Before you are sending queries to your database, you should be aware of the security issues you will be facing. If you will use any kind of user input in your statements, you will potentially be vulnerable to the injection of code and this can lead to disastrous effects. Imagine for example the following simple statement:
Line 87: Line 123:
 
  me'; DELETE FROM mytable WHERE uid LIKE '%
 
  me'; DELETE FROM mytable WHERE uid LIKE '%
  
This results in a valid query with two statements and suddenly all your table entries are gone. To avoid SQL insertion, you have to escape every (!) user input you like to use in a query, insert statement or anything else. It's a good idea to escape also strings returned from the database because they could be injected SQL statements too. Fortunately odbx_escape() handles all escaping for you. It prevents any kind of SQL insertion and sometimes replaces special characters in the input string. The result is written to a buffer provided by your program and the parameter containing the length of the buffer will be overwritten by the real length of the result. Unfortunately most backends require that your result buffer is more than twice as big as your input, so you should first think about how long the user input might be.
+
This results in a valid query with two statements and suddenly all your table entries are gone. To avoid SQL injection, you have to escape every (!) user input you like to use in a query, insert statement or anything else. It's a good idea to escape also strings returned from the database because they could be injected SQL statements too. Fortunately [[OpenDBX/API/odbx_escape|odbx_escape]]() handles all escaping for you. It prevents any kind of SQL insertion and sometimes replaces special characters in the input string. The result is written to a buffer provided by your program and the parameter containing the length of the buffer will be overwritten by the real length of the result. Unfortunately most backends require that your result buffer is more than twice as big as your input (precisely, 2 * input size + 1 ), so you should first think about how long the user input might be.
  
 
  char to[100];
 
  char to[100];
Line 101: Line 137:
  
 
  int len;
 
  int len;
  char query[256] = "SELECT * FROM mytable WHERE uid = '%s'";
+
  char query[256];
 +
char qstr[] = "SELECT * FROM mytable WHERE uid = '%s'";
 
   
 
   
  if( ( len = snprintf( query, 256, to ) ) > 0 && len < 256 )
+
  if( ( len = snprintf( query, 256, qstr, to ) ) > 0 && len < 256 )
 
  {
 
  {
 
     if( ( err = odbx_query( handle, query, len ) ) < 0 )
 
     if( ( err = odbx_query( handle, query, len ) ) < 0 )
Line 114: Line 151:
 
== Retrieving results ==
 
== Retrieving results ==
  
Once you've send a query to the DBMS, the result sets can be retrieved by odbx_result(). In most backends you are able to specify a timeout and how the result should be retrieved: either one by one, all at once or in chunks. If chunks are not supported, they are returned one by one. The result handle - which you must use to get the rows and the values - is stored in the second parameter after odbx_result() returns. In case your query was not a SELECT-like statement and therefore returns no result, you can check for the number of affected rows instead. SELECT-like statement or not, in each case odbx_result() doesn't return an error, you have to delete the result structure by calling odbx_result_free().
+
Once you've send a query to the DBMS, the result sets can be retrieved by [[OpenDBX/API/odbx_result|odbx_result]](). In most backends you are able to specify a timeout and how the result should be retrieved: either one by one, all at once or in chunks. If chunks are not supported, they are returned one by one. The result handle - which you must use to get the rows and the values - is stored in the second parameter after [[OpenDBX/API/odbx_result|odbx_resul]]t() returns. In case your query was not a SELECT-like statement and therefore returns no result, you can check for the number of affected rows instead. SELECT-like statement or not, in each case [[OpenDBX/API/odbx_result|odbx_result]]() returns a result structure you have to delete afterwards by calling [[OpenDBX/API/odbx_result_finish|odbx_result_finish]]().
  
 
  odbx_result_t* result;
 
  odbx_result_t* result;
Line 136: Line 173:
 
         case 2:
 
         case 2:
 
         fprintf( stdout, "Affected rows: %d\n", odbx_rows_affected( result ) );
 
         fprintf( stdout, "Affected rows: %d\n", odbx_rows_affected( result ) );
         odbx_result_free( result );
+
         odbx_result_finish( result );
 
         continue;
 
         continue;
 
     }
 
     }
Line 142: Line 179:
 
     // Fetch rows and process values
 
     // Fetch rows and process values
 
   
 
   
     odbx_result_free();
+
     odbx_result_finish(result);
 +
    tv = { 3, 0 };
 
  }
 
  }
  
When you've successfully retrieved a result from the DBMS, you have to use odbx_row_fetch() to get the rows subsequently until a return value of zero indicates that no more rows are available. It is essential to fetch all rows of the result, even if you've found the row you've been searching for. Otherwise you will get an error while sending the next query or the outstanding rows are returned before the new result is available.
+
When you've successfully retrieved a result from the database, you have to use [[OpenDBX/API/odbx_row_fetch|odbx_row_fetch]]() to get the rows subsequently until it returns ODBX_ROW_DONE which indicates that no more rows are available. It is essential to fetch all rows of the result, even if you've found the row you've been searching for. Otherwise you will get an error while sending the next query or the outstanding rows are returned before the new result is available.
  
  while( ( err = odbx_row_fetch( result ) ) != 0 )
+
  while( ( err = odbx_row_fetch( result ) ) != ODBX_ROW_DONE )
 
  {
 
  {
 
     if( err < 0 )
 
     if( err < 0 )
Line 160: Line 198:
 
== Processing results ==
 
== Processing results ==
  
After fetching a row, all values of this row are available for further processing, as well as their name, length and type - but the name and the type of a column won't change. Also the number of columns returned by odbx_column_count() is fixed for the whole result.
+
After fetching a row, all values of this row are available for further processing, as well as their name, length and type - but the name and the type of a column won't change. Also the number of columns returned by [[OpenDBX/API/odbx_column_count|odbx_column_count]]() is fixed for the whole result.
  
 
  int i;
 
  int i;
Line 166: Line 204:
 
  for( i = 0; i < odbx_column_count( result ); i++ )
 
  for( i = 0; i < odbx_column_count( result ); i++ )
 
  {
 
  {
     fprintf( stdout, "Name: %s\n", odbx_column_name( result, i );
+
     fprintf( stdout, "Name: %s\n", odbx_column_name( result, i ) );
     fprintf( stdout, "Type: %d\n", odbx_column_type( result, i );
+
     fprintf( stdout, "Type: %d\n", odbx_column_type( result, i ) );
     fprintf( stdout, "Length: %d\n", odbx_field_length( result, i );
+
     fprintf( stdout, "Length: %d\n", odbx_field_length( result, i ) );
     fprintf( stdout, "Value: %s\n", odbx_field_value( result, i );
+
     fprintf( stdout, "Value: %s\n", odbx_field_value( result, i ) );
 
  }
 
  }
  
  
Besides odbx_column_type() these functions doesn't return error codes and odbx_column_name() only returns NULL if an error occured or the field contains a NULL value. Instead they return zero or NULL, but you shouldn't check for those because these values are also valid return values for odbx_column_count() and odbx_field_{length,value}().
+
Besides [[OpenDBX/API/odbx_column_type|odbx_column_type]]() these functions don't return error codes. Instead, they return zero ([[OpenDBX/API/odbx_field_length|odbx_field_length]]()) or NULL ([[OpenDBX/API/odbx_column_name|odbx_column_name]]() and [[OpenDBX/API/odbx_field_value|odbx_field_value]]()), but you shouldn't check for those because these values are also valid return values.
  
 
All numbers are returned as strings from the database regardless if they are integers or floats. If you want to do arithmetic operations, you have to convert them to their machine dependent binary representation first.
 
All numbers are returned as strings from the database regardless if they are integers or floats. If you want to do arithmetic operations, you have to convert them to their machine dependent binary representation first.
  
 +
== Futher examples ==
  
----
+
A full-featured working example of how to use the OpenDBX C API correctly can be found in the test/odbx-regression.c file of the source distribution available at the [[OpenDBX/Download#Source|download section]].
Back to [[OpenDBX|Overview]]
+

Latest revision as of 17:18, 5 October 2012

Logo-opendbx.png


Using the interface is fairly easy and pretty straight forward: Connect to your database, send your query, retrieve the result set, process the row values and clean up the fetched result set. After you've done that you can continue to send queries. If you've finished your job, you have to disconnect from the database and free all resources.

Sometimes there may occur an error somewhere, which you will be informed by a return value of the called function smaller than zero. The meanings of the error codes are listed in odbx.h and you get a textual explanation of it if you feed odbx_error() with the return value. A return value of -1 is handled a little bit special internally because then the native database library is asked directly what's wrong.

A simple test program might be:

#include <odbx.h>
#include <stdio.h>

int main( int argc, char* argv[] )
{
    odbx_t* handle;

    if( odbx_init( &handle, "mysql", "127.0.0.1", "" ) < 0 )
    {
        printf( "Error in odbx_init\n" );
        return 1;
    }

    printf( "Initialized connection\n" );

    if( odbx_finish( handle ) < 0 )
    {
        printf( "Error in odbx_finish\n" );
        return 1;
    }

    return 0;
}

As soon as you've implemented something you want to test, you want compile and run your program to see if it is successful. Compilation and linking of your program are done with

gcc -o myprog myprog.c -lopendbx

This call the GNU compiler and translates the C code in "myprog.c" into an binary object called "myprog.o". In the second step it creates an executable named "myprog" and links it against the OpenDBX library ("-lopendbx"). Now you can test and see what happens.

Dealing with connections

You need connect to your database before you can send queries to the DataBase Management System (DBMS). Before the connection can be established, the chosen backend must be loaded and all necessary data structures initialized. You don't have to worry about specific details because odbx_init() handles everything for you:

int err;
odbx_t* handle;

if( ( err = odbx_init( &handle, "mysql", "127.0.0.1", "" ) ) < 0 )
{
    fprintf( stderr, "odbx_init(): %s\n", odbx_error( handle, err ) );
    return err;
}

The values for the host and port arguments of odbx_init() respectively the database, who and cred arguments of odbx_bind() may be dependent on the database backend you are using. Please refer to the OpenDBX backend configuration page and the documentation of the database backend to get further information.

Connecting to the database is done by calling odbx_bind() with the initialized handle, the database name and the credentials. The backend will open an connection to the DBMS and authenticates itself by using the user name and password.

if( ( err = odbx_bind( handle, "testdb", "testuser", "testpwd", ODBX_BIND_SIMPLE ) ) < 0 )
{
    fprintf( stderr, "odbx_bind(): %s\n", odbx_error( handle, err ) );
    odbx_finish( handle );
    return err;
}

Sometimes it may be necessary to connect to a different database or re-authenticate using different credentials. You can do this by calling odbx_bind() again, but you have to close the connection with odbx_unbind() before.

if( ( err = odbx_unbind( handle ) ) < 0 )
{
    fprintf( stderr, "odbx_unbind(): %s\n", odbx_error( handle, err ) );
    odbx_finish( handle );
    return err;
}

I also strongly recommend to unbind before you terminate your program. Finally call odbx_finish() to release all resources and disconnect from the database.

if( ( err = odbx_finish( handle ) ) < 0 )
{
    fprintf( stderr, "odbx_finish(): %s\n", odbx_error( handle, err ) );
    return err;
}

Handling options and capabilities

If you want to check for library info or use non-standard options like encrypted data transfer or multi-statement support (provided they are supported), this can be changed after calling odbx_init() and before odbx_bind() by using odbx_{get,set}_option(). The function odbx_get_option() returns info about the library and its implemented functionality while odbx_set_option() can change the default behavior of the library.

int safe;
int option;

if( ( err = odbx_get_option( handle, ODBX_OPT_THREAD_SAFE, (void*) &safe ) ) < 0 )
{
    fprintf( stderr, "odbx_get_option(): %s\n", odbx_error( handle, err ) );
    odbx_finish( handle );
    return err;
}

option = ODBX_TLS_ALWAYS;
if( ( err = odbx_set_option( handle, ODBX_OPT_TLS, (void*) &option ) ) < 0 )
{
    fprintf( stderr, "odbx_set_option(): %s\n", odbx_error( handle, err ) );
    odbx_finish( handle );
    return err;
}

Capabilities are implemented sets of functions serving a specific purpose, e.g. handling large objects. By calling odbx_capabilities() you can find out what sets of functionality are supported by the backend and you can do this any time after successfully invoking odbx_init(). Each set - the basic set is only the default one which is always available - is fully implemented by the backend, so you don't have to worry that the one or another function might be missing.

if( ( err = odbx_capabilities( handle, ODBX_CAP_LO ) ) < 0 )
{
    fprintf( stderr, "odbx_capabilites(): %s\n", odbx_error( handle, err ) );
    odbx_finish( handle );
    return err;
}

Executing statements

Before you are sending queries to your database, you should be aware of the security issues you will be facing. If you will use any kind of user input in your statements, you will potentially be vulnerable to the injection of code and this can lead to disastrous effects. Imagine for example the following simple statement:

SELECT * FROM mytable WHERE uid = '%s';

Now your programme replaces "%s" by my input, which is

me'; DELETE FROM mytable WHERE uid LIKE '%

This results in a valid query with two statements and suddenly all your table entries are gone. To avoid SQL injection, you have to escape every (!) user input you like to use in a query, insert statement or anything else. It's a good idea to escape also strings returned from the database because they could be injected SQL statements too. Fortunately odbx_escape() handles all escaping for you. It prevents any kind of SQL insertion and sometimes replaces special characters in the input string. The result is written to a buffer provided by your program and the parameter containing the length of the buffer will be overwritten by the real length of the result. Unfortunately most backends require that your result buffer is more than twice as big as your input (precisely, 2 * input size + 1 ), so you should first think about how long the user input might be.

char to[100];
int tolen = 100;

if( ( err = odbx_escape( handle, input, strlen( input ), to, &tolen ) ) < 0 )
{
    fprintf( stderr, "odbx_escape(): %s\n", odbx_error( handle, err ) );
    return err;
}

The queries sent to the server must be understood by the DBMS because there is no translation done between the different dialects of SQL for example. But this is desired! It adds no further level of complexity to the library and your program which slows down your real work. Usually only a few statements with varying values are used in programmes and the easiest way make them adaptable to different databases by the users is to get them from a config file.

int len;
char query[256];
char qstr[] = "SELECT * FROM mytable WHERE uid = '%s'";

if( ( len = snprintf( query, 256, qstr, to ) ) > 0 && len < 256 )
{
    if( ( err = odbx_query( handle, query, len ) ) < 0 )
    {
        fprintf( stderr, "odbx_query(): %s\n", odbx_error( handle, err ) );
        return err;
    }
}

Retrieving results

Once you've send a query to the DBMS, the result sets can be retrieved by odbx_result(). In most backends you are able to specify a timeout and how the result should be retrieved: either one by one, all at once or in chunks. If chunks are not supported, they are returned one by one. The result handle - which you must use to get the rows and the values - is stored in the second parameter after odbx_result() returns. In case your query was not a SELECT-like statement and therefore returns no result, you can check for the number of affected rows instead. SELECT-like statement or not, in each case odbx_result() returns a result structure you have to delete afterwards by calling odbx_result_finish().

odbx_result_t* result;
struct timeval tv = { 3, 0 };

while( ( err = odbx_result( handle, &result, &tv, 0 ) ) != 0 )
{
    if( err < 0 )
    {
        fprintf( stderr, "odbx_result(): %s\n", odbx_error( handle, err ) );
        return err;
    }

    switch( err )
    {
        case 1:
        // NOTE: Retrieving the result is not (!) canceled
        fprintf( stdout, "odbx_result(): Timeout\n" );
        continue;

        case 2:
        fprintf( stdout, "Affected rows: %d\n", odbx_rows_affected( result ) );
        odbx_result_finish( result );
        continue;
    }

    // Fetch rows and process values

    odbx_result_finish(result);
    tv = { 3, 0 };
}

When you've successfully retrieved a result from the database, you have to use odbx_row_fetch() to get the rows subsequently until it returns ODBX_ROW_DONE which indicates that no more rows are available. It is essential to fetch all rows of the result, even if you've found the row you've been searching for. Otherwise you will get an error while sending the next query or the outstanding rows are returned before the new result is available.

while( ( err = odbx_row_fetch( result ) ) != ODBX_ROW_DONE )
{
    if( err < 0 )
    {
        fprintf( stderr, "odbx_row_fetch(): %s\n", odbx_error( handle, err ) );
        break;
    }

    // Get the values
}

Processing results

After fetching a row, all values of this row are available for further processing, as well as their name, length and type - but the name and the type of a column won't change. Also the number of columns returned by odbx_column_count() is fixed for the whole result.

int i;

for( i = 0; i < odbx_column_count( result ); i++ )
{
    fprintf( stdout, "Name: %s\n", odbx_column_name( result, i ) );
    fprintf( stdout, "Type: %d\n", odbx_column_type( result, i ) );
    fprintf( stdout, "Length: %d\n", odbx_field_length( result, i ) );
    fprintf( stdout, "Value: %s\n", odbx_field_value( result, i ) );
}


Besides odbx_column_type() these functions don't return error codes. Instead, they return zero (odbx_field_length()) or NULL (odbx_column_name() and odbx_field_value()), but you shouldn't check for those because these values are also valid return values.

All numbers are returned as strings from the database regardless if they are integers or floats. If you want to do arithmetic operations, you have to convert them to their machine dependent binary representation first.

Futher examples

A full-featured working example of how to use the OpenDBX C API correctly can be found in the test/odbx-regression.c file of the source distribution available at the download section.