//
// File: hdbRepos.cpp
//
// Legal Notice: This file is part of the HadronZoo C++ Class Library. Copyright 2025 HadronZoo Project (http://www.hadronzoo.com)
//
// The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free
// Software Foundation, either version 3 of the License, or any later version.
//
// The HadronZoo C++ Class Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses.
//
//
// Implimentation of the HadronZoo Proprietary Database Suite
//
#include <iostream>
#include <fstream>
#include <cstdio>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "hzBasedefs.h"
#include "hzString.h"
#include "hzSMAR.h"
#include "hzChars.h"
#include "hzChain.h"
#include "hzDate.h"
#include "hzTextproc.h"
#include "hzCodec.h"
#include "hzDocument.h"
#include "hzDirectory.h"
#include "hzDatabase.h"
#include "hzProcess.h"
using namespace std ;
/*
** Variables
*/
extern hzSMAR* g_pSMAR_Edo ; // SMAR for EDOs
/*
** hzEdo Functions
*/
void hzEdo::Clear (void)
{
// Release space
_hzfunc("hzEdo::Clear") ;
uchar* pSlot ; // EDO string space
uint32_t nSize ; // EDO size
if (m_addr)
{
pSlot = g_pSMAR_Edo->Xlate(m_addr) ;
if (!pSlot)
{
hzerr(E_CORRUPT, "Could not Xlate address %u", m_addr) ;
m_addr = 0 ;
return ;
}
if (pSlot[0] & 0x80)
{
if (pSlot[0] & 0x40)
nSize = ((pSlot[0] & 0x3f) << 16) + (pSlot[1] << 8) + pSlot[2] ;
else
nSize = ((pSlot[0] & 0x3f) << 8) + pSlot[1] ;
}
else
nSize = pSlot[0] ;
if (!nSize)
hzerr(E_CORRUPT, "Zero sized EDO at address %u:%u", m_addr/0xffff, m_addr%0xffff) ;
else
g_pSMAR_Edo->Free(m_addr, nSize) ;
m_addr = 0 ;
}
}
uint32_t hzEdo::Size (void) const
{
// Yeild the EDO size
uchar* pSlot ; // EDO string space
if (m_addr)
{
pSlot = g_pSMAR_Edo->Xlate(m_addr) ;
if (pSlot[0] & 0x80)
{
if (pSlot[0] & 0x40)
return ((pSlot[0] & 0x3f) << 16) + (pSlot[1] << 8) + pSlot[2] ;
return ((pSlot[0] & 0x3f) << 8) + pSlot[1] ;
}
return pSlot[0] ;
}
return 0 ;
}
uint32_t hzEdo::Total (void) const
{
// Yeild the EDO size
uchar* pSlot ; // EDO string space
uint32_t nSize ; // EDO size
if (!m_addr)
return 0 ;
pSlot = g_pSMAR_Edo->Xlate(m_addr) ;
if (!pSlot)
return 0 ;
if (pSlot[0] & 0x80)
{
if (pSlot[0] & 0x40)
nSize = (((pSlot[0] & 0x3f) << 16) + (pSlot[1] << 8) + pSlot[2]) + 3 ;
else
nSize = (((pSlot[0] & 0x3f) << 8) + pSlot[1]) + 2 ;
}
else
nSize = pSlot[0] + 1 ;
return nSize ;
}
const char* hzEdo::Txt (void) const
{
// Return 'text' value
if (!m_addr)
return 0 ;
return (const char*) g_pSMAR_Edo->Xlate(m_addr) ;
}
hzEdo& hzEdo::SetValue (const hzChain& Z)
{
// Set the EDO to the value in the supplied chain
_hzfunc("hzEdo::SetValue") ;
chIter zi ; // Chain iterator
uchar* pSlot ; // EDO slot
uchar* u ; // EDO slot iterator
if (!g_pSMAR_Edo)
g_pSMAR_Edo = new hzSMAR(300) ;
Clear() ;
if (Z.Size())
{
m_addr = g_pSMAR_Edo->Alloc(Z.Size()) ;
u = pSlot = g_pSMAR_Edo->Xlate(m_addr) ;
for (zi = Z ; !zi.eof() ; u++, zi++)
*u = *zi ;
}
return *this ;
}
hzEcode hzEdo::GetValue (hzChain& Z) const
{
// Populate supplied chain with EDO value
//
// Argument: Z The target chain
//
// Returns: E_CORRUPT If the EDO address is illegal
// E_OK Operation successful
_hzfunc("hzEdo::GetValue") ;
uchar* pSlot ; // EDO slot
uint32_t nSize ; // EDO size
uint32_t nC ; // Char counter
Z.Clear() ;
if (!m_addr)
return 0 ;
pSlot = g_pSMAR_Edo->Xlate(m_addr) ;
if (!pSlot)
return hzerr(E_CORRUPT, "Illegal address %u:%u", m_addr/0xffff, m_addr%0xffff) ;
if (pSlot[0] & 0x80)
{
if (pSlot[0] & 0x40)
nSize = (((pSlot[0] & 0x3f) << 16) + (pSlot[1] << 8) + pSlot[2]) + 3 ;
else
nSize = (((pSlot[0] & 0x3f) << 8) + pSlot[1]) + 2 ;
}
else
nSize = pSlot[0] + 1 ;
for (nC = 0 ; nC < nSize ; nC++)
{
Z.AddByte(pSlot[nC]) ;
}
return E_OK ;
}
hzEdo& hzEdo::operator= (const hzEdo& op)
{
if (m_addr == op.m_addr)
return *this ;
Clear() ;
m_addr = op.m_addr ;
return *this ;
}
/*
** Prototypes
*/
uint32_t Datatype2Size (hdbBasetype eType) ;
const char* _hds_showinitstate (hdbIniStat nState) ;
void _hdb_ck_initstate (const hzString& objName, hdbIniStat eActual, hdbIniStat eExpect) ;
/*
** hzSMAR Interface
*/
class _edoItem
{
// EDO control area (start of EDO).
public:
uchar m_size[1] ; // Start of serial integer size (1 to 3 bytes)
char m_resv[3] ; // Start of data in the case of bytes < 128 bytes
void _setSize (uint32_t nSize) ;
uint32_t _getSize (void) ;
char* _data (void) ;
} ;
void _edoItem::_setSize (uint32_t nSize)
{
if (nSize < 128)
m_size[0] = nSize ;
else if (nSize < SMAR_OVERSIZE)
{
m_size[0] = 128 + (nSize / 256) ;
m_size[1] = nSize % 256 ;
}
else if (nSize < HZSTRING_MAXLEN)
{
m_size[0] = 192 + (nSize / 0xffff) ;
m_size[1] = (nSize & 0xff00) >> 8 ;
m_size[2] = nSize & 0xff ;
}
else
{
// Invalid size
m_size[0] = m_size[1] = m_size[2] = 0xff ;
}
}
uint32_t _edoItem::_getSize (void)
{
if (m_size[0] & 0x80)
{
if (m_size[0] & 0x40)
return ((m_size[0] & 0x3f) << 16) + (m_size[1] << 8) + m_size[2] ;
return ((m_size[0] & 0x3f) << 8) + m_size[1] ;
}
return m_size[0] ;
}
char* _edoItem::_data (void)
{
// Note that in setting a string, _setSize must be done first before using the pointer returned by this function to set the string value
if (m_size[0] & 0x80)
{
if (m_size[0] & 0x40)
return m_resv + 2 ;
return m_resv + 1 ;
}
return m_resv ;
}
uint32_t _edoAlloc (uint32_t nSize)
{
// Allocate edo space. If the regime does not yet exist, create it with id code of 100.
if (!g_pSMAR_Edo)
g_pSMAR_Edo = new hzSMAR(300) ;
return g_pSMAR_Edo->Alloc(nSize) ;
}
/*
** hdbObjRepos Cache Functions
*/
hzEcode hdbObjRepos::FetchEDO (hzChain& edo, uint32_t objId) const
{
// Fetch the EDO indicated by the supplied object id, into the supplied chain
//
// Arguments: 1) edo hzChain as EDO recepticle
// 2) objId The object id
//
// Returns: E_NOTFOUND If an EDO of the id is not found
// E_RANGE If the supplied object ID is 0 or above the highest ID issued so far
// E_OK Operation successful
_hzfunc("hdbObjRepos::FetchEDO") ;
if (!this)
Fatal("No Instance\n") ;
edo.Clear() ;
if (!objId) return hzerr(E_RANGE, "Illegal object ID (0)") ;
if (objId > m_nSeqId) return hzerr(E_RANGE, "Object ID exceeds highest (%u)", m_nSeqId) ;
if (m_eMode & HDB_REPOS_CACHE)
{
if (!m_Cache.Exists(objId))
return E_NOTFOUND ;
return m_Cache[objId].GetValue(edo) ;
}
// Wrong call
return hzerr(E_NOTFOUND, "RAM Primacy Cache NOT in Operation") ;
}
hzEcode hdbObjRepos::CommitEDO (const hzChain& edo, uint32_t objId, bool bLoad)
{
// Commit an EDO to the cache.
//
// If the supplied object Id is 0, the EDO is a new object so the commit is an INSERT - in which case the object will be placed at the end, and the id issued in respect of it,
// will be the highest thusfar issues. If the supplied object id is non-zero, the commit is an UPDATE and it must be of an object that already exists and has not been deleted.
//
// In an UPDATE, the supplied EDO replaces the existing EDO. The supplied EDO may be shorter, longer, or the same size as the existing EDO. If the same size, the existing EDO
// is overwritten, otherwise the cache block is recreated. All data up to the existing EDO is copied to a new block, the supplied EDO is then added to the new block, then all
// data after the existing EDO is copied over - then the new block replaces the old.
//
// Arguments: 1) edo EDO to commit (supplied as hzChain)
// 2) objId The object id
//
// Returns: E_CORRUPPT If the supplied object id is non-zero but no cache block could be identified.
// E_NOTFOUND If the supplied object id addresses a deleted object
// E_OK Operation successful
_hzfunc("hdbObjRepos::CommitEDO") ;
hzEdo theEdo ; // The EDO instance
// Validate
if (!this) hzexit(E_CORRUPT, "No repository instance") ;
if (!m_pClass) hzexit(E_NOINIT, "Cache not initialized") ;
if (!objId)
hzexit(E_ARGUMENT, "Invalid object ID") ;
if (!(m_eMode & HDB_REPOS_CACHE))
return hzerr(E_CORRUPT, "RAM Primacy Cache NOT in Operation") ;
if (objId > m_nSeqId)
m_nSeqId = objId ;
/* Diags
hzChain err ;
chIter ei ;
err << "EDO is [ " ;
for (ei = edo ; !ei.eof() ; ei++)
{
err.Printf("%02x ", (uchar) *ei) ;
}
err << "]\n" ;
threadLog(err) ;
threadLog("Commiting EDO objId %d\n", objId) ;
*/
theEdo.SetValue(edo) ;
m_Cache.Insert(objId, theEdo) ;
memset(&theEdo, 0, sizeof(hzEdo)) ;
return E_OK ;
}
/*
** hdbObjRepos Functions
*/
hdbObjRepos::hdbObjRepos (hdbADP& adp)
{
m_pADP = &adp ;
m_pBR_Delta = 0 ;
m_pBR_Datum = 0 ;
m_nSeqId = m_nPopulation = 0 ;
m_DeltaId = 0 ;
m_bBinaries = false ;
m_eReposInit = HDB_CLASS_INIT_NONE ;
}
hdbObjRepos::~hdbObjRepos (void)
{
m_Cache.Clear() ;
//delete m_pCache ;
}
hzEcode hdbObjRepos::InitStart (const hdbClass* pNative, const hzString& name, const hzString& workdir, hdbReposMode eMode)
{
// Begin repository initialization sequence. This function sets the repository native class, names the repository and the working directory (location of data files). Once this
// function has completed, it may be followed by calls to InitMbrIndex() to add member-wise indexes to the repository. Lastly InitDone() is called to complete the process.
//
// Arguments: 1) pNative The data class
// 2) name The repository name
// 3) workdir The operaional directory
// 4) bCache Use RAM Primacy
//
// Returns: E_ARGUMENT If no data class is supplied
// E_NOINIT If no class members have been defined
// E_INITDUP If this is a repeat call
// E_DUPLICATE If the cache already exists
// E_OK If the operation was successful
_hzfunc("hdbObjRepos::InitStart") ;
//const hdbMember* pMbr ; // Named class member
//uint32_t nIndex ; // Member iterator
hzEcode rc ; // Return code
if (!this)
hzexit(E_CORRUPT, "No hdbObjRepos instance") ;
// Check init state and state of supplied class
_hdb_ck_initstate(name, m_eReposInit, HDB_CLASS_INIT_NONE) ;
// Check data class has been supplied and is initialized
if (!pNative)
return hzerr(E_ARGUMENT, "No data class supplied") ;
if (!pNative->IsInit())
return hzerr(E_NOINIT, "Supplied class (%s) is not initialized", pNative->txtName()) ;
if (pNative->HasBinaries())
{
if (eMode == HDB_REPOS_CACHE)
{
hzwarn(E_TYPE, "Ignoring CACHE mode, using DUAL") ;
eMode = HDB_REPOS_DUAL ;
}
}
// Ensure Repository Name is Unique
if (!name)
return hzerr(E_ARGUMENT, "No name supplied") ;
if (m_pADP->GetObjRepos(name))
return hzerr(E_DUPLICATE, "Repository %s already exists", *name) ;
// Ensure working directory is given and operational
if (!workdir)
return hzerr(E_ARGUMENT, "No working directory supplied for object cache %s", *name) ;
rc = AssertDir(*workdir, 0777) ;
if (rc != E_OK)
return hzerr(rc, "Cannot assert working directory %s for object cache %s\n", *workdir, *name) ;
// Proceed with initialization
m_pClass = pNative ;
m_Name = name ;
m_Workdir = workdir ;
if (eMode == HDB_REPOS_HARD || eMode == HDB_REPOS_DUAL)
{
// Set up the member data binary datum repostory
if (pNative->HasBinaries())
{
m_nameBR_Datum = m_Name + "_br_datum" ;
m_pBR_Datum = new hdbBinRepos(*m_pADP) ;
rc = m_pBR_Datum->Init(m_nameBR_Datum, m_Workdir) ;
if (rc != E_OK)
return hzerr(rc, "Failed to initialize binary data store: Repos %s, Datum BR (%s), workdir %s\n", *m_Name, *m_nameBR_Datum, *m_Workdir) ;
}
// Set up the default binary datum repostory
if (!m_pBR_Delta)
{
m_nameBR_Delta = m_Name + "_br_delta" ;
m_pBR_Delta = new hdbBinRepos(*m_pADP) ;
rc = m_pBR_Delta->Init(m_nameBR_Delta, m_Workdir) ;
if (rc != E_OK)
return hzerr(rc, "Failed to initialize binary data store %s (%s)\n", *m_nameBR_Delta, *m_Workdir) ;
}
}
// Set up RAM Primacy cache if applicable
if (eMode == HDB_REPOS_CACHE || eMode == HDB_REPOS_DUAL)
{
m_pathCD = m_Workdir ;
m_pathCD += "/" ;
m_pathCD += m_Name ;
m_pathCD += ".cache" ;
//m_pCache = new hzMapS<uint32_t,hzEdo> ;
}
// Set init state
m_eReposInit = HDB_REPOS_INIT_PROG ;
m_eMode = eMode ;
// Insert the repository
m_pADP->RegisterObjRepos(this) ;
return E_OK ;
}
hzEcode hdbObjRepos::InitMbrIndex (const hdbMember* pMbr, bool bUnique)
{
// Add an index based on the supplied member name. Find the member in the class and from the datatype, this will determine which sort of index
// should be set up.
//
// Arguments: 1) mbrName Name of member index shall apply to
// 2) bUnique Flag if value uniqness applies
//
// Returns: E_NOINIT If the cache initialization sequence has not been started
// E_ARGUMENT If the member name is not supplied
// E_SEQUENCE If the cache initialization has been completed by InitDone()
// E_NOTFOUND If the named member is not found in the cache class
// E_TYPE If the named member is of a type that cannot accept an index
// E_OK If the index is successfully created
_hzfunc("hdbObjRepos::InitMbrIndex") ;
hdbIndex* pIdx ; // The index to be added
hzString iname ; // Index name of the form repos::member
hzEcode rc = E_OK ; // Return code
// Check init state
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_INIT_PROG) ;
if (!m_pClass) return hzerr(E_NOINIT, "No cache class set up") ;
if (!pMbr) return hzerr(E_ARGUMENT, "No member supplied") ;
if (pMbr->Class() != m_pClass)
return hzerr(E_NOTFOUND, "No such member as %s in class %s\n", pMbr->txtName(), m_pClass->txtType()) ;
if (pMbr->Posn() < 0 || pMbr->Posn() >= m_pClass->MbrCount())
return hzerr(E_NOTFOUND, "Member %s has no defined position within the class\n", pMbr->txtName()) ;
// Member's datatype will determine the type of index
pIdx = 0 ;
switch (pMbr->Basetype())
{
case BASETYPE_EMADDR:
case BASETYPE_URL: pIdx = new hdbIndexUkey() ;
break ;
case BASETYPE_STRING:
case BASETYPE_IPADDR:
case BASETYPE_TIME:
case BASETYPE_SDATE:
case BASETYPE_XDATE:
case BASETYPE_DOUBLE:
case BASETYPE_INT64:
case BASETYPE_INT32:
case BASETYPE_UINT64:
case BASETYPE_UINT32:
case BASETYPE_UINT16:
case BASETYPE_UBYTE: pIdx = new hdbIndexUkey() ;
break ;
case BASETYPE_INT16:
case BASETYPE_BYTE:
case BASETYPE_ENUM: pIdx = new hdbIndexEnum() ;
break ;
case BASETYPE_TEXT:
case BASETYPE_TXTDOC: pIdx = new hdbIndexText() ;
break ;
case BASETYPE_CLASS:
case BASETYPE_BINARY: hzerr(E_TYPE, "Invalid member type. No Index allowed") ;
rc = E_TYPE ;
break ;
default:
break ;
}
m_mapIndex.Insert(pMbr->DeltaId(), pIdx) ;
//m_mapIndex.Insert(pMbr->Posn(), pIdx) ;
m_eReposInit = HDB_REPOS_INIT_PROG ;
return rc ;
}
hzEcode hdbObjRepos::InitMbrIndex (const hzString& mbrName, bool bUnique)
{
// Add an index based on the supplied member name. Find the member in the class and from the datatype, this will determine which sort of index
// should be set up.
//
// Arguments: 1) mbrName Name of member index shall apply to
// 2) bUnique Flag if value uniqness applies
//
// Returns: E_NOINIT If the cache initialization sequence has not been started
// E_ARGUMENT If the member name is not supplied
// E_SEQUENCE If the cache initialization has been completed by InitDone()
// E_NOTFOUND If the named member is not found in the cache class
// E_TYPE If the named member is of a type that cannot accept an index
// E_OK If the index is successfully created
_hzfunc("hdbObjRepos::InitMbrIndex") ;
const hdbMember* pMbr ; // Named class member
// Check init state
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_INIT_PROG) ;
if (!m_pClass) return hzerr(E_NOINIT, "No cache class set up") ;
if (!mbrName) return hzerr(E_ARGUMENT, "No member name supplied") ;
pMbr = m_pClass->GetMember(mbrName) ;
if (!pMbr)
return hzerr(E_NOTFOUND, "No such member as %s in class %s\n", *mbrName, m_pClass->txtType()) ;
return InitMbrIndex(pMbr, bUnique) ;
}
hzEcode hdbObjRepos::InitMbrRepos (const hzString& mbrName, const hzString& reposName)
{
// By default subclass data objects are embedded within host class data objects. This function directs the repository to hold subclass data objects in another repository. This
// is done on a per-member basis and only applies to member with of a CLASS data type.
//
// Arguments: 1) mbrName The member name. The named member must exist and be of a CLASS data type.
// 2) pRepos Pointer to the target repository
//
// Returns: E_NOTFOUND If the named member does not exist.
// E_TYPE If the named member is not of a BINARY data type
// E_OK If the operation was successful.
_hzfunc("hdbObjRepos::InitMbrRepos") ;
const hdbObjRepos* pRepos ; // External repository
const hdbMember* pMbr ; // Named class member
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_INIT_PROG) ;
pMbr = m_pClass->GetMember(mbrName) ;
if (!pMbr)
return hzerr(E_NOTFOUND, "No such member as %s in class %s\n", *mbrName, m_pClass->txtType()) ;
if (pMbr->Basetype() != BASETYPE_BINARY && pMbr->Basetype() != BASETYPE_TEXT && pMbr->Basetype() != BASETYPE_TXTDOC)
return hzerr(E_TYPE, "Member %s has non binary base type", *mbrName) ;
if (!reposName)
return hzerr(E_ARGUMENT, "No Binary Repository named") ;
// A pre-existing hdbObjRepos has been named so the member will use this.
pRepos = m_pADP->GetObjRepos(reposName) ;
if (!pRepos)
return hzerr(E_NOTFOUND, "Repository %s does not exist", *reposName) ;
m_mapRepos.Insert(pMbr->Posn(), pRepos) ;
return E_OK ;
}
hzEcode hdbObjRepos::InitDone (void)
{
// Complete repository initialization.
//
// Deal with files if the working directory has been supplied. If files bearing the repository name exists in the stated working directory, these are assumed to be data files
// and will be read in to populate the repository. Delta files begin with a header which must match the class definition. This is checked before loading the rest of the data.
// If a file of the cache's name does not exist in the working directory, it will be created and a header will be written.
//
// If a backup directory has been specified and a file of the cache's name exists in this directory, the header will be checked and assuming this is OK, the length of the file
// will aslo be checked (should match with that in the work directory)
//
// Arguments: None
//
// Returns: E_INITFAIL If Repository has no native data class, or the native data class has no members and/or no description
// E_WRITEFAIL If the data file cannot be opened in write mode
_hzfunc("hdbObjRepos::InitDone") ;
const hdbMember* pMbr ; // Member
ifstream is ; // For reading in working data file
ofstream os ; // For writing in working data file
FSTAT fs ; // File status
hzChain E ; // Existing class description header from data file
hzAtom atom ; // For setting member values
hdbIndex* pIdx ; // The index to be added
hdbIndexUkey* pIdxU ; // The index to be added
hzString strDesc ; // Temp string holding memeber data
uint32_t nLine ; // Line number for reporting file errors
uint32_t mbrNo ; // Member number
char* lineBuf ; // For getline
hzEcode rc ; // Return code
// Check initialization and if there are some members added
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_INIT_PROG) ;
if (!m_pClass)
return hzerr(E_INITFAIL, "Repository %s: No data class", *m_Name) ;
if (!m_pClass->MbrCount())
return hzerr(E_INITFAIL, "Data class %s: No members!", *m_Name) ;
// Create and compare the XML Class Description
if (m_pathCD)
{
// Cache and cache delta applies
if (lstat(*m_pathCD, &fs) < 0)
{
// Working data file does not exist or is empty. Create a new one from the class description as per C++ calls
os.open(*m_pathCD) ;
if (os.fail())
return hzerr(E_WRITEFAIL, "Data class %s Cannot open data file %s in write mode", *m_Name, *m_pathCD) ;
os << m_pClass->Desc() ;
if (os.fail())
{
os.close() ;
os.clear() ;
hzerr(rc, "Data class %s Cannot write class description to data file %s", *m_Name, *m_pathCD) ;
return rc ;
}
os.flush() ;
os.close() ;
os.clear() ;
}
// Working data file does exist and has content so read it in. Start with header
is.open(*m_pathCD) ;
if (is.fail())
return hzerr(E_OPENFAIL, "Class %s data file %s exists but cannot be read in", *m_Name, *m_pathCD) ;
lineBuf = new char[512] ;
for (nLine = 1 ;; nLine++)
{
is.getline(lineBuf, 500) ;
if (!lineBuf[0])
break ;
E << lineBuf ;
E.AddByte(CHAR_NL) ;
if (!strcmp(lineBuf, "</class>"))
break ;
}
is.close() ;
delete lineBuf ;
// Compare class description header from file to that of the class
strDesc = E ;
if (strDesc != m_pClass->Desc())
hzerr(E_FORMAT, "Format error in data file %s. Existing description \n[\n%s\n]\nNew\n[\n%s\n]\n", *m_pathCD, *strDesc, *m_pClass->Desc()) ;
}
// Initialize all allocated indexes
for (mbrNo = 0 ; mbrNo < m_pClass->MbrCount() ; mbrNo++)
{
pMbr = m_pClass->GetMember(mbrNo) ;
threadLog("Initializing member %s:%s\n", m_pClass->txtName(), pMbr->txtName()) ;
// Check if the member has an associated index
pIdx = m_mapIndex[pMbr->DeltaId()] ;
if (!pIdx)
continue ;
if (pIdx->Whatami() == HZINDEX_UKEY)
{
pIdxU = (hdbIndexUkey*) pIdx ;
rc = pIdxU->Init(this, pMbr->strName(), pMbr->Basetype()) ;
if (rc != E_OK)
return hzerr(rc, "Failed to initialize unique-key index %s (%s)\n", *m_Name, pMbr->txtName()) ;
}
}
// Init matrix
if (rc != E_OK)
threadLog("Failed to init repos %s\n", *m_Name) ;
else
{
threadLog("Complete init repos %s\n", *m_Name) ;
m_eReposInit = HDB_REPOS_INIT_DONE ;
}
return rc ;
}
void hdbObjRepos::DescRepos (hzChain& Z, uint32_t nIndent) const
{
// Export Repository Description
//
// Write out XML fragment describing the data object repository to the supplied chain. The anticipated use of this function is as part of an ADP export, so the supplied chain
// is not cleared.
//
// Arguments: 1) Z The chain to aggregate the class description to
// 2) nIndent The number of leading tabs to apply to each line
//
// Returns: None
_hzfunc("hdbObjRepos::DescRepos") ;
const hdbMember* pMbr ; // Data class member
hdbIndex* pIndex ; // Index
uint32_t nI ; // Indent
uint32_t nE ; // Entity count
uint16_t mbrNo ; // Member number
if (!m_pClass) hzerr(E_NOINIT, "No class") ;
if (!m_Name) hzerr(E_NOINIT, "No repos name") ;
if (!m_Workdir) hzerr(E_NOINIT, "No working directory") ;
for (nI = nIndent ; nI > 0 ; nI--)
Z.AddByte(CHAR_TAB) ;
Z.Printf("<repos id=\"%u\" name=\"%s\" workdir=\"%s\" class=\"%s\">\n", m_DeltaId, *m_Name, *m_Workdir, m_pClass->txtName()) ;
for (nE = 0 ; nE < m_mapIndex.Count() ; nE++)
{
mbrNo = m_mapIndex.GetKey(nE) ;
pIndex = m_mapIndex.GetObj(nE) ;
pMbr = m_pClass->GetMember(mbrNo) ;
if (!pMbr)
hzerr(E_CORRUPT, "No member %u\n", mbrNo) ;
if (!pIndex)
hzerr(E_CORRUPT, "No index for member %u\n", mbrNo) ;
for (nI = nIndent ; nI > 0 ; nI--)
Z.AddByte(CHAR_TAB) ;
Z.Printf("\t<index mbr=\"%s\" name=\"%s\"/>\n", pMbr->txtName(), pIndex->txtName()) ;
}
if (m_pBR_Delta)
{
for (nI = nIndent ; nI > 0 ; nI--)
Z.AddByte(CHAR_TAB) ;
Z.Printf("\t<binReposDelta name=\"%s\"/>\n", m_pBR_Delta->txtName()) ;
}
if (m_pBR_Datum)
{
for (nI = nIndent ; nI > 0 ; nI--)
Z.AddByte(CHAR_TAB) ;
Z.Printf("\t<binReposDatum name=\"%s\"/>\n", m_pBR_Datum->txtName()) ;
}
for (nI = nIndent ; nI > 0 ; nI--)
Z.AddByte(CHAR_TAB) ;
Z << "</repos>\n" ;
}
const hdbObjRepos* hdbObjRepos::ObjRepos (const hdbMember* pMbr) const
{
// Locate the external repository associated with a subclass member. This will either be the repository itself or that specified during initialization InitMbrRepos()
//
// Argument: pMbr Data class member
//
// Returns: Pointer to the external repository if found, 0 otherwise
_hzfunc("hdbObjRepos::ObjRepos") ;
const hdbObjRepos* pR ; // Binary repos
if (!m_pClass) { hzerr(E_NOINIT, "No data class") ; return 0 ; }
if (!pMbr) { hzerr(E_NOINIT, "No member supplied") ; return 0 ; }
if (pMbr->Class() != m_pClass)
{ hzerr(E_CORRUPT, "Member %s not in class %s", pMbr->txtName(), m_pClass->txtName()) ; return 0 ; }
if (pMbr->Basetype() != BASETYPE_CLASS)
{ hzerr(E_TYPE, "Member %s is not BINARY or TXTDOC", pMbr->txtName()) ; return 0 ; }
pR = m_mapRepos[pMbr->Posn()] ;
if (!pR)
return this ;
return pR ;
}
/*
** Open Repository. Support functions
*/
hzEcode hdbObjRepos::_loadCache (void)
{
// Load whole object deltas
_hzfunc("hdbObjRepos::_loadCache") ;
hdbObject currObj ; // Current object
hzChain mlText ; // For gathering text content accross several lines
hzChain edo ; // For EDO commital
uint32_t objId ; // Object id
hzEcode rc = E_OK ; // Return code
// Check init state
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_INIT_DONE) ;
// Init data object container
rc = currObj.Init(m_pClass) ;
if (rc != E_OK)
return hzerr(E_NOINIT, "Could not init object") ;
// Check if RAM Primacy is deployed
if (!(m_eMode & HDB_REPOS_CACHE))
return E_OK ;
if (!m_pBR_Delta)
return hzerr(E_NOINIT, "No binary repos for whole object deltas") ;
threadLog("CALLED on repos %s, binary repos %s\n", *m_Name, *m_nameBR_Delta) ;
for (objId = 1 ; objId < m_pBR_Delta->Count() && rc == E_OK ; objId++)
{
rc = m_pBR_Delta->Fetch(mlText, objId) ;
if (rc != E_OK)
{
threadLog("Could not fetch object %u, err=%s\n", objId, Err2Txt(rc)) ;
continue ;
}
rc = currObj.ImportDelta(mlText) ;
if (rc != E_OK)
hzerr(rc, "Could not load full delta") ;
else
{
rc = currObj.Integrity() ;
if (rc != E_OK)
hzerr(rc, "Case 1 Cannot Insert Object (Integrity fails)") ;
else
{
rc = currObj.ExportEDO(edo) ;
if (rc != E_OK)
hzerr(rc, "Could not export EDO") ;
else
{
//rc = m_pMain->CommitEDO(edo, currObj.GetObjId(), true) ;
rc = CommitEDO(edo, currObj.GetObjId(), true) ;
if (rc != E_OK)
hzerr(rc, "Could not commit EDO") ;
else
{
rc = _updateIdx(currObj) ;
if (rc != E_OK)
hzerr(rc, "Could not update indexes") ;
}
}
}
}
currObj.Clear() ;
mlText.Clear() ;
}
m_nPopulation = m_Cache.Count() ;
threadLog("Population of objects in cache %s is %d. Status is %s\n", txtName(), m_nPopulation, Err2Txt(rc)) ;
return rc ;
}
hzEcode hdbObjRepos::_loadDeltas (void)
{
// Load data from delta files.
//
// Loads both whole object and member deltas. Note that member deltas are ignored unless the host object exists within the repository.
_hzfunc("hdbObjRepos::_loadDeltas") ;
ifstream is ; // For reading in working data file
hdbObject currObj ; // Current object
hzChain mlText ; // For gathering text content accross several lines
hzChain Y ; // For writing class description header from data file
hzChain edo ; // For EDO commital
hzAtom atom ; // For setting member values
_atomval av ; // Atom value
const hdbMember* pMbr ; // Member
hdbIndex* pIdx ; // Index pointer
hdbIndexUkey* pIdxU ; // Index pointer
hdbIndexEnum* pIdxE ; // Index pointer
char* lineBuf ; // For getline
char* j ; // For buffer iteration
hzString tmpStr ; // Temp string holding memeber data
hzDomain dom ; // Temp domain
hzEmaddr ema ; // Temp email addr
hzUrl url ; // Temp URL
uint32_t nLine ; // Line number for reporting file errors
uint32_t reposId ; // Repos id
uint32_t classId ; // Class id
uint32_t objId ; // Object id
uint32_t lastObjId ; // Last object id
uint32_t novals ; // Number of member values
uint32_t mbrNo ; // Member number
hzEcode rc = E_OK ; // Return code
// Check init state
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_INIT_DONE) ;
// Init data object container
rc = currObj.Init(m_pClass) ;
if (rc != E_OK)
return hzerr(E_NOINIT, "Could not init object") ;
// Check if RAM Primacy is deployed
if (!(m_eMode & HDB_REPOS_CACHE))
return E_OK ;
threadLog("CALLED on repos %s, delta file %s\n", *m_Name, *m_pathCD) ;
// Allocate line buffer
lineBuf = new char[2048] ;
reposId = classId = objId = lastObjId = 0 ;
// Bypass header
is.open(*m_pathCD) ;
for (nLine = 1 ;; nLine++)
{
is.getline(lineBuf, 2040) ;
if (!is.gcount())
break ;
if (is.gcount() == 2040)
{ rc = hzerr(E_RANGE, "Line %u exceeds buffer", nLine) ; break ; }
lineBuf[is.gcount()] = 0 ;
Y << lineBuf ;
Y.AddByte(CHAR_NL) ;
if (!strcmp(lineBuf, "</class>"))
break ;
}
// Now read in the rest of the data (in delta notation)
threadLog("LOAD on repos %s, workpath %s\n", *m_Name, *m_pathCD) ;
lastObjId = 0 ;
novals = 0 ;
for (; rc == E_OK ; nLine++)
{
// if (!(nLine % 10))
// threadLog("Processing line %u\n", nLine) ;
is.getline(lineBuf, 2040) ;
if (!is.gcount())
{
threadLog("Terminating on line %u\n", nLine) ;
break ;
}
if (is.gcount() >= 2039)
hzexit(E_RANGE, "Line %u exceeds buffer", nLine) ;
lineBuf[is.gcount()] = 0 ;
if (lineBuf[0] == CHAR_AT)
{
if (!memcmp(lineBuf, "@obj", 4))
{
// Whole form delta
mlText << lineBuf ;
mlText.AddByte(CHAR_NL) ;
for (;; nLine++)
{
is.getline(lineBuf, 2040) ;
if (!is.gcount())
break ;
lineBuf[is.gcount()] = 0 ;
mlText << lineBuf ;
mlText.AddByte(CHAR_NL) ;
if (lineBuf[0] == '}' && lineBuf[1] == 0)
break ;
}
rc = currObj.ImportDelta(mlText) ;
if (rc != E_OK)
hzerr(rc, "Could not load full delta") ;
else
{
threadLog("Loaded object %u\n", currObj.GetObjId()) ;
rc = currObj.Integrity() ;
if (rc != E_OK)
hzerr(rc, "Cannot Insert Object (Integrity fails)") ;
else
{
rc = currObj.ExportEDO(edo) ;
if (rc != E_OK)
hzerr(rc, "Could not export EDO") ;
else
{
//rc = m_pMain->CommitEDO(edo, currObj.GetObjId(), true) ;
rc = CommitEDO(edo, currObj.GetObjId(), true) ;
if (rc != E_OK)
hzerr(rc, "Could not commit EDO") ;
else
{
rc = _updateIdx(currObj) ;
if (rc != E_OK)
hzerr(rc, "Could not update indexes") ;
}
}
}
}
currObj.Clear() ;
mlText.Clear() ;
novals = 0 ;
continue ;
}
// Handle partial deltas
j = lineBuf + 1 ;
// Gather repos id
if (*j == 'r')
{
for (j++, reposId = 0 ; IsDigit(*j) ; j++)
{ reposId *= 10 ; reposId += *j - '0' ; }
if (*j != CHAR_PERIOD)
{ rc = hzerr(E_FORMAT, "File %s Line %d: Period expected after repos ID", *m_pathCD, nLine) ; break ; }
j++ ;
}
// Gather class id
if (*j == 'c')
{
for (j++, classId = 0 ; IsDigit(*j) ; j++)
{ classId *= 10 ; classId += *j - '0' ; }
if (*j != CHAR_PERIOD)
{ rc = hzerr(E_FORMAT, "File %s Line %d: Period expected after class ID", *m_pathCD, nLine) ; break ; }
j++ ;
}
// Gather and act on the object id in the delta
if (*j == 'o')
{
for (j++, objId = 0 ; IsDigit(*j) ; j++)
{ objId *= 10 ; objId += *j - '0' ; }
if (!lastObjId)
lastObjId = objId ;
if (*j != CHAR_PERIOD)
{ rc = hzerr(E_FORMAT, "File %s Line %d: Period and member ID expected after object ID", *m_pathCD, nLine) ; break ; }
j++ ;
if (lastObjId && objId != lastObjId)
{
// Now dealing with another object, so write out the EDO for the existing object, and start a new one
// currObj.ExportDelta(J) ;
rc = currObj.Integrity() ;
if (rc != E_OK)
{
hzerr(rc, "Case 2 Cannot Insert Object - Integrity fails") ;
break ;
}
currObj.SetObjId(lastObjId) ;
rc = currObj.ExportEDO(edo) ;
if (rc != E_OK)
hzerr(rc, "Could not export EDO") ;
else
{
//rc = m_pMain->CommitEDO(edo, lastObjId, true) ;
rc = CommitEDO(edo, lastObjId, true) ;
if (rc != E_OK)
hzerr(rc, "Could not commit EDO") ;
}
if (rc != E_OK)
break ;
currObj.Clear() ;
lastObjId = objId ;
novals = 0 ;
}
}
// Gather and act on the member ID in the delta
if (*j == 'm')
{
for (j++, mbrNo = 0 ; IsDigit(*j) ; j++)
{ mbrNo *= 10 ; mbrNo += *j - '0' ; }
pMbr = m_pClass->GetMember(mbrNo) ;
if (!pMbr)
{
rc = hzerr(E_FORMAT, "File %s Line %d: Member %d does not exist in class %s\n", *m_pathCD, nLine, mbrNo, m_pClass->txtType()) ;
break ;
}
}
// Get member values
if (*j == CHAR_EQUAL)
{
// Read in value (multi-line if applicable)
tmpStr.Clear() ;
j++ ;
if (*j == CHAR_SQOPEN && j[1] == 0)
{
// Multi-line value. Loop with getline until a line is found with a ']' by itself.
mlText.Clear() ;
for (;;)
{
is.getline(lineBuf, 2040) ;
if (!is.gcount())
break ;
lineBuf[is.gcount()] = 0 ;
if (lineBuf[0] == CHAR_SQCLOSE && lineBuf[1] == 0)
break ;
mlText.Append(lineBuf, is.gcount()) ;
}
if (!mlText.Size())
continue ;
tmpStr = mlText ;
}
else
{
tmpStr = j ;
}
novals++ ;
if (!tmpStr)
continue ;
// Set the member value in the object
rc = atom.SetValue(pMbr->Basetype(), tmpStr) ;
if (rc != E_OK)
{
threadLog("ERROR: Object %d member %s value %s. Err=%s\n", objId, pMbr->txtName(), *tmpStr, Err2Txt(rc)) ;
break ;
}
// if (pMbr->Basetype() == BASETYPE_XDATE)
// threadLog("DELTA: Object %d member %s=%s (%s)\n", objId, pMbr->txtName(), atom.Show(), j) ;
rc = currObj.SetValue(pMbr, atom) ;
if (rc != E_OK)
{
threadLog("ERROR - Could not set member %s\n", pMbr->txtName()) ;
break ;
}
pIdx = m_mapIndex[pMbr->DeltaId()] ;
if (pIdx)
{
if (pIdx->Whatami() == HZINDEX_UKEY)
{
pIdxU = (hdbIndexUkey*) pIdx ;
rc = pIdxU->Insert(atom, objId) ;
if (rc != E_OK)
{
threadLog("Mbr %s Failed on UKEY index insert. Atom %s Err=%s\n", pMbr->txtName(), atom.Show(), Err2Txt(rc)) ;
break ;
}
}
if (pIdx->Whatami() == HZINDEX_ENUM)
{
pIdxE = (hdbIndexEnum*) pIdx ;
rc = pIdxE->Insert(objId, atom) ;
if (rc != E_OK)
{
threadLog("Mbr %s Failed on ENUM index insert. Atom %s\n", pMbr->txtName(), atom.Show()) ;
}
}
}
atom.Clear() ;
}
continue ;
}
hzerr(E_SYNTAX, "File %s Line %d [%s]: Unknown instruction. Only @obj:, @del:, and @obj: allowed\n", *m_pathCD, nLine, lineBuf) ;
rc = E_SYNTAX ;
break ;
}
is.close() ;
threadLog("Delta file closed\n") ;
if (novals && rc == E_OK)
{
// Commit the last EDO
rc = currObj.Integrity() ;
if (rc != E_OK)
hzerr(rc, "Case 3 Cannot Insert Object - Integrity fails") ;
else
{
currObj.SetObjId(objId) ;
rc = currObj.ExportEDO(edo) ;
if (rc != E_OK)
hzerr(rc, "Could not export EDO") ;
else
{
//rc = m_pMain->CommitEDO(edo, objId, true) ;
rc = CommitEDO(edo, objId, true) ;
if (rc != E_OK)
hzerr(rc, "Could not commit EDO") ;
}
}
}
m_nPopulation = m_Cache.Count() ;
threadLog("Population of objects in cache %s is %d. Delta file %s. Status is %s\n", txtName(), m_nPopulation, *m_pathCD, Err2Txt(rc)) ;
delete [] lineBuf ;
return rc ;
}
hzEcode hdbObjRepos::Open (void)
{
// Open the data object repository, i.e. ready it for data operations. Depending on repository configuration, this action will do the following:-
//
// Open the binary repository for Whole Object Deltas. This is done in all cases.
// This action restores RAM Primacy components to the last known data state.
//
// Now deal with files if the working directory has been supplied. If a file of the cache's name exists in the working directory, this is assumed
// to be the data file and will be read in to populate the cache. The file must begin with a header which must match the class definition so this
// is checked before loading the remaining data. If a file of the cache's name does not exist in the working directory, it will be created and a
// header will be written.
//
// If a backup directory has been specified and a file of the cache's name exists in this directory, the header will be checked and assuming this
// is OK, the length of the file will aslo be checked (should match with that in the work directory)
//
// Arguments: None
_hzfunc("hdbObjRepos::Open") ;
hzEcode rc ; // Return code
if (!this)
Fatal("No instance") ;
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_INIT_DONE) ;
//if (!m_Workdir)
// return hzerr(E_NOINIT, "No workdir") ;
if (m_pBR_Datum)
{
// Only if native class has BINARY/TXTDOC members
rc = m_pBR_Datum->Open() ;
if (rc != E_OK)
return hzerr(E_NOINIT, "Could not open mbr datum binary repos") ;
}
if (m_pBR_Delta)
{
rc = m_pBR_Delta->Open() ;
if (rc != E_OK)
return hzerr(E_NOINIT, "Could not open delta binary repos") ;
rc = _loadCache() ;
threadLog("Whole Object Deltas loaded - Status %s\n", Err2Txt(rc)) ;
}
rc = _loadDeltas() ;
threadLog("Deltas loaded\n") ;
// Now open file for writing
if (rc == E_OK)
{
m_osDelta.open(*m_pathCD, ios::app) ;
if (m_osDelta.fail())
return hzerr(E_WRITEFAIL, "Class %s Cannot open data file %s in write mode", *m_Name, *m_pathCD) ;
m_eReposInit = HDB_REPOS_OPEN ;
}
threadLog("Population of objects in repos %s is %d. Delta file %s. Status %s\n", txtName(), Count(), *m_pathCD, Err2Txt(rc)) ;
//threadLog("Repos %s: Pop %d\n", *m_Name, Count()) ;
return rc ;
}
hzEcode hdbObjRepos::Clear (void)
{
// Destroys all data in the Ram table and re-initializes everything.
//
// Arguments: None
_hzfunc("hdbObjRepos::Clear") ;
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_OPEN) ;
m_Cache.Clear() ;
return E_OK ;
}
hzEcode hdbObjRepos::_updateIdx (const hdbObject& obj)
{
// Supprt function. Updates the repository indexes on behalf of Insert() and _loadDeltas()
_hzfunc("hdbObjRepos::_updateIdx") ;
const hdbMember* pMbr ; // Member pointer
hdbIndex* pIdx ; // Index pointer
hdbIndexUkey* pIdxU ; // Index pointer
hdbIndexEnum* pIdxE ; // Index pointer
hzAtom atom ; // Atom from member of populating object
uint32_t objId ; // Object id
uint32_t mbrNo ; // Member number
hzEcode rc = E_OK ; // Return value
hzEcode ic = E_OK ; // Return value
objId = obj.GetObjId() ;
if (!objId)
return hzerr(E_NOTFOUND, "No object id") ;
for (mbrNo = 0 ; mbrNo < m_pClass->MbrCount() ; mbrNo++)
{
// Get member and value
pMbr = m_pClass->GetMember(mbrNo) ;
obj.GetValue(atom, pMbr) ;
if (atom.IsNull())
continue ;
// Check if member has an index
pIdx = m_mapIndex[pMbr->DeltaId()] ;
if (!pIdx)
continue ;
// Update index
if (pIdx->Whatami() == HZINDEX_UKEY)
{
pIdxU = (hdbIndexUkey*) pIdx ;
rc = pIdxU->Insert(atom, objId) ;
}
if (pIdx->Whatami() == HZINDEX_ENUM)
{
pIdxE = (hdbIndexEnum*) pIdx ;
rc = pIdxE->Insert(objId, atom) ;
}
if (rc != E_OK)
{
threadLog("UKEY idx insert of %s returned err=%s\n", atom.Show(), Err2Txt(ic)) ;
break ;
}
}
return rc ;
}
hzEcode hdbObjRepos::Insert (uint32_t& objId, const hdbObject& theObj)
{
// The INSERT operation adds a new data object to a repository and creates a new object id in respect of it. If any indexes apply, these are updated accordingly.
//
// Note that in INSERT operations, the supplied object is expected to be an entirely new, and whole, object of the repository native data class. All subclass objects will have
// been added to the native class data object, prior to this function call. Because the supplied data object is entirely new, a whole object delta is generated.
//
// In all cases the new data object is committed to a binary datum repository as a whole object delta. If RAM Primacy applies, the new data object is also written as an EDO to
// repository cache. The whole object delta is produced by calling ExportDelta() on the supplied object. The EDO is produced by calling ExportEDO() on the supplied object.
//
// If the object has populated BINARY or TXTDOC members, their values are committed to the applicable binary datum repository before the delta and EDO exports. This is so that
// the values are assigned datum ids - which are the only representation the values will have in the delta and in the EDO.
//
// Arguments: 1) objId The object id that will be assigned by this operation
// 2) pObj Pointer to object to be inserted
//
// Returns: E_NOINIT If either the cache or the supplied object is not initialized
// E_ARGUMENT If the object is not supplied
// E_TYPE If the object is not of the same data class as the cache
// E_DUPLICATE If the object cannot be inserted because one or more members violate uniqueness
// E_WRITEFAIL If the object deltas cannot be written
// E_OK If the insert operation was successful
_hzfunc("hdbObjRepos::Insert") ;
const hdbMember* pMbr ; // Member pointer
hzChain Z ; // For building output to data file
hzChain theChain ; // For extracting atom data stored as chains
hzChain edo ; // For EDO export and commital
hzChain delta ; // For delta export
hzAtom atom ; // Atom from member of populating object
_atomval av ; // Atomic value
hdbIndex* pIdx ; // Index pointer
hdbIndexUkey* pIdxU ; // Index pointer
//hdbIndexEnum* pIdxE ; // Index pointer
hzString strVal ; // Temp string
uint32_t mbrNo ; // Member number
hzEcode rc = E_OK ; // Return value
// Check Init state
if (!this)
Fatal("No Instance\n") ;
objId = 0 ;
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_OPEN) ;
if (!theObj.Class())
return hzerr(E_NOINIT, "Supplied object is not initialized") ;
// Check object class is the same as the host class or contains a sub-class that is the same as the sub-class
if (theObj.Class() != m_pClass)
{
if (!m_pADP->IsSubClass(m_pClass, theObj.Class()))
{
hzerr(E_TYPE, "Supplied object class %s not compatible with this cache class %s", theObj.Classname(), m_pClass->txtType()) ;
return E_TYPE ;
}
}
// For an INSERT the object id (in the supplied object), must be 0
if (theObj.GetObjId() != 0)
return hzerr(E_RANGE, "Supplied object has an object id of %u", theObj.GetObjId()) ;
/*
** Check members to see if any of them require unique values. Any that do will have an unique key index. If the member value already exists, abort the INSERT.
*/
for (mbrNo = 0 ; mbrNo < m_pClass->MbrCount() ; mbrNo++)
{
pMbr = m_pClass->GetMember(mbrNo) ;
pIdx = m_mapIndex[pMbr->DeltaId()] ;
if (!pIdx)
continue ;
if (pIdx->Whatami() == HZINDEX_UKEY)
{
pIdxU = (hdbIndexUkey*) pIdx ;
rc = theObj.GetValue(atom, pMbr) ;
if (rc == E_NOTFOUND)
continue ;
rc = pIdxU->Select(objId, atom) ;
if (rc != E_OK)
return hzerr(rc, "%s: Could not select on index for member %s", *m_Name, pMbr->txtName()) ;
if (objId)
return hzerr(E_DUPLICATE, "%s: The value for member %s, already exists in object %u", *m_Name, pMbr->txtName(), objId) ;
}
}
/*
** The new object does not conflict with an existing one and so insertation can proceed. The objId is assigned as the number of existing objects + 1
*/
// Issue the object id
objId = ++m_nSeqId ;
theObj.SetObjId(objId) ;
// Commit BINARY/TXTDOC values
if (m_pBR_Datum)
{
rc = theObj.CommitBinaries(m_pBR_Datum) ;
if (rc != E_OK)
return hzerr(rc, "Could not commit binaries") ;
}
// Export whole object delta
rc = theObj.ExportDelta(delta) ;
if (rc != E_OK)
return hzerr(rc, "Could not export whole object delta") ;
// Update the repository delta file
m_osDelta << delta ;
m_osDelta.flush() ;
// Originate delta to DS
m_pADP->DeltaOriginateObj(this, delta) ;
// Commit whole object delta to binary datum repository
if (m_eMode & HDB_REPOS_CACHE)
{
// Commit EDO
rc = theObj.ExportEDO(edo) ;
if (rc != E_OK)
return hzerr(rc, "Could not export EDO") ;
// Commit EDO to cache
rc = CommitEDO(edo, objId, false) ;
if (rc != E_OK)
return hzerr(rc, "Could not commit EDO") ;
}
// Update indexes
rc = _updateIdx(theObj) ;
if (rc != E_OK)
return hzerr(rc, "Could not update indexes") ;
return rc ;
}
hzEcode hdbObjRepos::Delete (uint32_t objId)
{
// Delete the object with the supplied object id.
//
// This results in the following action:-
// A delete marker is added to the delta file
// If the repository has a cache, the object EDO will be removed from it.
//
// The whole object delta is NOT removed from the internal binary repository, m_pBR_Delta. This will wait until rationalization.
//
// Argument: objId The target object id
//
// Returns: E_NOINIT If the repository is not initialized
// E_NOTFOUND If the object indicated by the object id, does not exist
// E_OK Operation successful
_hzfunc("hdbObjRepos::Delete") ;
char dBuf[32] ;
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_OPEN) ;
// Check object exists
if (objId > m_Cache.Count())
return hzerr(E_NOTFOUND, "Beyond Range %u\n", m_Cache.Count()) ;
if (m_eMode & HDB_REPOS_CACHE)
{
if (!m_Cache.Exists(objId))
return E_NOTFOUND ;
// Update the repository delta file
sprintf(dBuf, "@del%u", objId) ;
}
m_osDelta << dBuf ;
m_osDelta.flush() ;
//if (m_bDeletes)
//return E_DELETE ;
// pOld = (char*) mx->m_Objects[objId] ;
// mx->m_Objects.Delete(objId) ;
return E_OK ;
}
hzEcode hdbObjRepos::Update (hdbObject& obj, uint32_t objId)
{
// Overwrite the object found at the supplied address, with the supplied object and update any affected indexes accordingly
//
// Arguments: 1) obj The new version of the data object
// 2) objId The object id of the original version
//
// DEPRECATED
_hzfunc("hdbObjRepos::Update") ;
const hdbMember* pMbr ; // Data member
hzAtom atom ; // For setting member values
uint32_t mbrNo ; // Member iterator
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_OPEN) ;
if (objId >= m_Cache.Count())
return E_RANGE ;
// Now go thru object's class members filling in the allocated fixed length space
for (mbrNo = 0 ; mbrNo < m_pClass->MbrCount() ; mbrNo++)
{
pMbr = m_pClass->GetMember(mbrNo) ;
obj.GetValue(atom, pMbr, 0) ;
m_pADP->DeltaOriginateMbr(this, pMbr, atom, DELTA_ORIG_MBRVAL_EQ) ;
}
return E_OK ;
}
hzEcode hdbObjRepos::Fetch (hdbObject& obj, uint32_t objId) const
{
// Fetch populates the supplied object recipticle (hdbObject instance) with the object identified by the supplied object id.
//
// The supplied recepticle is cleared by this function. If the supplied object id is invalid, the recepticle is left blank
//
// Arguments: 1) obj The object
// 2) objId The object id to fetch
//
// Returns: E_NOTFOUND The requested object does not exist or has been deleted
// E_OK Operation success
_hzfunc("hdbObjRepos::Fetch") ;
hzChain edo ; // EDO fetched from repos and passed to object
hzChain don ; // Object in HDON format (whole object delta)
hzEcode rc = E_OK ; // Return code
// Check init state and that supplied object is of the same class as the cache
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_OPEN) ;
// Wrong data class?
if (obj.Class() != m_pClass)
hzexit(E_TYPE, "Repository %s is of class %s. Supplied object is of class %s", *m_Name, Classname(), obj.Classname()) ;
// Clear target object
obj.Clear() ;
// Fetch data object
if (m_eMode & HDB_REPOS_CACHE)
{
// Object id out of range?
if (objId > m_nSeqId)
return hzerr(E_NOTFOUND, "Beyond Range %u\n", m_nSeqId) ;
// Fetch EDO
//rc = m_pMain->FetchEDO(edo, objId) ;
rc = FetchEDO(edo, objId) ;
if (rc != E_OK)
{
threadLog("FetchEDO failed\n") ;
return rc ;
}
rc = obj.ImportEDO(edo) ;
if (rc != E_OK)
{
threadLog("ImportEDO failed\n") ;
return rc ;
}
rc = obj.Integrity() ;
if (rc != E_OK)
{
hzChain err ; // Error
chIter ei ; // EDO iterator
err << "EDO is [ " ;
for (ei = edo ; !ei.eof() ; ei++)
{
err.Printf("%02x ", (uchar) *ei) ;
}
err << "]\n" ;
threadLog(err) ;
threadLog("Integrity check failed err=%s\n", Err2Txt(rc)) ;
}
return rc ;
}
// Fetch data object as whole object delta
// No cache so check dead index and if not dead, fetch message from m_pBR_Delta
rc = m_pBR_Delta->Fetch(don, objId) ;
if (rc == E_OK)
rc = obj.ImportDelta(don) ;
return rc ;
}
hzEcode hdbObjRepos::Exists (uint32_t& objId, const hdbMember* pMbr, const hzAtom& value)
{
// Identify a single object by matching on the supplied member name and value.
//
// Arguments: 1) objId The object id identified by the operation (0 if not object found)
// 2) pMbr The member to be tested
// 3) value The value the member must have to identify the object
//
// Returns@ E_NOINIT If the object cache is not initialized
// E_CORRUPT If the member does not exist within the cache data class
// E_NODATA If the member is not indexed
// E_OK If no errors occured
_hzfunc("hdbObjRepos::Exists()") ;
hdbIndex* pIdx ; // Index pointer
hdbIndexUkey* pIdxU ; // Index pointer
hzEcode rc ; // Return code
// No instance?
if (!this)
hzexit(E_CORRUPT, "No instance") ;
threadLog("Called on member %s value %s\n", pMbr->txtName(), value.Show()) ;
// Check init state
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_OPEN) ;
if (!m_pClass) return hzerr(E_NOINIT, "No cache class set up") ;
if (!pMbr) return hzerr(E_ARGUMENT, "No member supplied") ;
objId = 0 ;
// Check for index
pIdx = m_mapIndex[pMbr->DeltaId()] ;
if (!pIdx)
return hzerr(E_NODATA, "No index on member %s in class %s", pMbr->txtName(), m_pClass->txtName()) ;
//atom.SetValue(pMbr->Basetype(), value) ;
if (pIdx->Whatami() != HZINDEX_UKEY)
return hzerr(E_TYPE, "Wrong index on member %s in class %s", pMbr->txtName(), m_pClass->txtName()) ;
pIdxU = (hdbIndexUkey*) pIdx ;
rc = pIdxU->Select(objId, value) ;
if (rc != E_OK)
return hzerr(rc, "ERROR: Index selection on member %s in class %s", pMbr->txtName(), m_pClass->txtName()) ;
return rc ;
}
hzEcode hdbObjRepos::GetBinary (hzChain& Z, const hdbMember* pMbr, uint32_t objId) const
{
// Populate the supplied chain with the binary object held by the named member in the identified data object.
//
// This assumes the named data class member holds binary objects and that the supplied data object id is valid. As object repositories do not directly hold
// binary values, the member value will be the address of the binary object residing in a separate binary repository. The address is then used in a Fetch()
// on the binary repository.
//
// Arguments: 1) atom The atom to be set to the member's value.
// 2) member The name of member
// 3) objId The id of the data object
//
// Returns: E_RANGE If the object id is invalid
// E_TYPE If the member is not BASETYPE_BINARY or BASETYPE_TXTDOC
// E_OK If the operation was successful
_hzfunc("hdbObjRepos::GetBinary") ;
// Exit if cache not open
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_OPEN) ;
if (!pMbr)
return hzerr(E_ARGUMENT, "No member supplied") ;
if (pMbr->Class() != m_pClass)
return hzerr(E_CORRUPT, "No class member of %s in class %s", pMbr->txtName(), m_pClass->txtType()) ;
// And check member is a document or a binary
if (pMbr->Basetype() != BASETYPE_BINARY && pMbr->Basetype() != BASETYPE_TXTDOC)
return E_TYPE ;
// Grab the binary value
return m_pBR_Datum->Fetch(Z, objId) ;
}
hzEcode hdbObjRepos::Select (hzIntset& result, const char* cpSQL) const
{
// Select data objects according to the supplied search criteria (arg 2), and populate the supplied hzIntset with the object ids.
//
// The parse process is rudimentary, so SQL-esce rather than strict SQL. Each search criteria term will produce an idset result. OR and AND operations are applied where there
// are multiple terms. If these operations are not stated, AND is assumed.
//
// Arguments: 1) result The bitmap of object ids identified by the select operation
// 2) cpSql The SQL-esce search criteria
_hzfunc("hdbObjRepos::Select_a") ;
hzEcode rc = E_OK ;
// Check init state
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_OPEN) ;
// Clear result idset
result.Clear() ;
// STUB
// Do the Select
// if (!pExp->Parse(cpSQL))
// hzerr(E_PARSE) ;
return rc ;
}