Embedded C/C++ Application (Windows/MS Visual Studio)

Introduction

Now that you've gotten comfortable with creating a database in the How to Create a Schema section, lets build an application that will allow you to read and write to a database.

We recommend getting familiar with some of the concepts and tools that you will be using in this tutorial. We will be covering: interacting with the Transaction File Server (TFS), inserting a row with the function call rdm_dbInsertRow(), and reading rows with the function call rdm_cursorReadRow(). You may also find it useful to read about the DOCROOT as well.

  • The TFS is responsible for safely storing and retrieving objects. It is like a key/value store, but very fast and transactionally safe. The TFS owns and is co-located with the database files.
  • In this example rdm_dbInsertRow() will be used to insert a new row into two of our tables: AUTHOR and BOOK.
  • rdm_cursorReadRow() reads all columns from a row specified by the RDM_CURSOR and places the contents inside of the designated buffer.
  • The DOCROOT is a directory which is designated for holding databases available to a Transaction File Server (TFS). The concept is similar to a web server document root for storing web pages

Prerequisites:

Steps:

  1. Open Visual Studio and click Create a new project button;
  2. Change the All languages drop down menu to C++;
  3. Choose Console App or Empty Project and let's name our project bookStore;
  4. Rename bookStore.cpp to bookStore_main.c;
  5. Edit bookStore_main.c to look like this:
#include <stdio.h>;

int main(void)
{
    char name[20];
    printf("Hello. What's your name?\n");
    fgets(name, 20, stdin);
    printf("Hi there, %s", name);
    return 0;
}
  1. Add the project specific header file that we created earlier to the project: bookStore_structs.h;
  2. Our file structure should be all set up and will look like this:
     
  3. Add the library and header files that Raima provides to the VS project:
    • Under the Build tab, click on Configuration Manager and change Platform to x64 if not already so
    • Right click your project file located in the solution explorer
    • Navigate to the properties tab
    • Change the Platform to x64 if not already set
    • Click on the VC++ Directories tab Configuration Properties > VC++
    • To the right of Library Directories add the path containing the RDM libraries C:\Raima\RDM\15.2\lib;$(LibraryPath) (This is the path where I have installed Raima, this can vary depending on where you chose to install your copy) 
    • Click on the Linker tab and then General tab Configuration Properties > Linker > General
    • To the right of Additional Library Directories add the path containing the RDM libraries C:\Raima\RDM\15.2\lib;%(AdditionalLibraryDirectories) (This is the path where I have installed Raima, this can vary depending on where you chose to install your copy) 
    • Now move to the Input tab Configuration Properties > Linker > Input
    • To the right of Additional Dependencies click and add the .lib files that you need (These are the libraries that will be needed for this project. If you are building something different, other libraries might be required.): rdmtfs-15.lib;rdmrdm-15.lib;rdmbase-15.lib;rdmtx-15.lib
    • Click on the C/C++ tab and then General tab Configuration Properties > C/C++ > General
    • To the right of Additional Include Directories add the path containing the RDM include folder C:\Raima\RDM\15.2\/include;$(JAVA_HOME)\include;$(JAVA_HOME)\include\win32%(AdditionalIncludeDirectories) (This is the path where I have installed Raima, this can vary depending on where you chose to install your copy)
  4. Add these includes to the top of bookStore_main.c:
#include "rdm.h"
#include "bookStore_structs.h"
  1. In the bookStore_main.c file, change the main function to look like this:
#include <stdio.h>
#include <stdlib.h>

#include "rdmapi.h"
#include "bookStore_structs.h"

int main (int argc, const char* const* argv)
{
    RDM_RETCODE rc; /* Status/Error Return Code */
    RDM_TFS tfs;    /* TFS Handle */
    RDM_DB  db;     /* Database Handle */

    /* Allocate a TFS Handle */
    rc = rdm_rdmAllocTFS (&tfs);
    if (rc == sOKAY)
    {
        rc = rdm_tfsInitialize (tfs);
        if (rc == sOKAY)
        {
            /* Allocate a database handle */
            rc = rdm_tfsAllocDatabase (tfs, &db);
            if (rc == sOKAY)
            {
                /* Open the database */
                rc = rdm_dbOpen (db, "bookStore", RDM_OPEN_SHARED);
                if (rc == sOKAY)
                {
                    /* Insert a row to database */
                    rc = writeARow (db);
                    if (rc == sOKAY)
                    {
                        /* Read the rows */
                        rc = readRows (db);
                    }
                    rdm_dbClose (db);
                }
                else
                {
                    fprintf (stderr, "\nSorry, can't open bookStore database.\n");
                    fprintf (stderr, "Error: %s (%d): %s\n", rdm_retcodeGetName (rc), rc, rdm_retcodeGetDescription (rc));
                }
            }
            rdm_dbFree (db);
        }
        rdm_tfsFree (tfs);
    }

    return rc == sOKAY ? EXIT_SUCCESS : EXIT_FAILURE;
}

Your program won't do much at this point, but it should compile and run. I would recommend reading through the code and comments to understand how things are set up. Go ahead and run it to make sure everything is working.

  1. Next, lets add some functionality. We will add a function that takes user input from the command line and adds a book and author into our corresponding tables (Don't forget to uncomment the writeARow function call inside of the main function shown on line 23 in the code snippet above).
/* Write a row to from author and book tables */
RDM_RETCODE writeARow (RDM_DB db)
{
    RDM_RETCODE rc;
    RDM_TABLE_ID tables[] = {TABLE_BOOK, TABLE_AUTHOR};

    char last_name[13];
    char full_name[35];
    char bookid[14];
    char title[105];
    char price[10];
    RDM_BCD_T dPrice;
    char *inputs[5] = {last_name, full_name, bookid, title, price};
    unsigned int len;
    int ii = 0;

    printf ("ADDING AUTHOR TO DATABASE\nAuthor Last Name:\n");
    fgets (last_name, 13, stdin);
    printf ("Author Full Name:\n");
    fgets (full_name, 35, stdin);
    printf ("ADDING BOOK TO DATABASE\nTitle of Book:\n");
    fgets (title, 105, stdin);
    printf ("Enter id located on book:\n");
    fgets (bookid, 14, stdin);
    printf ("Price:\n");
    fgets (price, 10, stdin);

    /* Remove newline character from fgets */
    while (ii < (sizeof (inputs) / sizeof (inputs[0])))
    {
        len = strlen (inputs[ii]);
        if (inputs[ii][len - 1] == '\n')
        {
            inputs[ii][len - 1] = '\0';
        }
        ii++;
    }

    /* Start an update transaction and lock the table */
    rc = rdm_dbStartUpdate (db, tables, RDM_LEN(tables), NULL, 0, NULL);
    if (rc == sOKAY)
    {
        AUTHOR authInsert; /* Row buffer */
        BOOK bookInsert;   /* Row buffer */

        /* Populate the columns in the AUTHOR Row buffer */
        strcpy (authInsert.LAST_NAME, last_name);
        strcpy (authInsert.FULL_NAME, full_name);
        authInsert._FULL_NAME_has_value = RDM_COL_HAS_VALUE;

        /* Insert a row into the table */
        rc = rdm_dbInsertRow (db, TABLE_AUTHOR, &authInsert, sizeof (authInsert), NULL);

        if (rc == sOKAY)
        {
            /* Convert the string to a binary-coded decimal */
            rdm_bcdFromString (price, &dPrice);

            /* Populate the columns in the BOOK Row buffer */
            strcpy (bookInsert.TITLE, title);
            bookInsert._TITLE_has_value = RDM_COL_HAS_VALUE;
            strcpy (bookInsert.BOOKID, bookid);
            bookInsert.PRICE = dPrice;
            bookInsert._PRICE_has_value = RDM_COL_HAS_VALUE;

            /* Insert a row into the table */
            rc = rdm_dbInsertRow (db, TABLE_BOOK, &bookInsert, sizeof (bookInsert), NULL);
        }
        if (rc == sOKAY)
        {
            /* Commit a transaction */
            printf ("The book %s by %s at a price of $%s was added to the database successfully.\n",
                    title, full_name, price);
            rc = rdm_dbEnd (db);
        }
        else
        {
            /* Abort the transaction */
            fprintf (stderr, "A problem occurred when adding data to the database.\n");
            fprintf (stderr, "Error: %s (%d): %s\n", rdm_retcodeGetName (rc), rc, rdm_retcodeGetDescription (rc));
            (void) rdm_dbEndRollback (db);
        }
    }
    return rc;
}
  1. Now run the program again and make sure you are able to add authors and books to the database.

  2. Finally, we will add a readRows function to our program that will allow us to see what we have added (Don't forget to uncomment the readRows function call inside of the main function shown on line 27 in the code snippet above):
/* Read rows from author table */
RDM_RETCODE readRows (RDM_DB db)
{
    RDM_RETCODE rc;
    RDM_TABLE_ID tables[] = {TABLE_AUTHOR};
    AUTHOR authRead;
    RDM_CURSOR cursor = NULL;

    rc = rdm_dbStartRead (db, tables, RDM_LEN(tables), NULL);
    if (rc == sOKAY)
    {
        rc = rdm_dbGetRows (db, TABLE_AUTHOR, &cursor);
        if (rc == sOKAY)
        {
            /* Navigate to the first row in the cursor */
            printf ("Displaying all of the authors in the author table\n");
            rc = rdm_cursorMoveToFirst (cursor);
            while (rc == sOKAY)
            {
                /* Read the row column values */
                rc = rdm_cursorReadRow (cursor, &authRead, sizeof (authRead), NULL);
                if (rc == sOKAY)
                {
                    printf ("%s", authRead.LAST_NAME);

                    /* Move to the next row in the cursor */
                    rc = rdm_cursorMoveToNext (cursor);
                }
            }

            /* We expect to break out of the loop with a sENDOFCURSOR code*/
            if (rc == sENDOFCURSOR)
            {
                rc = sOKAY;
            }

            /* Free the cursor allocated in rdm_dbGetRows */
            rdm_cursorFree (cursor);
        }

        /* release the read locks */
        (void) rdm_dbEnd (db);
    }
    return rc;
}

Conclusion

Your program should be fully functional at this point. You set up an Embedded TFS which is used when you are interacting with a database that is not shared with other processes, and you wrote and read rows from the database by interacting with the TFS.

Next, you might want to add some more functionality by deleting a row with rdm_cursorDeleteRow() or update a row with rdm_cursorUpdateRow(). Some other concepts to look over might be:

The full source file can be found here: learn/bookStore_embed.c