//
// 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 "hzChars.h"
#include "hzChain.h"
#include "hzDate.h"
#include "hzTextproc.h"
#include "hzCodec.h"
#include "hzDocument.h"
#include "hzDirectory.h"
#include "hzDatabase.h"
#include "hzDelta.h"
#include "hzProcess.h"
using namespace std ;
/*
** Variables
*/
extern hzDeltaClient* _hzGlobal_DeltaClient ; // Total of current delta clients
/*
** Prototypes
*/
uint32_t Datatype2Size (hdbBasetype eType) ;
const char* _hds_showinitstate (hdbIniStat nState) ;
void _hdb_ck_initstate (const hzString& objName, hdbIniStat eActual, hdbIniStat eExpect) ;
/*
** Chain block bucket regime
*/
/*
** hdbObjRepos::_cache_chain Functions
*/
void hdbObjRepos::_cache::Clear (void)
{
// Clear cache
//
// Arguments: None
// Returns: None
m_Chain.Clear() ;
}
hdbObjRepos::_c_blk* hdbObjRepos::_cache::_findBlock (uint32_t objId)
{
// Find the address of the chain block for the given object id. This is done by a binary chop on the chain of blocks.
//
// Argument: objId The object id
//
// Returns: Pointer to the block or NULL if the object does not exist
_hzfunc("_hz_xchain::_findBlock") ;
_c_blk* pBloc ; // Current chain block
uint32_t nMax ; // Number of blocks -1
uint32_t nDiv ; // Binary chop divider
uint32_t nPos ; // Starting position
uint32_t found ; // Number found/limit checker
if (!this)
Fatal("No Instance\n") ;
if (!objId) return 0 ;
if (!m_Chain.Count()) return 0 ;
if (m_Chain.Count() == 1)
return m_Chain[0] ;
nMax = m_Chain.Count() - 1 ;
for (found = 2 ; found < nMax ; found *= 2) ;
nDiv = found / 2 ;
nPos = found - 1 ;
for (;;)
{
if (nPos > nMax)
{
if (!nDiv)
break ;
nPos -= nDiv ;
nDiv /= 2 ;
continue ;
}
pBloc = m_Chain[nPos] ;
if (objId > pBloc->m_nHi)
{
// Go higher
if (!nDiv)
break ;
nPos += nDiv ;
nDiv /= 2 ;
continue ;
}
if (objId < pBloc->m_nLo)
{
// Go lower
if (!nDiv)
break ;
nPos -= nDiv ;
nDiv /= 2 ;
continue ;
}
found = nPos ;
return m_Chain[found] ;
}
return 0 ;
}
/*
** hdbObjRepos::_cache Functions
*/
hzEcode hdbObjRepos::_cache::Init (const hdbClass* pClass)
{
// Initialize the _cache. Create the _hz_xchain instance
//
// Argument: pClass The data class
//
// Returns: E_OK
//
// Exits: E_ARGUMENT If no class is supplied
// E_NOINIT If supplied class is not initialized
// E_DUPLICATE If Cache is already set to a class
if (!pClass) hzexit(E_ARGUMENT, "No class supplied") ;
if (!pClass->IsInit()) hzexit(E_NOINIT, "Class not initialized") ;
if (m_pClass) hzexit(E_DUPLICATE, "Cache already has a data class") ;
m_pClass = pClass ;
return E_OK ;
}
hzEcode hdbObjRepos::_cache::FetchEDO (hzChain& edo, uint32_t objId)
{
// 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_OK Operation successful
_hzfunc("hdbObjRepos::_cache::FetchEDO") ;
xbufIter zi ; // Node data iterator
xbufIter edoStart ; // Start of EDO marker
xbufIter edoMark ; // Tail of EDO marker
_c_blk* pBloc ; // Current chain block
uint32_t nLen ; // EDO length
uint32_t nId ; // EDO object id
if (!this)
Fatal("No Instance\n") ;
if (!objId)
return hzerr(E_NOTFOUND, "Illegal object ID (0)") ;
edo.Clear() ;
pBloc = _findBlock(objId) ;
if (!pBloc)
return hzerr(E_NOTFOUND, "No block identified for object %u", objId) ;
// Now iterate EDOs in block. Read the length and ids from serial integers and iterate until either an EDO of the object id is found or surpassed
zi = pBloc->m_edo_space ;
for (; !zi.eof() ;)
{
// Read EDO length and id
edoStart = zi ;
ReadSerialUINT32(nLen, zi) ;
edoMark = zi ;
ReadSerialUINT32(nId, zi) ;
//threadLog("EDO id %u len %u oset %u\n", nId, nLen, zi._oset()) ;
if (nId < objId)
{ zi = edoMark ; zi += nLen ; continue ; }
if (nId > objId)
return E_NOTFOUND ;
// EDO found, advance marker to the end
edoMark += nLen ;
break ;
}
// Copy the EDO to the chain
for (zi = edoStart ; !zi.eof() && zi != edoMark ; zi++)
{
edo.AddByte(*zi) ;
}
return E_OK ;
}
hzEcode hdbObjRepos::_cache::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::_cache::CommitEDO") ;
hzXbuf newBuf ; // New buffer (result of insert)
chIter ei ; // EDO iterator
xbufIter zi ; // Iterator
xbufIter xi ; // Reserve iterator
_c_blk* pBloc ; // Current chain block
uint32_t nLen ; // EDO length
uint32_t nId ; // EDO object id
// Validate
if (!this) hzexit(E_CORRUPT, "No repository instance") ;
if (!m_pClass) hzexit(E_NOINIT, "Cache not initialized") ;
/* Diags
hzChain err ;
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) ;
*/
if (!objId)
hzexit(E_ARGUMENT, "Invalid object ID") ;
// Virgin cache
if (!m_Chain.Count())
{
pBloc = new _c_blk() ;
m_Chain.Add(pBloc) ;
}
//if (!objId || objId > m_nTopId)
if (objId > m_nTopId)
{
// EDO is taken to be new so objId is set at current highest + 1. Note this is not the EDO population as some EDOs may have been deleted. The position of the new EDO will
// be at the end of the last block in the chain.
pBloc = m_Chain[m_Chain.Count()-1] ;
if (pBloc->m_edo_space.Size() > 4000)
{
pBloc = new _c_blk() ;
m_Chain.Add(pBloc) ;
pBloc->m_nLo = objId ;
threadLog("Allocating new block %p low %u\n", pBloc, pBloc->m_nLo) ;
}
pBloc->m_edo_space += edo ;
pBloc->m_nHi = objId ;
m_nEDO++ ;
m_nTopId++ ;
return E_OK ;
}
// objId supplied so looking for an existing EDO to replace. If not found this is an error
pBloc = _findBlock(objId) ;
if (!pBloc)
return hzerr(E_CORRUPT, "No block located") ;
// Now iterate EDOs in block. Read the length and ids from serial integers and iterate until either an EDO of the object id is found or surpassed
//zi.SetPosn(pBloc, pBloc->m_nOsetEDO) ;
if (objId > pBloc->m_nHi)
{
// New object will be the highest
pBloc->m_edo_space += edo ;
pBloc->m_nHi = objId ;
m_nEDO++ ;
}
else if (objId < pBloc->m_nLo)
{
// New object will be the lowest
newBuf += edo ;
newBuf += pBloc->m_edo_space ;
pBloc->m_edo_space = newBuf ;
pBloc->m_nLo = objId ;
m_nEDO++ ;
}
else
{
// New object is in-between so iterate to the correct position
zi = pBloc->m_edo_space ;
for (; !zi.eof() ;)
{
xi = zi ;
ReadSerialUINT32(nLen, zi) ;
ReadSerialUINT32(nId, zi) ;
if (nId > objId)
return E_NOTFOUND ;
if (nId < objId)
{
xi += nLen ;
zi = xi ;
continue ;
}
break ;
}
if (nId > objId)
{
// The object id does not exist in the block. In normal operation this is an error since object ids are non-recurrent. During the initial delta load (bLoad=true), this
// is allowed since deltas are not necessarily in order.
if (!bLoad)
return E_NOTFOUND ;
// xi is the position of the new edo
for (zi = pBloc->m_edo_space ; zi != xi ; zi++)
{
newBuf.AddByte(*zi) ;
}
newBuf += edo ;
for (; !xi.eof() ; xi++)
{
newBuf.AddByte(*zi) ;
}
pBloc->m_edo_space = newBuf ;
}
else
{
// xi marks the existing object. If the size is the same, just overwrite
if (nLen == edo.Size())
{
for (ei = edo ; !ei.eof() ; ei++)
{
xi = *ei ;
xi++ ;
}
}
else
{
// Size not the same. Write everything up to xi to newBuf, write edo to newBuf, write everything north of xi + nLen to newBuf, make newBuf the new edo_space.
for (zi = pBloc->m_edo_space ; zi != xi ; zi++)
{
newBuf.AddByte(*zi) ;
}
newBuf += edo ;
for (xi += nLen ; !xi.eof() ; xi++)
{
newBuf.AddByte(*zi) ;
}
pBloc->m_edo_space = newBuf ;
}
}
}
return E_OK ;
}
void hdbObjRepos::_cache::Show (hzChain& Z, bool bDetail) const
{
// Show node content
_hzfunc("hdbObjRepos::_cache::Show") ;
xbufIter zi ; // X-buf iterator
_c_blk* pBloc ; // Current chain block
uint32_t n ; // Block counter
uint32_t byte ; // Byte
for (n = 0 ; n < m_Chain.Count() ; n++)
{
pBloc = m_Chain[n] ;
Z.Printf("Node %u: Lo %u Hi %u Size %u\n", n, pBloc->m_nLo, pBloc->m_nHi, pBloc->m_edo_space.Size()) ;
if (bDetail)
{
for (zi = pBloc->m_edo_space ; !zi.eof() ; zi++)
{
byte = *zi & 0xff ;
Z.Printf("%02x,", byte) ;
}
Z.AddByte(CHAR_NL) ;
}
}
}
/*
** hdbObjRepos Functions
*/
hdbObjRepos::hdbObjRepos (hdbADP& adp)
{
m_pADP = &adp ;
m_pBR_Delta = 0 ;
m_pBR_Datum = 0 ;
m_pMain = 0 ;
m_nSeqId = m_nPopulation = 0 ;
m_DeltaId = 0 ;
m_bBinaries = false ;
m_eReposInit = HDB_CLASS_INIT_NONE ;
}
hdbObjRepos::~hdbObjRepos (void)
{
if (m_pMain)
m_pMain->Clear() ;
delete m_pMain ;
}
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_pMain = new _cache() ;
m_pMain->Init(m_pClass) ;
}
// Set init state
m_eReposInit = HDB_REPOS_INIT_PROG ;
// 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") ;
//const hdbMember* pMbr ; // Named class member
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_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 the XML Class Description
// m_pClass->DescClass(X, 0) ;
// if (!X.Size())
// return hzerr(E_INITFAIL, "Data class %s: Failed to write description", *m_Name) ;
// threadLog("Called with name %s and workdir %s\n", *m_Name, *m_Workdir) ;
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 ;
}
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_pMain)
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) ;
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_nSeqId = m_nPopulation = m_pMain->Count() ;
threadLog("Population of objects in cache %s is %d. Status is %s\n", txtName(), Count(), 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_pMain)
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 % 1000))
threadLog("Processing line %u\n", nLine) ;
is.getline(lineBuf, 2040) ;
if (!is.gcount())
{
threadLog("Terminating on line %u\n", nLine) ;
break ;
}
if (is.gcount() == 2040)
{ rc = hzerr(E_RANGE, "Line %u exceeds buffer", nLine) ; break ; }
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
{
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) ;
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) ;
if (rc != E_OK)
hzerr(rc, "Could not commit EDO") ;
}
if (rc != E_OK)
break ;
currObj.Clear() ;
lastObjId = objId ;
novals = 0 ;
//threadLog("Cleared object: ID %u\n", objId) ;
}
}
// 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 ;
}
//threadLog("Doing member %s\n", pMbr->txtName()) ;
}
// 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) ;
if (rc != E_OK)
hzerr(rc, "Could not commit EDO") ;
}
}
}
m_nSeqId = m_nPopulation = m_pMain->Count() ;
threadLog("Population of objects in cache %s is %d. Delta file %s. Status is %s\n", txtName(), Count(), *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) ;
if (m_pMain)
m_pMain->Clear() ;
// if (m_pAuxA) m_pAuxA->Clear() ;
// if (m_pAuxB) m_pAuxB->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 Object\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) ;
//atom.SetValue(pMbr->Basetype(), theObj.m_Values[mbrNo]) ;
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: Got objId of %d for member %s", *m_Name, objId, pMbr->txtName()) ;
}
}
/*
** 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 delta file
m_osDelta << delta ;
m_osDelta.flush() ;
// Commit whole object delta to binary datum repository
if (m_pMain)
{
// Commit EDO
rc = theObj.ExportEDO(edo) ;
if (rc != E_OK)
return hzerr(rc, "Could not export EDO") ;
// Commit EDO to cache
rc = m_pMain->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)
{
// Mark as deleted, the object found at the supplied address. Write out an object deletion to the working and backup data files is they apply.
//
// Arguments: 1) objId The target object id
_hzfunc("hdbObjRepos::Delete") ;
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_OPEN) ;
// if (!mx->m_Objects.Exists(objId))
// return E_NOTFOUND ;
//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") ;
uint32_t nIndex ; // Member and index iterator
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_OPEN) ;
if (objId >= m_pMain->Count())
return E_RANGE ;
// Now go thru object's class members filling in the allocated fixed length space
for (nIndex = 0 ;; nIndex++)
{
}
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
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 all object members
obj.Clear() ;
// Object id out of range?
if (objId > m_pMain->Count())
return hzerr(E_NOTFOUND, "Beyond Range %u\n", m_pMain->Count()) ;
// Fetch data object
if (m_pMain)
{
// Fetch EDO
rc = m_pMain->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
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 ;
}
#if 0
hzEcode hdbObjRepos::Fetchbin (hzAtom& atom, const hzString& member, uint32_t objId)
{
// Fetch the actual binary content from a document/binary member. This is a two step process. Firstly in the object element itself, we have the
// address of the binary which will reside in a hdbBinCron or hdbBinStore instance. The Second part is lifting the actual value (reading in the
// binary)
//
// 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::Fetchbin") ;
const hdbMember* pMbr ; // Member pointer
const hdbBinRepos* pRepos ; // Applicabe binary datum repository
hzChain Z ; // Chain to recieve the binary from the hdbBinCron
_atomval av ; // Atom value
uint32_t nSlot ; // Set by AssignSlot
uint32_t mbrNo ; // Member position
uint32_t addr ; // String number
hzEcode rc = E_OK ; // Return from hdbBinCron::Fetch
atom.Clear() ;
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_OPEN) ;
pMbr = m_pClass->GetMember(member) ;
if (!pMbr)
return hzerr(E_CORRUPT, "No class member of %s in class %s", *member, m_pClass->txtType()) ;
mbrNo = pMbr->Posn() ;
if (objId < 1 || objId > m_pMain->Count())
return E_RANGE ;
// And check member is a document or a binary
if (pMbr->Basetype() != BASETYPE_BINARY && pMbr->Basetype() != BASETYPE_TXTDOC)
return E_TYPE ;
// Grab the binary address from this object element
m_pMain->AssignSlot(nSlot, objId, 0) ;
m_pMain->GetVal(av, objId, mbrNo) ;
addr = av.m_uInt32 ;
// Grab the binary value
//rc = m_Binaries[mbrNo]->Fetch(Z, addr) ;
pRepos = (const hdbBinRepos*) m_pStores[mbrNo] ;
rc = pRepos->Fetch(Z, addr) ;
if (rc == E_OK)
atom.SetValue(pMbr->Basetype(), Z) ;
return rc ;
}
#endif
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) ;
}
#if 0
hzEcode hdbObjRepos::Fetchlist (hzVect<uint32_t>& items, const hzString& member, uint32_t objId)
{
// For members that amount to lists (of either atomic values or class instances), fetch the list into the supplied vector (of uint32_t). The vector
// will then be used to iterate through real values for the member, stored in one of the auxillary RAM tables.
//
// Arguments: 1) items The vector of items (uint32_t) which will serve as addresses
// 2) member The class member (which must be defined as supporting multiple values)
// 3) objId The data object id
//
// Returns: E_NOINIT If the hdbObjRepos is not fully initialized
// E_CORRUPT If the named member does not exist
// E_RANGE If the member is not a list (has max pop of 1)
// E_NOTFOUND If the object id does not exist in the repository
// E_OK If init state is full, member is a list and object exists - even if the member has no values.
_hzfunc("hdbObjRepos::Fetchlist") ;
const hdbMember* pMbr ; // Member pointer
items.Clear() ;
_hdb_ck_initstate(m_Name, m_eReposInit, HDB_REPOS_OPEN) ;
pMbr = m_pClass->GetMember(member) ;
if (!pMbr)
return hzerr(E_CORRUPT, "No class member of %s in class %s", *member, m_pClass->txtType()) ;
if (pMbr->MaxPop() == 1)
return E_RANGE ;
if (objId < 1 || objId > m_pMain->Count())
return E_NOTFOUND ;
// if (!mx->m_Lists[pMbr->Posn()])
// return E_TYPE ;
return E_OK ;
}
#endif
hzEcode hdbObjRepos::Select (hdbIdset& result, const char* cpSQL) const
{
// Select data objects according to the supplied search criteria (arg 2), and populate the supplied hdbIdset 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() ;
// Do the Select
// if (!pExp->Parse(cpSQL))
// hzerr(E_PARSE) ;
return rc ;
}