Relations

Chapter Updated 06/17/25




The objective of this chapter is to provide information regarding functionality for setting up table relationships.

The xbRelation class is used to establish a relationship between a master table and one or more slave tables. (Each table is a .DBF file).

  • A master table can have one or more slave tables.
  • A slave table can be the master of another slave table.
  • Tables can be linked together to form a tree of relationships.
  • Each table relationship is set to be one of either optional (think left join) or required (think inner join).
  • Each open table can only be defined in one relationship. If multiple uses of the same table are required, open the table with multiple handles, one for each needed use.
  • Version 4.1.5 requires the slave tables be indexed on the fields used to define the linkage between the tables.

    Internal Processing

    The SetMaster() method initializes a tree of nodes that is used to define table relationships. Additionally, SetMaster() defines the root table in the tree. The AddRelation() method adds additional nodes to the tree. After the tree is built, method InitQuery() is used to create a linked list from the tree which is then used by the GetFirstRelRec(), GetNextRelRec(), GetPrevRelRec() and GetLastRelRec() methods.

    Methods for xbRelation

    MethodDescriptionParms
    xbRelate( xbXBase *xbase )ConstructorPointer to main xbase structure.
    ~xbRelate()Destructor
    xbInt16 AddRelation( xbDbf *dParent, const xbString &sParentExpression, xbDbf *dChild, xbIx *pChildIx, void *pChildTag, char cType = 'O', const xbString &sFilter = "" Add a slave relation to a master table. xbDbf *dParent - Pointer to parent table
    const xbString &sParentExpression - parent expression, needs to match pChildTag
    xbDbf *dChild - Pointer to child table
    xbIx *pChildIx - Pointer to child index
    void *pChildTag - Pointer to child tag
    char cType = 'O' - O = Optional, R = required
    const xbString &sFilter = "" - optional child record filter. Placeholder for future functionality. Not tested in version 4.1.5
    xbInt16 AddRelation( xbDbf *dParent, const xbString &sParentExpression, xbDbf *dChild, xbIx *pChildIx, void *pChildTag, char cType = 'O', const xbString &sFilter = "" Add a slave relation to a master table. xbDbf *dParent - Pointer to parent table
    const xbString &sParentExpression - parent expression, needs to match pChildTag
    xbDbf *dChild - Pointer to child table
    xbString &sChildTagName - Child Tag Name
    char cType = 'O' - O = Optional, R = required
    const xbString &sFilter = "" - optional child record filter. Placeholder for future functionality. Not tested in version 4.1.5
    xbInt16 CheckTblSts( xbDbf *d )Check status of given table for use in other relations. Returns XB_ALREADY_DEFINED if table already allocated to other relationship structure.
    void DumpRelationList()Available if XB_DEBUG_SUPPORT option enabled.
    Dump internal structure of relationship list. The list is built from the tree and is used by the GetFirst,GetNext, GetPrev aand GetLast routines.
    void DumpRelationTree()Available if XB_DEBUG_SUPPORT option enabled.
    Dump internal structure of relationship tree. The tree is initially built from the SetMaster() and AddRelation() routines.
    xbInt16 GetFirstRelRecord()Return the first record for the defined relationship.
    xbInt16 GetLastRelRecord()Return the last record for the defined relationship.
    xbInt16 GetNextRelRecord()Return the next record for the defined relationship.
    xbInt16 GetPrevRelRecord()Return the prev record for the defined relationship.
    xbInt16 SetMaster( xbDbf * d, const xbString &sFilter = "" ) Set the master table for the relationship. Call this method first after constructing an xbRelation. xbDbf * d - Pointer to the master table
    const char *sFilter = "" - Optional filter. Placeholder for future functionality. Not tested in version 4.1.5


    Example program demonstrating xbRelation class.

    /* xb_ex_relation.cpp XBase64 Software Library Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel The xb64 software library is covered under the terms of the GPL Version 3, 2007 license. Email Contact: XDB-devel@lists.sourceforge.net XDB-users@lists.sourceforge.net */ // This program demonstrates how to use the xbRelation class // usage: xb_ex_relation #include "xbase.h" using namespace xb; xbSchema RlMaster[] = { { "PRIM_KS0", XB_NUMERIC_FLD, 8, 0 }, { "FORGN_KS1", XB_CHAR_FLD, 3, 0 }, // foreign key 1 { "FORGN_KS2", XB_CHAR_FLD, 3, 0 }, // foreign key 2 { "CHAR_DATA3", XB_CHAR_FLD, 3, 0 }, { "",0,0,0 } }; xbSchema RlSlave1[] = { { "PRIM_KS1", XB_CHAR_FLD, 3, 0 }, { "FORGN_KS3", XB_CHAR_FLD, 3, 0 }, // foreign key 3 { "CHAR_DATA4", XB_CHAR_FLD, 4, 0 }, { "",0,0,0 } }; xbSchema RlSlave2[] = { { "PRIM_KS2", XB_CHAR_FLD, 3, 0 }, { "CHAR_DATA5", XB_CHAR_FLD, 5, 0 }, { "",0,0,0 } }; xbSchema RlSlave3[] = { { "PRIM_KS3", XB_CHAR_FLD, 3, 0 }, { "CHAR_DATA6", XB_CHAR_FLD, 6, 0 }, { "",0,0,0 } }; int main( int argCnt, char **av ) { int iRc = 0; xbXBase x; x.SetDataDirectory( PROJECT_DATA_DIR ); x.SetMultiUser( xbFalse ); xbDbf4 dMaster( &x ); xbDbf4 dSlave1( &x ); xbDbf4 dSlave2( &x ); xbDbf4 dSlave3( &x ); xbIx *pIx2; xbIx *pIx3; xbIx *pIx4; void *pTag2; void *pTag3; void *pTag4; xbRelate relate1( &x ); // define tables and indices if(( iRc = dMaster.CreateTable( "MASTER.DBF", "Master", RlMaster, XB_OVERLAY, XB_SINGLE_USER )) != XB_NO_ERROR ){ x.DisplayError( iRc ); } if(( iRc = dSlave1.CreateTable( "SLAVE1.DBF", "Slave1", RlSlave1, XB_OVERLAY, XB_SINGLE_USER )) != XB_NO_ERROR ){ x.DisplayError( iRc ); } if(( iRc = dSlave1.CreateTag( "MDX", "PRIM_KS1", "PRIM_KS1", "", 0, 0, XB_OVERLAY, &pIx2, &pTag2 )) != XB_NO_ERROR ){ x.DisplayError( iRc ); } if(( iRc = dSlave2.CreateTable( "SLAVE2.DBF", "Slave2", RlSlave2, XB_OVERLAY, XB_SINGLE_USER )) != XB_NO_ERROR ){ x.DisplayError( iRc ); } if(( iRc = dSlave2.CreateTag( "MDX", "PRIM_KS2", "PRIM_KS2", "", 0, 0, XB_OVERLAY, &pIx3, &pTag3 )) != XB_NO_ERROR ){ x.DisplayError( iRc ); } if(( iRc = dSlave3.CreateTable( "SLAVE3.DBF", "Slave3", RlSlave3, XB_OVERLAY, XB_SINGLE_USER )) != XB_NO_ERROR ){ x.DisplayError( iRc ); } if(( iRc = dSlave3.CreateTag( "MDX", "PRIM_KS3", "PRIM_KS3", "", 0, 0, XB_OVERLAY, &pIx4, &pTag4 )) != XB_NO_ERROR ){ x.DisplayError( iRc ); } // add some data to the tables dMaster.BlankRecord(); dMaster.PutLongField( "PRIM_KS0", 1 ); dMaster.PutField( "FORGN_KS1", "101" ); dMaster.PutField( "FORGN_KS2", "201" ); dMaster.PutField( "CHAR_DATA3", "AAA" ); dMaster.AppendRecord(); dMaster.Commit(); dMaster.BlankRecord(); dMaster.PutLongField( "PRIM_KS0", 2 ); dMaster.PutField( "FORGN_KS1", "102" ); dMaster.PutField( "FORGN_KS2", "202" ); dMaster.PutField( "CHAR_DATA3", "BBB" ); dMaster.AppendRecord(); dMaster.Commit(); dMaster.BlankRecord(); dMaster.PutLongField( "PRIM_KS0", 3 ); dMaster.PutField( "FORGN_KS1", "103" ); dMaster.PutField( "FORGN_KS2", "203" ); dMaster.PutField( "CHAR_DATA3", "CCC" ); dMaster.AppendRecord(); dMaster.Commit(); dMaster.BlankRecord(); dMaster.PutLongField( "PRIM_KS0", 4 ); dMaster.PutField( "FORGN_KS1", "104" ); dMaster.PutField( "FORGN_KS2", "204" ); dMaster.PutField( "CHAR_DATA3", "DDD" ); dMaster.AppendRecord(); dMaster.Commit(); dMaster.BlankRecord(); dMaster.PutLongField( "PRIM_KS0", 5 ); dMaster.PutField( "FORGN_KS1", "105" ); dMaster.PutField( "FORGN_KS2", "205" ); dMaster.PutField( "CHAR_DATA3", "EEE" ); dMaster.AppendRecord(); dMaster.Commit(); dSlave1.BlankRecord(); dSlave1.PutField( "PRIM_KS1", "101" ); dSlave1.PutField( "FORGN_KS3", "301" ); dSlave1.PutField( "CHAR_DATA4", "AAAA" ); dSlave1.AppendRecord(); dSlave1.Commit(); dSlave1.BlankRecord(); dSlave1.PutField( "PRIM_KS1", "102" ); dSlave1.PutField( "FORGN_KS3", "302" ); dSlave1.PutField( "CHAR_DATA4", "BBBB" ); dSlave1.AppendRecord(); dSlave1.Commit(); dSlave1.BlankRecord(); dSlave1.PutField( "PRIM_KS1", "103" ); dSlave1.PutField( "FORGN_KS3", "303" ); dSlave1.PutField( "CHAR_DATA4", "CCCC" ); dSlave1.AppendRecord(); dSlave1.Commit(); dSlave1.BlankRecord(); dSlave1.PutField( "PRIM_KS1", "104" ); dSlave1.PutField( "FORGN_KS3", "304" ); dSlave1.PutField( "CHAR_DATA4", "DDDD" ); dSlave1.AppendRecord(); dSlave1.Commit(); dSlave1.BlankRecord(); dSlave1.PutField( "PRIM_KS1", "105" ); dSlave1.PutField( "FORGN_KS3", "305" ); dSlave1.PutField( "CHAR_DATA4", "EEEE" ); dSlave1.AppendRecord(); dSlave1.Commit(); dSlave2.BlankRecord(); dSlave2.PutField( "PRIM_KS2", "201" ); dSlave2.PutField( "CHAR_DATA5", "AAAAA" ); dSlave2.AppendRecord(); dSlave2.Commit(); dSlave2.BlankRecord(); dSlave2.PutField( "PRIM_KS2", "202" ); dSlave2.PutField( "CHAR_DATA5", "BBBBB" ); dSlave2.AppendRecord(); dSlave2.Commit(); dSlave2.BlankRecord(); dSlave2.PutField( "PRIM_KS2", "203" ); dSlave2.PutField( "CHAR_DATA5", "CCCCC" ); dSlave2.AppendRecord(); dSlave2.Commit(); dSlave2.BlankRecord(); dSlave2.PutField( "PRIM_KS2", "204" ); dSlave2.PutField( "CHAR_DATA5", "DDDDD" ); dSlave2.AppendRecord(); dSlave2.Commit(); dSlave2.BlankRecord(); dSlave2.PutField( "PRIM_KS2", "205" ); dSlave2.PutField( "CHAR_DATA5", "EEEEE" ); dSlave2.AppendRecord(); dSlave2.Commit(); dSlave3.BlankRecord(); dSlave3.PutField( "PRIM_KS3", "301" ); dSlave3.PutField( "CHAR_DATA6", "AAAAAA" ); dSlave3.AppendRecord(); dSlave3.Commit(); dSlave3.BlankRecord(); dSlave3.PutField( "PRIM_KS3", "302" ); dSlave3.PutField( "CHAR_DATA6", "BBBBBB" ); dSlave3.AppendRecord(); dSlave3.Commit(); dSlave3.BlankRecord(); dSlave3.PutField( "PRIM_KS3", "304" ); dSlave3.PutField( "CHAR_DATA6", "DDDDDD" ); dSlave3.AppendRecord(); dSlave3.Commit(); dSlave3.BlankRecord(); dSlave3.PutField( "PRIM_KS3", "305" ); dSlave3.PutField( "CHAR_DATA6", "EEEEEE" ); dSlave3.AppendRecord(); dSlave3.Commit(); if(( iRc = relate1.SetMaster( &dMaster )) != XB_NO_ERROR ){ x.DisplayError( iRc ); } // example using tag name for relation if(( iRc = relate1.AddRelation( &dMaster, "FORGN_KS1", &dSlave1, "PRIM_KS1", 'O' )) != XB_NO_ERROR ){ x.DisplayError( iRc ); } // example using tag pointers for relation if(( iRc = relate1.AddRelation( &dMaster, "FORGN_KS2", &dSlave2, pIx3, pTag3, 'R' )) != XB_NO_ERROR ){ x.DisplayError( iRc ); } if(( iRc = relate1.AddRelation( &dSlave1, "FORGN_KS3", &dSlave3, "PRIM_KS3", 'R' )) != XB_NO_ERROR ){ x.DisplayError( iRc ); } #ifdef XB_DEBUG_SUPPORT // look at the internals relate1.GetFirstRelRecord(); relate1.DumpRelationTree(); relate1.DumpRelationList(); #endif // XB_DEBUG_SUPPORT // loop forward iRc = relate1.GetFirstRelRecord(); while( iRc == XB_NO_ERROR ){ std::cout << "GetNext example - do something with m rec# = " << dMaster.GetCurRecNo() << " s1 rec# = " << dSlave1.GetCurRecNo() << " s2 rec# = " << dSlave2.GetCurRecNo() << " s3 rec# = " << dSlave3.GetCurRecNo() << "\n"; iRc = relate1.GetNextRelRecord(); } std::cout << std::endl; // insert a blank line // loop backwards iRc = relate1.GetLastRelRecord(); while( iRc == XB_NO_ERROR ){ std::cout << "GetPrev example - do something with m rec# = " << dMaster.GetCurRecNo() << " s1 rec# = " << dSlave1.GetCurRecNo() << " s2 rec# = " << dSlave2.GetCurRecNo() << " s3 rec# = " << dSlave3.GetCurRecNo() << "\n"; iRc = relate1.GetPrevRelRecord(); } return iRc; }