Products Support Documentation Download
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.

11 #include "rdm.h" /* The RDM API. */
12 #include "hello_world_structs.h" /* The hello_world database definitions. */
13 #include "hello_world_cat.h" /* The hello_world database catalog */
Header for the native RDM Runtime API.
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.

107  /* Allocate a TFS Handle */
108  rc = rdm_rdmAllocTFS("", &tfs);
109  if (rc == sOKAY)
110  {
111  rc = rdm_tfsDropDatabase(tfs, "hello_world");
112  if (rc == eNODB)
113  {
114  /* It is okay if there wasn't a database to drop */
115  rc = sOKAY;
116  }
117  if (rc == sOKAY)
118  {
119  /* Allocate a database hande */
120  rc = rdm_tfsAllocDatabase(tfs, &db);
RDM_RETCODE rdm_tfsDropDatabase(RDM_TFS tfs, const RDM_TCHAR_T *dbNameSpec)
Drop the specified database.
RDM_RETCODE rdm_rdmAllocTFS(const RDM_TCHAR_T *optString, RDM_TFS *phTFS)
Allocate the TFS handle.
RDM_RETCODE rdm_tfsAllocDatabase(RDM_TFS tfs, RDM_DB *pDb)
Allocate memory for a new RDM 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).

129  /* Open the database */
130  rc = rdm_dbOpen(db, "hello_world", RDM_OPEN_SHARED);
131  if (rc == sOKAY)
132  {
RDM_RETCODE rdm_dbOpen(RDM_DB db, const RDM_TCHAR_T *dbNameSpec, RDM_OPEN_MODE mode)
Open an existing RDM database using the specified database handle.

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.

62 /* Insert "helloworld" row to database */
63 static RDM_RETCODE writeARow(
64  RDM_DB db)
65 {
66  RDM_TABLE_ID tables[] = { TABLE_INFO };
69  /* Start an update transaction and lock the table */
70  rc = rdm_dbStartUpdate(db, tables, RDM_LEN(tables), NULL, 0, NULL);
71  if (rc == sOKAY)
72  {
73  INFO infoInserted; /* Row buffer */
75  /* Populate the mychar column in the Row buffer */
76  strcpy(infoInserted.mychar, "Hello World! - using the embedded TFS");
78  /* Insert a row into the table */
79  rc = rdm_dbInsertRow(db, TABLE_INFO, &infoInserted, sizeof(infoInserted), NULL);
80  if (rc == sOKAY)
81  {
82  /* Commit a transaction */
83  rc = rdm_dbEnd(db);
84  }
85  else
86  {
87  /* Abort the transaction */
89  }
90  }
91  return rc;
92 }
#define RDM_LEN(x)
Definition: psptypes.h:194
RDM_RETCODE rdm_dbStartUpdate(RDM_DB db, const RDM_TABLE_ID *writeTableIds, uint32_t numWriteTableIds, const RDM_TABLE_ID *readTableIds, uint32_t numReadTableIds, RDM_TRANS *pTrans)
Get write locks.
RDM_RETCODE rdm_dbEnd(RDM_DB db)
End a transactional operation.
uint32_t RDM_TABLE_ID
Definition: rdmtypes.h:25
RDM status and error return codes.
RDM_RETCODE rdm_dbEndRollback(RDM_DB db)
End and rollback a transactional operation.
struct RDM_DB_S * RDM_DB
Definition: rdmtypes.h:239
RDM_RETCODE rdm_dbInsertRow(RDM_DB db, RDM_TABLE_ID tableId, const void *colValues, size_t bytesIn, RDM_CURSOR *pCursor)
Insert a new row into a table at the specified rowId.
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 Locking and Transaction Start Functions

17 /* Read "helloworld" row to database */
18 static RDM_RETCODE readARow(
19  RDM_DB db)
20 {
21  RDM_TABLE_ID tables[] = { TABLE_INFO };
24  /* Start an update transaction and lock the table */
25  rc = rdm_dbStartRead(db, tables, RDM_LEN(tables), NULL);
26  if (rc == sOKAY)
27  {
28  RDM_CURSOR cursor;
30  rc = rdm_dbAllocCursor(db, &cursor);
31  if (rc == sOKAY)
32  {
33  /* Fill a cursor with all the rows in a table */
34  rc = rdm_dbGetRows(db, TABLE_INFO, &cursor);
35  if (rc == sOKAY)
36  {
37  /* Move to the first row in the info table */
38  rc = rdm_cursorMoveToFirst(cursor);
39  if (rc == sOKAY)
40  {
41  INFO infoRead; /* Row buffer */
43  /* Read the full content of the current record */
44  rc = rdm_cursorReadRow(cursor, &infoRead, sizeof(infoRead), NULL);
45  if (rc == sOKAY)
46  {
47  printf("%s\n", infoRead.mychar);
48  }
49  }
50  }
51  /* Free the cursor */
52  rdm_cursorFree(cursor);
53  }
54  /* Free the read lock on the table */
55  rdm_dbEnd(db);
56  }
57  return rc;
58 }
#define RDM_LEN(x)
Definition: psptypes.h:194
Definition: rdmtypes.h:240
RDM_RETCODE rdm_dbEnd(RDM_DB db)
End a transactional operation.
RDM_RETCODE rdm_dbGetRows(RDM_DB db, RDM_TABLE_ID tableId, RDM_CURSOR *pCursor)
Associate an RDM_CURSOR with rows based on a table id.
RDM_RETCODE rdm_cursorReadRow(RDM_CURSOR cursor, void *colValues, size_t bytesIn, size_t *bytesOut)
Read all columns from a row.
uint32_t RDM_TABLE_ID
Definition: rdmtypes.h:25
RDM_RETCODE rdm_cursorFree(RDM_CURSOR cursor)
RDM_RETCODE rdm_dbAllocCursor(RDM_DB db, RDM_CURSOR *pCursor)
Allocate a cursor.
RDM status and error return codes.
RDM_RETCODE rdm_dbStartRead(RDM_DB db, const RDM_TABLE_ID *tableIds, uint32_t numTableIds, RDM_TRANS *pTrans)
Get read locks.
struct RDM_DB_S * RDM_DB
Definition: rdmtypes.h:239
RDM_RETCODE rdm_cursorMoveToFirst(RDM_CURSOR cursor)
Position a cursor to the first row in the collection.