core03Example_main.c

Set instance. Create database containing two record types and a set between them. Create one owner, several members, then scan owner(s) and their members. This example needs a compiled schema, core03Example.sdl.

/*
* Raima Database Manager
*
* Copyright (c) 2019 Raima Inc., All rights reserved.
*
* Use of this software, whether in source code format, or in executable,
* binary object code form, is governed by the Raima LICENSE which
* is fully described in the LICENSE.TXT file, included within this
* distribution of files.
*/
/* \file
* \brief Source code for the RDM core03 example
*/
#include <stdio.h>
#include <string.h>
#include "example_fcns.h"
#include "rdm.h"
#include "rdmstartupapi.h"
/* Generated \c struct and \c typedef definitions to be used with the RDM APIs
*/
#include "core03Example_structs.h"
/* Generated catalog definition to be used with the RDM rdm_dbSetCatalog() API
*/
#include "core03Example_cat.h"
const char *const description =
"Demonstrates associating rows in one table with a row in another table";
const RDM_CMDLINE_OPT opts[] = {{NULL, NULL, NULL, NULL}};
/*
* \mainpage Core03 Popcorn Example
*
* Each time you run this example:
* \li The database is initialized (all existing contents removed)
* \li Several new artists are added to the database
* \li For each artist a couple albums are added to the database
* \li A list of each artist and their albums are displayed
*
* \par Table of Contents
*
* - \subpage hDB
* - \subpage hPGMfunc
*
* For additional information, please refer to the product documentation at
* http://docs.raima.com/.
*
* \page hDB Database Schema
*
* \par Database Schema Definition
*
* The DDL (Database Definition Language) specification for the database used
* in this example is located in the file \c core03.sdl.
*
* \include core03.sdl
*
* The schema was compiled using the RDM rdm-compile utility with options
* to generate C-structures and catalog files for interfacing with the database.
* The option to generate lowercase struct member names was also used
*
* \code rdm-compile --c-structs --catalog --lc-struct-members core03.sdl
* \endcode
*
* \page hPGMfunc Program Functions
* \li insertArtist() - \copybrief insertArtist
* \li readAllAlbums() - \copybrief readAllAlbums
* \li main() - \copybrief main
*/
/* \brief Insert an artist and all of the albums associated with the artist
*
* This function adds one more row to the ARTIST table in the core03 database
* and one to many rows to the ALBUM table associated with the given artist.
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
RDM_RETCODE insertArtist (
RDM_DB hDB, /*< [in] RDM db handle with the database open */
const char *artistName, /*< [in] Artist name to insert */
RDM_CURSOR *artistCursor) /*< [out] Cursor position of artist just added */
{
ARTIST artist_rec;
artist_rec.id = 0;
strncpy (artist_rec.name, artistName, sizeof (artist_rec.name));
hDB, TABLE_ARTIST, &artist_rec, sizeof (artist_rec), artistCursor);
return rc;
}
/* \brief Insert an albums and associated with the artist
*
* This function adds new ALBUM rows to the database and associates (links) them
* with the specific ARTIST using the cursor position provided.
*
* \note Notice in function that we do not explicitly allocate the cursor passed
* to the rdm_dbInsertRow() function. A "shortcut" for cursor association
* functions is to pass a cursor set to NULL to the function and it will be
* allocated for you. You still free the cursor object when finished to release
* the resources associated with the cursor.
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
RDM_RETCODE insertAlbums (
RDM_DB hDB, /*< [in] RDM db handle with the database open */
RDM_CURSOR artistCursor, /*< [in] Cursor position of artist just added */
const char **albumList, /*< [in] List of album names for the artist */
size_t listSize) /*< [in] Number of album names in the list */
{
int ii;
for (ii = 0; ii < (int) listSize; ii++)
{
ALBUM album_rec;
RDM_CURSOR albumCursor = NULL;
strncpy (album_rec.title, albumList[ii], sizeof (album_rec.title));
album_rec._id_has_value = RDM_FALSE; /* Mark the row as unlinked */
hDB, TABLE_ALBUM, &album_rec, sizeof (album_rec), &albumCursor);
if (rc == sOKAY)
{
/* associate the added row to the artist */
rc = rdm_cursorLinkRow (albumCursor, REF_ALBUM_ID, artistCursor);
rdm_cursorFree (albumCursor);
}
}
return rc;
}
/* \brief Reads and displays all rows in the core03 database
*
* This function reads and displays each row from the ARTIST table in the core03
* database and the ALBUMS associated with the artist.
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
RDM_RETCODE readAllAlbums (
RDM_DB hDB) /*< [in] RDM db handle with the database open */
{
RDM_CURSOR artistCursor = NULL;
RDM_CURSOR albumCursor = NULL;
rc = rdm_dbStartRead (hDB, RDM_LOCK_ALL, 0, NULL);
if (rc == sOKAY)
{
/* read the ARTIST table in table order */
rc = rdm_dbGetRows (hDB, TABLE_ARTIST, &artistCursor);
}
if (rc == sOKAY)
{
for (rc = rdm_cursorMoveToFirst (artistCursor); rc == sOKAY;
rc = rdm_cursorMoveToNext (artistCursor))
{
char Artist[RDM_COLUMN_SIZE (ARTIST, name)];
/* We found an artist row, read the contents */
artistCursor, COL_ARTIST_NAME, Artist, sizeof (Artist), NULL);
if (rc == sOKAY)
{
printf ("\nArtist: %s\n", Artist);
/* read the album rows associated with the artist*/
artistCursor, REF_ALBUM_ID, &albumCursor);
if (rc == sOKAY)
{
printf ("\nAlbums:");
for (rc = rdm_cursorMoveToFirst (albumCursor); rc == sOKAY;
rc = rdm_cursorMoveToNext (albumCursor))
{
char Title[RDM_COLUMN_SIZE (ALBUM, title)];
/* We found an album row, read the contents*/
albumCursor, COL_ALBUM_TITLE, Title, sizeof (Title),
NULL);
if (rc == sOKAY)
{
printf ("\t%s\n", Title);
}
}
/* We expect rc to be sENDOFCURSOR when we break out of the
* loop */
if (rc == sENDOFCURSOR)
{
rc = sOKAY; /* change status to sOKAY because
sENDOFCURSOR was expected. */
}
else
{
}
}
}
}
/* We expect rc to be sENDOFCURSOR when we break out of the loop */
if (rc == sENDOFCURSOR)
{
rc = sOKAY; /* change status to sOKAY because sENDOFCURSOR was
expected. */
}
else
{
}
if (albumCursor)
rdm_cursorFree (albumCursor);
if (artistCursor)
rdm_cursorFree (artistCursor);
rdm_dbEnd (hDB);
}
return rc;
}
/* List of albums for The Doors */
static const char *doors_albums[] = {"The Doors", "Strange Days",
"Waiting for the Sun"};
/* List of albums for The Rolling Stones */
static const char *stones_albums[] = {"The Rolling Stones", "Out of Our Heads",
"Beggars Banquet", "Tattoo You"};
/* List of albums for Nirvana */
static const char *nirvana_albums[] = {"Bleach", "Nevermind", "In Utero"};
/* \brief Insert an artist and albums and associated with the artist
*
* This function adds new ARTIST rows to the database and then adds ALBUMS using
* the cursor position returned.
*
* \note If the insert of the ARTIST or their associated ALBUMS fails, the
* transaction is rolled back.
*
* @return Returns an \c RDM_RETCODE code (\b sOKAY if successful)
*/
RDM_RETCODE insertAllArtists (RDM_DB hDB)
{
RDM_CURSOR artistCursor = NULL;
rc = rdm_dbStartUpdate (hDB, RDM_LOCK_ALL, 0, NULL, 0, NULL);
if (rc == sOKAY)
{
rc = insertArtist (hDB, "The Doors", &artistCursor);
if (rc == sOKAY)
{
rc = insertAlbums (
hDB, artistCursor, doors_albums,
(sizeof (doors_albums) / sizeof (char *)));
}
/* if insert of both Artist and Album are ok,
* commit the transaction, otherwise roll it back */
if (rc == sOKAY)
rc = rdm_dbEnd (hDB);
else
rc = rdm_dbEndRollback (hDB);
}
rc = rdm_dbStartUpdate (hDB, RDM_LOCK_ALL, 0, NULL, 0, NULL);
if (rc == sOKAY)
{
rc = insertArtist (hDB, "The Rolling Stones", &artistCursor);
if (rc == sOKAY)
{
rc = insertAlbums (
hDB, artistCursor, stones_albums,
(sizeof (stones_albums) / sizeof (char *)));
}
/* if insert of both Artist and Album are ok,
* commit the transaction, otherwise roll it back */
if (rc == sOKAY)
rc = rdm_dbEnd (hDB);
else
rc = rdm_dbEndRollback (hDB);
}
rc = rdm_dbStartUpdate (hDB, RDM_LOCK_ALL, 0, NULL, 0, NULL);
if (rc == sOKAY)
{
rc = insertArtist (hDB, "Nirvana", &artistCursor);
if (rc == sOKAY)
{
rc = insertAlbums (
hDB, artistCursor, nirvana_albums,
(sizeof (nirvana_albums) / sizeof (char *)));
}
/* if insert of both Artist and Album are ok,
* commit the transaction, otherwise roll it back */
if (rc == sOKAY)
rc = rdm_dbEnd (hDB);
else
rc = rdm_dbEndRollback (hDB);
}
return rc;
}
/* \brief Main function for core03 example
*
* The function initializes the RDM environment and runs the create, read
* operations.
*
* @return Returns the \c RDM_RETCODE on exit.
*/
int main_core03 (int argc, const char *const *argv)
{
RDM_TFS hTFS;
RDM_DB hDB;
rc = rdm_cmdlineInit (&cmd, argc, argv, description, opts);
if (rc != sCMD_USAGE)
if (rc == sOKAY)
{
rc = exampleOpenEmptyDatabase (&hTFS, &hDB, "core03Example", core03Example_cat);
if (rc == sOKAY)
{
rc = insertAllArtists (hDB);
if (rc == sOKAY)
{
rc = readAllAlbums (hDB);
}
exampleCleanup (hTFS, hDB);
}
}
return (int) rc;
}
#define RDM_COLUMN_SIZE(table, column)
Macro for getting the size of a column in the struct definition.
Definition: rdmtypes.h:197
RDM_RETCODE rdm_cursorMoveToFirst(RDM_CURSOR cursor)
Position a cursor to the first row in the collection.
RDM_RETCODE rdm_cursorMoveToNext(RDM_CURSOR cursor)
Position a cursor to the next row in the collection.
Header for the native RDM Runtime API.
#define exampleOpenEmptyDatabase(tfs, db, name, catalog)
Definition: example_fcns.h:39
@ RDM_FALSE
Definition: psptypes.h:59
RDM_RETCODE rdm_dbEnd(RDM_DB db)
End a transactional operation.
@ sCMD_USAGE
Definition: rdmretcodetypes.h:72
struct RDM_CURSOR_S * RDM_CURSOR
Definition: rdmtypes.h:306
RDM_RETCODE rdm_dbStartRead(RDM_DB db, const RDM_TABLE_ID *tableIds, uint32_t numTableIds, RDM_TRANS *pTrans)
Get read locks.
The buffer used by the command line parser to hold state information.
Definition: rdmcmdlinetypes.h:85
#define exampleCleanup(tfs, db)
Definition: example_fcns.h:47
RDM_RETCODE rdm_cursorLinkRow(RDM_CURSOR cursor, RDM_REF_ID refId, RDM_CURSOR cursorOwner)
Link a row to an owner.
#define print_error(rc)
Definition: example_fcns.h:34
@ sOKAY
Definition: rdmretcodetypes.h:96
Generic usage function option record.
Definition: rdmcmdlinetypes.h:32
RDM_RETCODE rdm_cursorFree(RDM_CURSOR cursor)
Free an RDM_CURSOR.
#define RDM_STARTUP_EXAMPLE(name)
Definition: rdmstartuptypes.h:73
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_dbEndRollback(RDM_DB db)
End and rollback a transactional operation.
RDM_RETCODE rdm_cursorGetMemberRows(RDM_CURSOR ownerCursor, RDM_REF_ID refId, RDM_CURSOR *pCursor)
Associate an RDM_CURSOR with members.
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.
RDM_RETCODE rdm_cursorReadColumn(RDM_CURSOR cursor, RDM_COLUMN_ID columnId, void *columnValue, size_t bytesIn, size_t *bytesOut)
Read a single column from a table row.
struct RDM_TFS_S * RDM_TFS
RDM TFS Handle.
Definition: rdmtfstypes.h:21
RDM_RETCODE rdm_cmdlineInit(RDM_CMDLINE *cmd, int32_t argc, const char *const argv[], const char *description, const RDM_CMDLINE_OPT *opts)
Initialize an RDM_CMDLINE buffer and validate the command line.
struct RDM_DB_S * RDM_DB
Definition: rdmtypes.h:305
#define RDM_LOCK_ALL
Definition: rdmtypes.h:170
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.
@ sENDOFCURSOR
Definition: rdmretcodetypes.h:59
Internal RDM Startup API used by startup macros.
RDM_RETCODE
RDM status and error return codes.
Definition: rdmretcodetypes.h:44