Example C-API Core Cursor Application
Schema Compilation
The schema compile process creates generated source files necessary for your application program to access the database defined by the schema.
Generated source files from RDM utilities should never be edited.
For this hello_world
example, we will use the schema compiler tool to generate the C-struct definitions that define the rows in each table. We will also use the optional --catalog
feature of rdm-compile
to create a version of the database catalog we can embed in our application. This embedded catalog will be used to create the database if it does not already exist.
Generating C program interface files:
rdm-compile --c-structs --catalog hello_world.sdl
The --c-structs
option creates the examples/hello_world_structs.h
file in the current directory. The --catalog
option creates the examples/hello_world_cat.h
and examples/hello_world_cat.c
files in the current directory.
The schema file must ALWAYS have an .sdl
extension. The output files from rdm-compile
will begin with the same base name as the .sdl
file.
Writing the Application
The complete source for this example HelloWorld
application can found in tutorials/HelloWorld/hello_worldTutorial_main.c
.
Include Files Needed
Application developed in C should always include the rdm.h
header file which contains the required prototypes and types for building an RDM database application. The application must also include the generated C-structs header file which contains the table and column definitions. Optionally, the catalog header file will need to be included.
#include "rdm.h" /* The RDM API. */ #include "hello_world_structs.h" /* The hello_world database definitions. */ #include "hello_world_cat.h" /* The hello_world database catalog */
Allocating Handles
The Database handle should NOT be shared across multiple threads within an application process. If multiple threads need access to the same database, it is advised that a separate RDM_DB
handle for each thread be allocated.
/* Allocate a TFS Handle */ rc = rdm_rdmAllocTFS("", &tfs); if (rc == sOKAY) { rc = rdm_tfsDropDatabase(tfs, "hello_world"); if (rc == eNODB) { /* It is okay if there wasn't a database to drop */ rc = sOKAY; } if (rc == sOKAY) { /* Allocate a database handle */ rc = rdm_tfsAllocDatabase(tfs, &db);
The empty string in the above snippet call to rdm_rdmAllocTFS()
is where additional configuration options for the TFS can be set. See TFS Configuration Options. For example, setting the docroot to a specific directory on the disk will be done here. For example:
rc = rdm_rdmAllocTFS("docroot=C:\\DataDir", &tfs);
The above example places the docroot in the C:\DataDir
directory. This directory MUST exist prior before the TFS engine starts.
The database handle is associated with the TFS handle. Once the handle is allocated, additional database attributes can be set prior to opening the database. Below, after the rdm_tfsAllocDatabase()
function successfully returns, the HelloWorld
program associates the catalog with the database handle. This step is optional if the database already exists.
Some additional options that can be set using rdm_dbSetOptions()
after the database handle is allocated can be found in the Database Configuration Options section.
Opening the Database for Access
With the handles allocated and options set, we are ready to open the database. For this example, we will be opening the database in the "shared" mode which would allow others to open the same database simultaneously (assuming they are all using the same TFS instance).
/* Open the database */ rc = rdm_dbOpen(db, "hello_world", RDM_OPEN_SHARED); if (rc == sOKAY) {
The rdm_dbOpen()
function will establish a connection with the TFS and open the hello_world
database.
Writing to the Database
Writing to the database is always performed within a transaction. In the example code snippet below, notice the call to start a write transaction, rdm_dbStartUpdate()
, which requests a write lock on the INFO
table.
At line 76, the C-struct, which defines the row layout, is populated with the data we want to add to the database.
After the successful insert of the INFO
row buffer into the database, the call to rdm_dbEnd()
commits the changes to the database and releases the locks.
/* Insert "helloworld" row to database */ static RDM_RETCODE writeARow( RDM_DB db) { RDM_TABLE_ID tables[] = { TABLE_INFO }; RDM_RETCODE rc; /* Start an update transaction and lock the table */ rc = rdm_dbStartUpdate(db, tables, RDM_LEN(tables), NULL, 0, NULL); if (rc == sOKAY) { INFO infoInserted; /* Row buffer */ /* Populate the mychar column in the Row buffer */ strcpy(infoInserted.mychar, "Hello World! - using the embedded TFS"); /* Insert a row into the table */ rc = rdm_dbInsertRow(db, TABLE_INFO, &infoInserted, sizeof(infoInserted), NULL); if (rc == sOKAY) { /* Commit a transaction */ rc = rdm_dbEnd(db); } else { /* Abort the transaction */ rdm_dbEndRollback(db); } } return rc; }
Reading from the Database
Reading from the database is, also, performed within a transaction. In the example code snippet below, notice the call to start a read transaction, rdm_dbStartRead()
, which requests a read lock on the INFO
table.
Since the schema for this example database did not define any alternate access methods, we will use the rdm_dbGetRows()
function to create a row scan cursor to access the rows directly (rather than through an index or set). The call to rdm_dbGetRows()
on line 34 associates the cursor with the INFO
table and positions the cursor to the BeforeFirst position.See Transactions and Locking Overview
/* Read "helloworld" row to database */ static RDM_RETCODE readARow( RDM_DB db) { RDM_TABLE_ID tables[] = { TABLE_INFO }; RDM_RETCODE rc; /* Start an update transaction and lock the table */ rc = rdm_dbStartRead(db, tables, RDM_LEN(tables), NULL); if (rc == sOKAY) { RDM_CURSOR cursor; rc = rdm_dbAllocCursor(db, &cursor); if (rc == sOKAY) { /* Fill a cursor with all the rows in a table */ rc = rdm_dbGetRows(db, TABLE_INFO, &cursor); if (rc == sOKAY) { /* Move to the first row in the info table */ rc = rdm_cursorMoveToFirst(cursor); if (rc == sOKAY) { INFO infoRead; /* Row buffer */ /* Read the full content of the current record */ rc = rdm_cursorReadRow(cursor, &infoRead, sizeof(infoRead), NULL); if (rc == sOKAY) { printf("%s\n", infoRead.mychar); } } } /* Free the cursor */ rdm_cursorFree(cursor); } /* Free the read lock on the table */ rdm_dbEnd(db); } return rc; }