khanat-code-old/code/ryzom/server/src/persistant_data_service/pds_table.cpp

2318 lines
50 KiB
C++

// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "../pd_lib/pds_common.h"
#include <nel/misc/debug.h>
#include <nel/misc/common.h>
#include <nel/misc/entity_id.h>
#include <string.h>
#include "pds_table.h"
#include "pds_database.h"
//#include "db_file_stream.h"
#include "../pd_lib/db_delta_file.h"
#include "../pd_lib/db_description_parser.h"
using namespace std;
using namespace NLMISC;
/// Aligne a value on a boundary (if boundary is not a power of 2, rounded to next power of 2 boundary)
inline uint32 alignOnBoundary(uint32 value, uint32 boundary)
{
// round to power of 2 boundary
boundary = raiseToNextPowerOf2(boundary);
// align
return (value+boundary-1)&(~(boundary-1));
}
/*
* Constructor
*/
CTable::CTable()
{
clear();
}
/*
* Destructor
*/
CTable::~CTable()
{
//PDS_DEBUG("delete()");
clear();
}
/*
* Clear table
*/
void CTable::clear()
{
_Init = false;
uint i;
// clear table buffer
_TableBuffer.clear();
// delete columns
_Columns.clear();
// delete attributes
for (i=0; i<_Attributes.size(); ++i)
{
delete _Attributes[i];
_Attributes[i] = NULL;
}
_Attributes.clear();
_Parent = NULL;
_Name = "";
_Id = INVALID_TYPE_ID;
_Key = INVALID_TYPE_ID;
_Mapped = false;
_RowSize = 0;
_EmptyRow.clear();
}
/*
* Init table
*/
bool CTable::init(CDatabase *database, const CTableNode& table)
{
// first a good clean up
clear();
// set parent logger
setParentLogger(database);
_TableBuffer.setParentLogger(this);
_Parent = database;
_Name = table.Name;
_Id = table.Id;
_Inheritance = table.Inherit;
_Mapped = (table.Mapped != -1);
_Key = table.Key;
uint i;
for (i=0; i<table.Attributes.size(); ++i)
{
CAttribute* attribute = new CAttribute();
if (!attribute->init(database, this, table.Attributes[i]) || attribute->getId() != i)
return false;
_Attributes.push_back(attribute);
}
return true;
}
/*
* Build columns
*/
bool CTable::buildColumns()
{
// do not compute twice
if (!_Columns.empty())
return true;
uint i;
// build columns from attributes
for (i=0; i<_Attributes.size(); ++i)
if (!_Attributes[i]->buildColumns())
return false;
uint32 columnStart = 0;
for (i=0; i<_Columns.size(); ++i)
{
_Columns[i].setByteOffset(columnStart);
columnStart += _Columns[i].getByteSize();
}
_RowSize = columnStart;
_TableBuffer.init(_Id, _RowSize, _Mapped);
_EmptyRow.clear();
_EmptyRow.resize(_RowSize, 0);
for (i=0; i<_Columns.size(); ++i)
{
TDataType type = _Columns[i].getDataType();
void* data = &_EmptyRow[_Columns[i].getByteOffset()];
if (type == PDS_Index)
{
*(RY_PDS::CObjectIndex*)data = RY_PDS::CObjectIndex::null();
}
else if (type == PDS_List)
{
memset(data, 0, _Columns[i].getByteSize());
}
}
_Init = true;
return true;
}
/*
* Post Init, called once all tables have been initialised
*/
bool CTable::postInit()
{
CTable* root = getRootTable();
if (root != this)
_TableBuffer.linkRowMapper(&(root->_TableBuffer));
uint i;
for (i=0; i<_Attributes.size(); ++i)
if (!_Attributes[i]->computeBackRefKey())
return false;
return true;
}
/*
* Display table
*/
void CTable::display(NLMISC::CLog* log, bool expanded, bool displayHeader) const
{
if (!initialised())
{
log->displayNL("** Table not initialised");
return;
}
if (displayHeader)
{
log->displayNL("--------------------------------------------------------------------------------");
log->displayNL("%-10s %-3s %-32s | %-3s | %-3s | %-4s | %-8s %-8s %8s |", "", "Id", "Name", "Inh", "Att", "Cols", "Allocs", "Loaded", "LoadedSz");
}
log->displayNL("%-10s %-3d %-32s | %-3d | %-3d | %-4d | %-8d %-8d %6dkb |", "Table", _Id, _Name.c_str(), _Inheritance, _Attributes.size(), _Columns.size(), _TableBuffer.numAllocated(), _TableBuffer.getLoadedRows(), (_TableBuffer.getMemoryLoad()+1023)/1024);
uint i;
if (expanded)
{
log->displayNL("--------------------------------------------------------------------------------");
log->displayNL("%-10s %-3s %-32s | %-10s | %-4s %-4s |", "", "Id", "Name", "MetaType", "From", "NCol");
for (i=0; i<_Attributes.size(); ++i)
{
const CAttribute* attrib = _Attributes[i];
if (attrib == NULL || !attrib->initialised())
log->displayNL("** Attribute %d not initialised", i);
else
log->displayNL("%-10s %-3d %-32s | %-10s | %-4d %-4d |", "Attribute", attrib->getId(), attrib->getName().c_str(), getNameFromMetaType(attrib->getMetaType()).c_str(), attrib->getOffset(), attrib->getColumns());
}
log->displayNL("--------------------------------------------------------------------------------");
log->displayNL("%-10s %-3s %-64s | %-10s %-10s | %-10s", "", "Id", "Name", "MetaType", "DataType", "RowSz/Offs");
for (i=0; i<_Columns.size(); ++i)
{
const CColumn& column = _Columns[i];
if (!column.initialised())
log->displayNL("** Column %d not initialised", i);
else
log->displayNL("%-10s %-3d %-64s | %-10s %-10s | %1db at %-3d", "Column", column.getId(), column.getName().c_str(), getNameFromMetaType(column.getMetaType()).c_str(), getNameFromDataType(column.getDataType()).c_str(), column.getByteSize(), column.getByteOffset());
}
}
}
/*
* Display row
*/
void CTable::displayRow(RY_PDS::TRowIndex row, NLMISC::CLog* log, bool displayHeader)
{
if (!initialised())
{
log->displayNL("** Table not initialised");
return;
}
CTableBuffer::CAccessor rowaccess = _TableBuffer.getRow(row);
string flagstr = toString("%s%s%s",
(rowaccess.allocated() ? "allocated" : "free"),
(rowaccess.mapped() ? ", mapped" : ""),
(rowaccess.dirty() ? ", dirty" : ""));
log->displayNL("row %d: %d bytes, flags=[%s] (map=%016"NL_I64"X, dirtstamp=%08X)", row, _RowSize, flagstr.c_str(), (rowaccess.mapped() ? rowaccess.key() : (uint64)0), rowaccess.dirtyStamp());
if (displayHeader)
{
log->displayNL("%-10s %-3s %-64s | %-10s %-10s | %-9s | %-32s", "", "Id", "Name", "MetaType", "DataType", "Sz/Offs", "Value");
}
uint i;
for (i=0; i<_Columns.size(); ++i)
{
if (!_Columns[i].initialised())
{
log->displayNL("** Column %d not initialised", i);
continue;
}
string value;
CDataAccessor accessor(this, rowaccess, i);
const CColumn &col = _Columns[i];
if (!accessor.isValid())
value = "unaccessible value";
else
value = accessor.valueAsString();
log->displayNL("%-10s %-3d %-64s | %-10s %-10s | %1db at %-3d | %-32s",
"Column", col.getId(), col.getName().c_str(),
getNameFromMetaType(col.getMetaType()).c_str(), getNameFromDataType(col.getDataType()).c_str(),
col.getByteSize(), col.getByteOffset(),
value.c_str());
}
_TableBuffer.releaseRow(rowaccess);
}
/*
* Dump Delta file content
*/
void CTable::dumpDeltaFileContent(const std::string& filename, NLMISC::CLog* log) const
{
#ifdef DEBUG_DATA_ACCESSOR
if (!initialised())
{
PDS_WARNING("dumpDeltaFileContent(): table not initialised");
return;
}
CDBDeltaFile delta;
if (!_TableBuffer.setupDebugDeltaFile(filename, delta))
{
return;
}
uint8* data;
uint32 row;
log->displayNL("%-10s %-3s %-64s | %-10s %-10s | %-9s | %-32s", "", "Id", "Name", "MetaType", "DataType", "Sz/Offs", "Value");
while ((data = _TableBuffer.getDeltaRow(row, delta)) != NULL)
{
log->displayNL("row %d", row);
CTable* table = const_cast<CTable*>(this);
CDataAccessor accessor(table, data, 0);
uint i;
for (i=0; i<_Columns.size(); ++i)
{
if (!_Columns[i].initialised())
{
log->displayNL("** Column %d not initialised", i);
continue;
}
string value;
CDataAccessor accessor(accessor, i);
const CColumn &col = _Columns[i];
if (!accessor.isValid())
value = "unaccessible value";
else
value = accessor.valueAsString();
log->displayNL("%-10s %-3d %-64s | %-10s %-10s | %1db at %-3d | %-32s",
"Column", col.getId(), col.getName().c_str(),
CType::getNameFromMetaType(col.getMetaType()).c_str(), CType::getNameFromDataType(col.getDataType()).c_str(),
col.getByteSize(), col.getByteOffset(),
value.c_str());
}
}
#endif
}
/*
* Rebuild forwardrefs from backrefs
*/
bool CTable::rebuildForwardRefs()
{
if (!initialised())
{
PDS_WARNING("rebuildForwardRefs(): table not initialised");
return false;
}
if (!fillRefInfo())
{
PDS_WARNING("rebuildForwardRefs(): failed to fillRefInfo()");
return false;
}
if (BackRefInfo.empty() && ForwardRefInfo.empty())
return true;
if (!_TableBuffer.processRows(this))
{
PDS_WARNING("rebuildForwardRefs(): failed to processRows()");
return false;
}
return true;
}
/*
* Reset forwardrefs
*/
bool CTable::resetForwardRefs()
{
if (!initialised())
{
PDS_WARNING("resetForwardRefs(): table not initialised");
return false;
}
return true;
}
/*
* Reset table map
*/
bool CTable::resetTableMap()
{
if (!initialised())
{
PDS_WARNING("resetTableMap(): table not initialised");
return false;
}
return true;
}
/*
* Rebuild table map
*/
bool CTable::rebuildTableMap()
{
if (!initialised())
{
PDS_WARNING("rebuildTableMap(): table not initialised");
return false;
}
if (!_Mapped)
return true;
return true;
}
/*
* Allocate a row in a table
* \param row is the row to allocate
* Return true if succeded
*/
bool CTable::allocate(RY_PDS::TRowIndex row, bool acquireRow)
{
CTableBuffer::CAccessor accessor;
if (!_TableBuffer.allocate(row, accessor))
return false;
if (!accessor.allocated())
return false; // should not happen
resetRow(accessor.data());
// lock row if required
if (acquireRow)
_TableBuffer.acquireRow(accessor);
return true;
}
/*
* Deallocate a row in a table
* \param row is the row to deallocate
* Return true if succeded
*/
bool CTable::deallocate(RY_PDS::TRowIndex row)
{
return _TableBuffer.deallocate(row);
}
/*
* Map a row
* \param row is the row to allocate
* \param key is the 64 bits row key
* Return true if succeded
*/
bool CTable::mapRow(const RY_PDS::CObjectIndex &index, uint64 key)
{
if (!initialised())
{
PDS_WARNING("mapRow(): failed, table is not initialised");
return false;
}
if (!_Mapped)
{
PDS_WARNING("mapRow(): failed, table is not mapped");
return false;
}
if (!_TableBuffer.mapRow(index, key))
{
PDS_WARNING("mapRow(): failed to map '%s' to '%016"NL_I64"X'", index.toString().c_str(), key);
return false;
}
PDS_FULL_DEBUG("Mapped '%s' to key '%016"NL_I64"X'", index.toString().c_str(), key);
return true;
}
/*
* Unmap a row in a table
* \param tableIndex is the table to find row
* \param key is the 64 bits row key
* Return true if succeded
*/
bool CTable::unmapRow(uint64 key)
{
if (!initialised())
{
PDS_WARNING("unmapRow(): failed, table is not initialised");
return false;
}
if (!_Mapped)
{
PDS_WARNING("unmapRow(): failed, table is not mapped");
return false;
}
// get index
RY_PDS::CObjectIndex index = _TableBuffer.getMappedRow(key);
if (index.isNull())
{
PDS_WARNING("unmapRow(): failed, inherited table is not initialised");
return false;
}
if (!_TableBuffer.unmapRow(index, key))
{
PDS_WARNING("mapRow(): failed to unmap '%s' to '%016"NL_I64"X'", index.toString().c_str(), key);
return false;
}
PDS_FULL_DEBUG("Unmapped '%s' of key '%016"NL_I64"X'", index.toString().c_str(), key);
return true;
}
/*
* Get a mapped row
* \param key is the 64 bits row key
* Return a valid TRowIndex if success
*/
RY_PDS::CObjectIndex CTable::getMappedRow(uint64 key) const
{
if (!initialised())
{
PDS_WARNING("getMappedRow(): failed, table is not initialised");
return RY_PDS::CObjectIndex::null();
}
return _TableBuffer.getMappedRow(key);
}
/*
* Release a row in a table
* \param row is the row to release
* \param timestamp is the current timestamp
* Return true if succeded
*/
bool CTable::release(RY_PDS::TRowIndex row)
{
if (!initialised())
{
PDS_WARNING("release(): failed, table is not initialised");
return false;
}
return _TableBuffer.releaseRow(row);
}
/*
* Release all rows in table
*/
bool CTable::releaseAll()
{
return _TableBuffer.releaseAll();
}
/*
* Set value
*/
bool CTable::set(RY_PDS::TRowIndex row, RY_PDS::TColumnIndex column, uint datasize, const void* dataptr)
{
RY_PDS::CObjectIndex object((RY_PDS::TTableIndex)_Id, row);
// get an accessor on data
CDataAccessor accessor(_Parent, object, column);
if (!accessor.isValid())
{
PDS_WARNING("set(): failed to get accessor on '%s'", accessor.getColumnIndex().toString().c_str());
return false;
}
switch (accessor.column()->getMetaType())
{
case PDS_Type:
// for simple type, easy money
return accessor.setValue(dataptr, datasize);
break;
case PDS_BackRef:
{
// first unlink previous parent
if (!unlink(accessor, object))
{
PDS_WARNING("set(): unable to unlink previous parent at '%s'", accessor.getColumnIndex().toString().c_str());
}
// then link new parent
if (datasize != getStandardByteSize(PDS_Index))
{
PDS_WARNING("set(): unable to link new at '%s', provided bytesize is not standard", accessor.getColumnIndex().toString().c_str());
return false;
}
// get parent index
RY_PDS::CObjectIndex parent = *(RY_PDS::CObjectIndex*)dataptr;
// check checksum is valid
if (!parent.isChecksumValid())
{
PDS_WARNING("set(): unable to link new parent '%s' at '%s', parent checksum is invalid", parent.toString().c_str(), accessor.getColumnIndex().toString().c_str());
return false;
}
// if parent is invalid, then no need to link
if (!parent.isValid())
return true;
// and link!
if (!link(accessor, parent, object))
{
PDS_WARNING("set(): unable to link new parent '%s' at '%s'", parent.toString().c_str(), accessor.getColumnIndex().toString().c_str());
return false;
}
}
break;
// other types MUST not be directly set
default:
PDS_WARNING("set(): column '%d' doesn't support set on '%s'", column, getNameFromMetaType(accessor.column()->getMetaType()).c_str());
return false;
break;
}
return true;
}
/**
* Set Parent
*/
bool CTable::setParent(RY_PDS::TRowIndex row, RY_PDS::TColumnIndex column, const RY_PDS::CObjectIndex& parent)
{
RY_PDS::CObjectIndex object((RY_PDS::TTableIndex)_Id, row);
// get an accessor on data
CDataAccessor accessor(_Parent, object, column);
if (!accessor.isValid())
{
PDS_WARNING("set(): failed to get accessor on '%s'", accessor.getColumnIndex().toString().c_str());
return false;
}
switch (accessor.column()->getMetaType())
{
case PDS_BackRef:
{
// first unlink previous parent
if (!unlink(accessor, object))
{
PDS_WARNING("set(): unable to unlink previous parent at '%s'", accessor.getColumnIndex().toString().c_str());
}
// check checksum is valid
if (!parent.isChecksumValid())
{
PDS_WARNING("set(): unable to link new parent '%s' at '%s', parent checksum is invalid", parent.toString().c_str(), accessor.getColumnIndex().toString().c_str());
return false;
}
// if parent is invalid, then no need to link
if (!parent.isValid())
return true;
// and link!
if (!link(accessor, parent, object))
{
PDS_WARNING("set(): unable to link new parent '%s' at '%s'", parent.toString().c_str(), accessor.getColumnIndex().toString().c_str());
return false;
}
}
break;
// other types MUST not be directly set
default:
PDS_WARNING("setParent(): column '%d' doesn't support set on '%s'", column, getNameFromMetaType(accessor.column()->getMetaType()).c_str());
return false;
break;
}
return true;
}
/**
* link a BackRef to a parent
* This method will perfom a full linking of child and new parent
* \param backref is an accessor on the BackRef
* \param parent is an index on the parent to link
*/
bool CTable::link(CDataAccessor &backref, const RY_PDS::CObjectIndex &parent, const RY_PDS::CObjectIndex &child)
{
// check ref is valid
if (!backref.isValid())
{
PDS_WARNING("link(): failed, accessor is not valid");
return false;
}
// check really a BackRef
if (backref.attribute()->getMetaType() != PDS_BackRef || backref.column()->getMetaType() != PDS_BackRef)
{
PDS_WARNING("link(): failed, accessor is not a BackRef");
return false;
}
if (!parent.isValid())
{
PDS_WARNING("link(): failed, parent has not a valid index");
return false;
}
// link parent to child
uint32 forwardrefid = backref.attribute()->getReferencedAttribute();
CDataAccessor parentref(_Parent, parent, forwardrefid, 0);
if (!parentref.isValid())
{
PDS_WARNING("link(): failed, parent '%s' is not valid", parentref.toString().c_str());
return false;
}
// check parent and child can really be linked
// that is cross references match
if (parentref.attribute()->getReferencedAttribute() != backref.attribute()->getId() ||
backref.attribute()->getReferencedAttribute() != parentref.attribute()->getId())
{
PDS_WARNING("link(): failed, child '%s' and parent '%s' are not bound to be linked", backref.toString().c_str(), parentref.toString().c_str());
return false;
}
forwardLink(backref, parentref, child);
// set no ref index, with checksum validation
return backref.setIndex(parent);
}
/*
* link a ForwardRef to a child
* This method will only link parent to child
* \param forwardref is an accessor on the BackRef
* \param child is an index on the child to link
*/
bool CTable::forwardLink(CDataAccessor &backref, CDataAccessor &forwardref, const RY_PDS::CObjectIndex &child)
{
if (!backref.isValid() || !forwardref.isValid())
{
PDS_WARNING("forwardLink(): failed, accessor is not valid");
return false;
}
if (!child.isValid())
{
PDS_WARNING("forwardLink(): failed, child '%s' index is invalid", child.toString().c_str());
return false;
}
switch (forwardref.attribute()->getMetaType())
{
case PDS_ArrayRef:
// ArrayRef and ForwardRef set code is a bit shared as ArrayRef will only do
// a little seek to the good column and set the index just like a ForwardRef
// actually, datatypes are the same!
if (!forwardref.seek(backref))
{
PDS_WARNING("forwardLink(): unable to seek to key of '%s'", child.toString().c_str());
return false;
}
case PDS_ForwardRef:
{
RY_PDS::CObjectIndex prevChild;
// check no previous child
if (!forwardref.getIndex(prevChild))
{
PDS_WARNING("forwardLink(): unable to access previous child of '%s'", forwardref.toString().c_str());
}
else if (prevChild.isValid())
{
PDS_WARNING("forwardLink(): '%s' has a previous child '%s'", forwardref.toString().c_str(), prevChild.toString().c_str());
}
// set child
return forwardref.setIndex(child);
}
break;
case PDS_Set:
{
if (!forwardref.checkType(child))
{
PDS_WARNING("forwardLink(): failed, '%s' is not of attribute '%s' type", child.toString().c_str(), forwardref.attribute()->getName().c_str());
return false;
}
RY_PDS::CSetMap::CAccessor setaccess = forwardref.getSet();
if (!setaccess.isValid())
{
PDS_WARNING("forwardLink(): failed, parent '%s' set access is invalid", forwardref.toString().c_str());
return false;
}
setaccess.add(child);
// CHECK
if (!setaccess.belongsTo(child))
{
PDS_WARNING("forwardLink(): failed, child '%s' doesn't belong to parent '%s' though it just had been added", child.toString().c_str(), forwardref.toString().c_str());
return false;
}
// CHECK
return true;
}
break;
default:
PDS_WARNING("forwardLink(): failed, can't link '%s'", getNameFromMetaType(forwardref.attribute()->getMetaType()).c_str());
break;
}
return false;
}
/*
* Unlink a BackRef
* \param ref is an accessor on the BackRef
* \param child is a remember of the child index
*/
bool CTable::unlink(CDataAccessor &backref, const RY_PDS::CObjectIndex &child)
{
// check ref is valid
if (!backref.isValid())
{
PDS_WARNING("unlink(): failed, accessor is not valid");
return false;
}
// check really a BackRef
if (backref.attribute()->getMetaType() != PDS_BackRef || backref.column()->getMetaType() != PDS_BackRef)
{
PDS_WARNING("unlink(): failed, accessor is not a BackRef");
return false;
}
RY_PDS::CObjectIndex parent;
// unlink parent
if (!backref.getIndex(parent))
{
PDS_WARNING("unlink(): unable to get parent index");
}
else if (!parent.isChecksumValid())
{
PDS_WARNING("unlink(): parent index has invalid checksum");
}
else if (parent.isValid())
{
// only unlink if parent exists
// because link will perform unlink before, and child may have no parent yet
uint32 forwardrefid = backref.attribute()->getReferencedAttribute();
CDataAccessor parentref(_Parent, parent, forwardrefid, 0);
forwardUnlink(backref, parentref, child);
}
// set no ref index, with checksum validation
return backref.setIndex(RY_PDS::CObjectIndex::null());
}
/*
* unlink a ForwardRef to a child
* This method will only unlink parent to child
* \param forwardref is an accessor on the BackRef
* \param child is an index on the child to unlink
*/
bool CTable::forwardUnlink(CDataAccessor &backref, CDataAccessor &forwardref, const RY_PDS::CObjectIndex &child)
{
if (!forwardref.isValid())
{
PDS_WARNING("forwardUnlink(): failed, accessor is not valid");
return false;
}
if (!child.isValid())
{
PDS_WARNING("forwardUnlink(): failed, child '%s' index is invalid", child.toString().c_str());
return false;
}
switch (forwardref.attribute()->getMetaType())
{
case PDS_ArrayRef:
// ArrayRef and ForwardRef set code is a bit shared as ArrayRef will only do
// a little seek to the good column and set the index just like a ForwardRef
// actually, datatypes are the same!
if (!forwardref.seek(backref))
{
PDS_WARNING("forwardLink(): unable to seek to key of '%s'", child.toString().c_str());
return false;
}
case PDS_ForwardRef:
{
RY_PDS::CObjectIndex prevChild;
// check child is the one we want to unlink
if (!forwardref.getIndex(prevChild))
{
PDS_WARNING("forwardUnlink(): unable to access previous child of '%s'", forwardref.toString().c_str());
}
else if (!prevChild.isValid())
{
PDS_WARNING("forwardUnlink(): '%s' has no previous child '%s'", prevChild.toString().c_str(), forwardref.toString().c_str());
}
else if (prevChild != child)
{
PDS_WARNING("forwardUnlink(): failed, '%s' was not linked to '%s' but to '%s'", forwardref.toString().c_str(), child.toString().c_str(), prevChild.toString().c_str());
return false;
}
// set child
return forwardref.setIndex(RY_PDS::CObjectIndex::null());
}
break;
case PDS_Set:
{
RY_PDS::CSetMap::CAccessor setaccess = forwardref.getSet();
if (!setaccess.isValid())
{
PDS_WARNING("forwardUnlink(): failed, parent '%s' set access is invalid", forwardref.toString().c_str());
return false;
}
if (!setaccess.belongsTo(child))
{
PDS_WARNING("forwardUnlink(): failed, child '%s' doesn't belong to parent '%s'", child.toString().c_str(), forwardref.toString().c_str());
return false;
}
setaccess.erase(child);
if (setaccess.belongsTo(child))
{
PDS_WARNING("forwardUnlink(): failed, child '%s' still belongs to parent '%s' though it had been deleted", child.toString().c_str(), forwardref.toString().c_str());
return false;
}
return true;
}
break;
default:
PDS_WARNING("forwardUnlink(): failed, can't unlink '%s'", getNameFromMetaType(forwardref.attribute()->getMetaType()).c_str());
break;
}
return false;
}
/*
* Set value
*/
bool CTable::get(RY_PDS::TRowIndex row, RY_PDS::TColumnIndex column, uint& datasize, void* dataptr, TDataType& type)
{
RY_PDS::CObjectIndex object((RY_PDS::TTableIndex)_Id, row);
// get an accessor on data
CDataAccessor accessor(_Parent, object, column);
if (!accessor.isValid())
{
PDS_WARNING("get(): failed to get accessor on '%s'", accessor.getColumnIndex().toString().c_str());
return false;
}
// get datatype
type = accessor.column()->getDataType();
switch (accessor.column()->getMetaType())
{
case PDS_Type:
// for simple type, easy money
return accessor.getValue(dataptr, datasize);
break;
case PDS_BackRef:
case PDS_ForwardRef:
{
if (getStandardByteSize(type) > datasize)
{
PDS_WARNING("get(): databuffer too narrow to store column '%d' Index", column);
return false;
}
datasize = sizeof(RY_PDS::CObjectIndex);
return accessor.getIndex(*(RY_PDS::CObjectIndex*)dataptr);
}
break;
case PDS_Set:
{
PDS_WARNING("get(): not supported for sets");
return false;
}
break;
// other types MUST not be directly set
default:
PDS_WARNING("get(): column '%d' doesn't support get on '%s'", column, getNameFromMetaType(accessor.column()->getMetaType()).c_str());
return false;
break;
}
return true;
}
/*
* Perform column integrity check
*/
bool CTable::CDataAccessor::check() const
{
if (!isValid())
return false;
#ifdef DEBUG_DATA_ACCESSOR
if (_IsDebug)
return true;
#endif
switch (_Column->getMetaType())
{
case PDS_Type:
return checkAsTypeAccessor();
break;
case PDS_BackRef:
if (_Attribute->getMetaType() != PDS_BackRef)
{
PDS_WARNING_IN(_Table)("CDataAccessor::check(): column '%s' MetaType incoherent with attribute's definition", _Column->getName().c_str());
return false;
}
return checkAsRefAccessor();
break;
case PDS_ForwardRef:
if (_Attribute->getMetaType() != PDS_ForwardRef &&
_Attribute->getMetaType() != PDS_ArrayRef)
{
PDS_WARNING_IN(_Table)("CDataAccessor::check(): column '%s' MetaType incoherent with attribute's definition", _Column->getName().c_str());
return false;
}
return checkAsRefAccessor();
break;
case PDS_Set:
return checkAsSetAccessor();
break;
}
return false;
}
/*
* Check an accessor as a PDS_Type accessor
*/
bool CTable::CDataAccessor::checkAsTypeAccessor() const
{
// check attribute
if (_Attribute->getMetaType() != PDS_Type &&
_Attribute->getMetaType() != PDS_Class &&
_Attribute->getMetaType() != PDS_ArrayType &&
_Attribute->getMetaType() != PDS_ArrayClass)
{
PDS_WARNING_IN(_Table)("CDataAccessor::checkAsTypeAccessor(): column '%s' MetaType incoherent with attribute's definition", _Column->getName().c_str());
return false;
}
if (!checkStrictDataType(_Column->getDataType()))
{
PDS_WARNING_IN(_Table)("CDataAccessor::checkAsTypeAccessor(): column '%s' has not a strict valid datatype ('%s')", _Column->getName().c_str(), getNameFromDataType(_Column->getDataType()).c_str());
return false;
}
if (_Column->getDataType() == PDS_enum || _Column->getDataType() == PDS_dimension)
{
const CType* index = _Table->getParent()->getType(_Column->getTypeId());
if (index == NULL)
{
PDS_WARNING_IN(_Table)("CDataAccessor::checkAsTypeAccessor(): couldn't find column '%s' original type in database", _Column->getName().c_str());
return false;
}
if (!index->isIndex())
{
PDS_WARNING_IN(_Table)("CDataAccessor::checkAsTypeAccessor(): column '%s' original type is not an enum, whereas column says so", _Column->getName().c_str());
return false;
}
uint32 value;
if (!getAsIndexType(value))
{
PDS_WARNING_IN(_Table)("CDataAccessor::checkAsTypeAccessor(): couldn't getAsIndexValue() column '%s'", _Column->getName().c_str());
return false;
}
if (value >= index->getIndexSize())
{
PDS_WARNING_IN(_Table)("CDataAccessor::checkAsTypeAccessor(): column '%s' is out of enum '%s' range", _Column->getName().c_str(), index->getName().c_str());
return false;
}
}
return true;
}
/*
* Check an accessor as a PDS_BackRef or PDS_ForwardRef accessor
*/
bool CTable::CDataAccessor::checkAsRefAccessor() const
{
if (_Column->getDataType() != PDS_Index)
{
PDS_WARNING_IN(_Table)("CDataAccessor::checkAsRefAccessor(): column '%s' DataType must be Index, found '%s'", _Column->getName().c_str(), getNameFromDataType(_Column->getDataType()).c_str());
return false;
}
RY_PDS::CObjectIndex ref;
if (!getIndex(ref))
{
PDS_WARNING_IN(_Table)("CDataAccessor::checkAsRefAccessor(): can't get column '%s' Index", _Column->getName().c_str());
return false;
}
if (!checkType(ref))
{
PDS_WARNING_IN(_Table)("CDataAccessor::checkAsRefAccessor(): column '%s' contains object '%s' of invalid type", _Column->getName().c_str(), ref.toString().c_str());
return false;
}
if (!ref.isNull() && !_Table->getParent()->isAllocated(ref))
{
PDS_WARNING_IN(_Table)("CDataAccessor::checkAsRefAccessor(): column '%s' points to '%s' which is not allocated", _Column->getName().c_str(), ref.toString().c_str());
return false;
}
return true;
}
/*
* Check an accessor as a PDS_Set accessor
*/
bool CTable::CDataAccessor::checkAsSetAccessor() const
{
if (_Column->getDataType() != PDS_List)
{
PDS_WARNING_IN(_Table)("CDataAccessor::checkAsSetAccessor(): column '%s' DataType must be List, found '%s'", _Column->getName().c_str(), getNameFromDataType(_Column->getDataType()).c_str());
return false;
}
RY_PDS::CSetMap::CAccessor setaccess = getSet();
if (!setaccess.isValid())
{
PDS_WARNING_IN(_Table)("CDataAccessor::checkAsSetAccessor(): set accessor to '%s' is invalid", toString().c_str());
return false;
}
const RY_PDS::TIndexList& list = setaccess.get();
bool success = true;
uint i;
for (i=0; i<list.size(); ++i)
{
RY_PDS::CObjectIndex index = list[i];
if (!index.isValid())
{
PDS_WARNING_IN(_Table)("CDataAccessor::checkAsSetAccessor(): column '%s' points to invalid object '%s'", _Column->getName().c_str(), index.toString().c_str());
success = false;
continue;
}
if (!_Table->getParent()->isAllocated(index))
{
PDS_WARNING_IN(_Table)("CDataAccessor::checkAsSetAccessor(): column '%s' points to unallocated object '%s'", _Column->getName().c_str(), index.toString().c_str());
success = false;
continue;
}
if (!checkType(index))
{
PDS_WARNING_IN(_Table)("CDataAccessor::checkAsSetAccessor(): column '%s' points to object '%s' which is not of expected type", _Column->getName().c_str(), index.toString().c_str());
success = false;
continue;
}
}
return success;
}
/*
* Fetch a row into stream
* Fetch the whole object arborescence (i.e. children linked to this object)
* \param row is the row to fetch
* \param data is the stream store data into
*/
bool CTable::fetch(RY_PDS::TRowIndex row, RY_PDS::CPData &data, bool fetchIndex)
{
uint i;
RY_PDS::TTableIndex table = (RY_PDS::TTableIndex)_Id;
RY_PDS::CObjectIndex index(table, row);
if (fetchIndex)
{
data.serial(table, row);
}
CDataAccessor rowaccessor(_Parent, index, 0);
for (i=0; i<_Columns.size(); ++i)
{
if (!_Columns[i].initialised())
{
PDS_WARNING("fetch(): failed, column '%d' not initialised", i);
return false;
}
if (_Columns[i].getMetaType() == PDS_BackRef)
{
// backref are not sent, implicitely deduced
continue;
}
CDataAccessor accessor(rowaccessor, i);
if (!accessor.isValid())
{
PDS_WARNING("fetch(): failed, can't create accessor on column '%d' for object '%s'", i, index.toString().c_str());
return false;
}
if (!accessor.fetch(data))
return false;
}
// lock row
rowaccessor.acquire();
return true;
}
/**
* Fetch column data into stream
*/
bool CTable::CDataAccessor::fetch(RY_PDS::CPData &data)
{
if (!isValid())
{
PDS_WARNING_IN(_Table)("CDataAccessor:fetch(): failed, fetch invalid accessor '%s'", toString().c_str());
return false;
}
switch (_Column->getDataType())
{
case PDS_bool:
case PDS_sint8:
case PDS_uint8:
case PDS_char:
data.serial(*(uint8*)_Data);
break;
case PDS_ucchar:
case PDS_uint16:
case PDS_sint16:
data.serial(*(uint16*)_Data);
break;
case PDS_uint32:
case PDS_sint32:
case PDS_float:
case PDS_CSheetId:
case PDS_CNodeId:
case PDS_enum:
data.serial(*(uint32*)_Data);
break;
case PDS_uint64:
case PDS_sint64:
case PDS_double:
case PDS_CEntityId:
data.serial(*(uint64*)_Data);
break;
case PDS_Index:
{
// check this is a forward ref
// only forward ref are sent, backref are implicitely deduced
if (_Column->getMetaType() == PDS_BackRef)
{
PDS_WARNING_IN(_Table)("CDataAccessor:fetch(): failed, try to serialize '%s' as backref", toString().c_str());
return false;
}
RY_PDS::CObjectIndex child = *(RY_PDS::CObjectIndex*)_Data;
if (!child.isChecksumValid())
{
PDS_WARNING_IN(_Table)("CDataAccessor:fetch(): failed, '%s' points to invalid object '%s'", toString().c_str(), child.toString().c_str());
return false;
}
if (child.isNull())
{
// send object id
RY_PDS::TTableIndex tid = RY_PDS::INVALID_TABLE_INDEX;
RY_PDS::TRowIndex rid = RY_PDS::INVALID_ROW_INDEX;
data.serial(tid, rid);
return true;
}
// send object and hierarchy
return _Table->getParent()->fetch(child, data);
}
break;
case PDS_List:
{
RY_PDS::CSetMap::CAccessor setaccess = getSet();
if (!setaccess.isValid())
{
PDS_WARNING_IN(_Table)("CDataAccessor:fetch(): failed, object '%s' set access is invalid", toString().c_str());
return false;
}
const RY_PDS::TIndexList& list = setaccess.get();
uint i;
for (i=0; i<list.size(); ++i)
{
RY_PDS::CObjectIndex child = list[i];
// fetch object index
RY_PDS::TTableIndex tid = child.table();
RY_PDS::TRowIndex rid = child.row();
data.serial(tid, rid);
// fetch object key, for inset creation
CDataAccessor keyaccess(_Table->getParent(), child, _Attribute->getBackRefKey(), 0);
if (!keyaccess.isValid())
{
PDS_WARNING_IN(_Table)("CDataAccessor:fetch(): failed, '%s' points to invalid object '%s'", toString().c_str(), child.toString().c_str());
return false;
}
keyaccess.fetch(data);
// force table not to fetch index, because we already done it
_Table->getParent()->fetch(child, data, false);
}
RY_PDS::TTableIndex tid = RY_PDS::INVALID_TABLE_INDEX;
RY_PDS::TRowIndex rid = RY_PDS::INVALID_ROW_INDEX;
data.serial(tid, rid);
}
break;
case PDS_dimension:
if (_Column->getByteSize() == 1)
{
data.serial(*(uint8*)_Data);
}
else if (_Column->getByteSize() == 2)
{
data.serial(*(uint16*)_Data);
}
else
{
data.serial(*(uint32*)_Data);
}
break;
}
return true;
}
/*
* Get value as string
*/
string CTable::CDataAccessor::valueAsString(bool expandSet) const
{
if (!isValid())
return string("");
std::string value;
switch (_Column->getDataType())
{
case PDS_bool:
value = dataTypeAsString(*(bool*)_Data);
break;
case PDS_char:
value = dataTypeAsString(*(char*)_Data);
break;
case PDS_uint8:
value = dataTypeAsString(*(uint8*)_Data);
break;
case PDS_ucchar:
case PDS_uint16:
value = dataTypeAsString(*(uint16*)_Data);
break;
case PDS_CSheetId:
case PDS_CNodeId:
case PDS_uint32:
value = dataTypeAsString(*(uint32*)_Data);
break;
case PDS_uint64:
value = dataTypeAsString(*(uint64*)_Data);
break;
case PDS_sint8:
value = dataTypeAsString(*(sint8*)_Data);
break;
case PDS_sint16:
value = dataTypeAsString(*(sint16*)_Data);
break;
case PDS_sint32:
value = dataTypeAsString(*(sint32*)_Data);
break;
case PDS_sint64:
value = dataTypeAsString(*(sint64*)_Data);
break;
case PDS_float:
value = dataTypeAsString(*(float*)_Data);
break;
case PDS_double:
value = dataTypeAsString(*(double*)_Data);
break;
case PDS_CEntityId:
value = dataTypeAsString(*(CEntityId*)_Data);
break;
case PDS_enum:
{
value = "'"+NLMISC::toString(*(uint32*)_Data)+"'";
const CType* type = _Table->getParent()->getType(_Column->getTypeId());
if (type == NULL)
{
value += " <undisplayable type>";
}
else
{
value += " "+type->getName();
if (type->isEnum())
{
value += " "+type->getIndexName(*(uint32*)_Data);
}
else
{
value += " not enum";
}
}
}
break;
case PDS_dimension:
{
if (_Column->getByteSize() == 1)
value = "'"+NLMISC::toString(*(uint8*)_Data)+"'";
else if (_Column->getByteSize() == 2)
value = "'"+NLMISC::toString(*(uint16*)_Data)+"'";
else if (_Column->getByteSize() == 4)
value = "'"+NLMISC::toString(*(uint32*)_Data)+"'";
else
value = "<undisplayable>";
const CType* type = _Table->getParent()->getType(_Column->getTypeId());
if (type == NULL)
{
value += " <undisplayable type>";
}
else
{
value += " "+type->getName();
if (type->isDimension())
{
value += " "+type->getIndexName(*(uint32*)_Data);
}
else
{
value += " not dimension";
}
}
}
break;
case PDS_List:
{
#ifdef DEBUG_DATA_ACCESSOR
if (_IsDebug)
value = "undisplayable set";
else
{
const RY_PDS::TIndexList& list = getSet().get();
if (list.empty())
{
value = "Empty list";
}
else if (expandSet)
{
uint i;
value = "";
for (i=0; i<list.size(); ++i)
value += (list[i].toString()+" ");
}
else
{
value = NLMISC::toString(list.size())+" object(s)";
}
}
#else
const RY_PDS::TIndexList& list = getSet().get();
if (list.empty())
{
value = "Empty list";
}
else
{
value = NLMISC::toString(list.size())+" object(s)";
}
#endif
}
break;
case PDS_Index:
value = "'"+((RY_PDS::CObjectIndex*)_Data)->toString()+"'";
break;
default:
value = "undisplayable value";
break;
}
return value;
}
/*
* Build the index allocator for this table
*/
bool CTable::buildIndexAllocator(RY_PDS::CIndexAllocator& allocat)
{
allocat.clear();
uint row;
for (row=0; row<_TableBuffer.maxRowIndex(); ++row)
{
if (isAllocated(row))
{
allocat.forceAllocated(row);
}
}
return true;
}
/*
* Clear dirty list
*/
bool CTable::clearDirtyList()
{
if (!initialised())
{
PDS_WARNING("clearDirtyList(): table not initialised");
return false;
}
_TableBuffer.resetDirty();
return true;
}
/*
* Reset dirty tags
* Reset all rows so no one is marked as being dirty.
* This method fixes broken list issues
*/
bool CTable::resetDirtyTags()
{
/// \todo fill here
nlstop;
if (!initialised())
{
PDS_WARNING("clearDirtyList(): table not initialised");
return false;
}
return true;
}
/*
* Preload reference files
*/
bool CTable::preloadRefFiles()
{
if (!_TableBuffer.openAllRefFilesRead())
return false;
return true;
}
/*
* Notify new Reference, do necessary job...
*/
bool CTable::notifyNewReference(CRefIndex& newref)
{
if (!initialised())
return false;
_TableBuffer.setupRef(newref);
return true;
}
/*
* Apply delta changes from a file
*/
bool CTable::applyDeltaChanges(const string& filename)
{
if (!initialised())
{
PDS_WARNING("buildDelta(): failed, table not initialised");
return false;
}
return _TableBuffer.applyDeltaChanges(filename);
}
/*
* Reset row to initial value
*/
bool CTable::resetRow(uint8* rowData)
{
// copy row pattern
memcpy(rowData, &(_EmptyRow[0]), _RowSize);
return true;
}
/*
* Build the delta file and purge all dirty rows in this table
*/
bool CTable::buildDelta(const CTimestamp& starttime, const CTimestamp& endtime)
{
H_AUTO(PDS_Table_buildDelta);
if (!initialised())
{
PDS_WARNING("buildDelta(): failed, table not initialised");
return false;
}
return _TableBuffer.buildDelta(starttime, endtime);
}
/*
* Flush table from released rows
*/
bool CTable::flushReleased()
{
if (!initialised())
{
PDS_WARNING("flushReleased(): failed, table not initialised");
return false;
}
_TableBuffer.flushReleased();
return true;
}
/*
* Build RowMapper
*/
bool CTable::buildRowMapper()
{
if (!initialised())
{
PDS_WARNING("buildRowMapper(): failed, table not initialised");
return false;
}
return _TableBuffer.buildRowMapper();
}
/*
* The process row callback, fix forwardrefs from backrefs
*/
bool CTable::processRow(RY_PDS::TTableIndex table, CTableBuffer::CAccessor& accessor)
{
if (table != _Id)
{
PDS_WARNING("processRow(): try to process a row from another table! (id '%d')", table);
return false;
}
RY_PDS::CObjectIndex child = RY_PDS::CObjectIndex(table, accessor.row());
uint i;
for (i=0; i<BackRefInfo.size(); ++i)
{
// get backref accessor
CBackRefFiller& bref = BackRefInfo[i];
CDataAccessor backref(this, accessor, (RY_PDS::TColumnIndex)bref.Column->getId());
RY_PDS::CObjectIndex parent;
if (!backref.isValid() || !backref.getIndex(parent))
{
PDS_WARNING("processRow(): failed to get BackRef index");
return false;
}
// parent not set, do nothing
if (!parent.isValid())
{
continue;
}
// get forwardref accessor
CDataAccessor forwardref(_Parent, parent, bref.Referenced->getId(), 0);
switch (bref.Referenced->getMetaType())
{
case PDS_Set:
processBackRefToSet(forwardref, child);
break;
case PDS_ArrayRef:
// seek to pos in array
if (!forwardref.seek(backref))
{
PDS_WARNING("processRow(): failed to seek into '%s'", forwardref.toString().c_str());
return false;
}
case PDS_ForwardRef:
if (!processBackRefToForwardRef(forwardref, child))
{
PDS_WARNING("processRow(): failed to processBackRefToForwardRef('%s', '%s')", forwardref.toString().c_str(), child.toString().c_str());
return false;
}
break;
default:
break;
}
}
for (i=0; i<ForwardRefInfo.size(); ++i)
{
CAutoForwardRefFiller& fref = ForwardRefInfo[i];
CDataAccessor fwdref(this, accessor, (RY_PDS::TColumnIndex)fref.Column->getId());
RY_PDS::CObjectIndex child;
if (!fwdref.isValid() || !fwdref.getIndex(child))
{
PDS_WARNING("processRow(): failed to get ForwardRef index");
return false;
}
// should not be null and valid
if (child.isValid() && !child.isNull())
continue;
// if potentially broken link
// add to list of rows to fix, and leave
BrokenForwardRefs.push_back(accessor.row());
break;
}
return true;
}
/*
* Process Back Reference to a set
*/
bool CTable::processBackRefToSet(CDataAccessor& parent, RY_PDS::CObjectIndex child)
{
parent.getSet().add(child);
return true;
}
/*
* Process Back Reference to a forward ref
*/
bool CTable::processBackRefToForwardRef(CDataAccessor& parent, RY_PDS::CObjectIndex child)
{
RY_PDS::CObjectIndex checkchild;
if (!parent.getIndex(checkchild))
{
PDS_WARNING("processBackRefToForwardRef(): failed to access to parent '%s'", parent.toString().c_str());
return false;
}
if (checkchild != child)
parent.setIndex(child);
return true;
}
/*
* Fill up Backward and Forward References information
*/
bool CTable::fillRefInfo()
{
uint i;
for (i=0; i<_Columns.size(); ++i)
{
CColumn& column = _Columns[i];
// generate summary of backward reference
if (column.getMetaType() == PDS_BackRef)
{
CTable* parent = _Parent->getNonConstTable((RY_PDS::TTableIndex)(column.getTypeId()));
if (parent == NULL)
return false;
const CAttribute* referenced = parent->getAttribute(column.getParent()->getReferencedAttribute());
if (referenced == NULL)
return false;
CBackRefFiller bref;
bref.Column = &column;
bref.Referenced = referenced;
bref.ParentTable = parent;
BackRefInfo.push_back(bref);
}
// generate summary of forward reference
else if (column.getMetaType() == PDS_ForwardRef)
{
const CAttribute* parent = column.getParent();
// check column in array of ref that are allowed to contain null
if (parent->getMetaType() == PDS_ArrayRef &&
!parent->allowNull())
{
CAutoForwardRefFiller fref;
fref.Column = &column;
ForwardRefInfo.push_back(fref);
}
}
}
return true;
}
/*
* Fix broken forward refs
*/
bool CTable::fixForwardRefs()
{
if (BrokenForwardRefs.empty())
return true;
uint i;
for (i=0; i<BrokenForwardRefs.size(); ++i)
{
RY_PDS::TRowIndex row = BrokenForwardRefs[i];
if (!fixRowForwardRefs(row))
{
PDS_WARNING("fixForwardRefs(): failed to fix forward refs in row '%d' in '%s'", row, _Name.c_str());
}
}
// clean up...
BrokenForwardRefs.clear();
return true;
}
/*
* Fix row broken forward refs
*/
bool CTable::fixRowForwardRefs(RY_PDS::TRowIndex row)
{
// get row and examine all forward refs
CTableBuffer::CAccessor accessor = _TableBuffer.getRow(row);
uint j;
for (j=0; j<ForwardRefInfo.size(); ++j)
{
CAutoForwardRefFiller &colInfo = ForwardRefInfo[j];
CDataAccessor forwardref(this, accessor, (RY_PDS::TColumnIndex)colInfo.Column->getId());
RY_PDS::CObjectIndex index;
if (!forwardref.isValid() || !forwardref.getIndex(index))
{
PDS_WARNING("fixRowForwardRefs(): failed to get forward ref '%s', data left as is", forwardref.toString().c_str());
continue;
}
// was ref fixed?
if (index.isValid())
continue;
// allocate new row
CTable *childTable = _Parent->getNonConstTable((RY_PDS::TTableIndex)(colInfo.Column->getTypeId()));
if (childTable == NULL || !childTable->initialised())
{
PDS_WARNING("fixRowForwardRefs(): failed to get child table '%d'", colInfo.Column->getTypeId());
continue;
}
RY_PDS::TRowIndex alloc = childTable->nextUnallocatedRow();
if (!childTable->allocate(alloc))
{
PDS_WARNING("fixRowForwardRefs(): failed to get allocate free row in table '%s'", childTable->getName().c_str());
continue;
}
// get an accessor on row key
CTableBuffer::CAccessor allocAccess = childTable->_TableBuffer.getRow(alloc);
CDataAccessor keyAccess(childTable, allocAccess, (RY_PDS::TColumnIndex)(childTable->getAttribute(childTable->getKey())->getOffset()));
if (!keyAccess.isValid())
{
PDS_WARNING("fixRowForwardRefs(): failed to get key access of row '%s:%d'", childTable->getName().c_str(), alloc);
childTable->_TableBuffer.releaseRow(allocAccess);
continue;
}
// compute key
TEnumValue keyValue = forwardref.column()->getId()-forwardref.attribute()->getOffset();
// set new row key
if (!keyAccess.setAsIndexType(keyValue))
{
PDS_WARNING("fixRowForwardRefs(): failed to set key '%s'", keyAccess.toString().c_str());
childTable->_TableBuffer.releaseRow(allocAccess);
continue;
}
CDataAccessor backref(keyAccess, (RY_PDS::TColumnIndex)(childTable->getAttribute(forwardref.attribute()->getReferencedAttribute())->getOffset()));
// set back&forward links
forwardref.setIndex(backref.getObjectIndex());
backref.setIndex(forwardref.getObjectIndex());
// fixup links in new child
if (!childTable->fixRowForwardRefs(alloc))
{
PDS_WARNING("fixRowForwardRefs(): failed to set fix forward refs in newly allocated row '%s'", backref.getObjectIndex().toString().c_str());
childTable->_TableBuffer.releaseRow(allocAccess);
continue;
}
childTable->_TableBuffer.releaseRow(allocAccess);
}
_TableBuffer.releaseRow(accessor);
return true;
}
/*
* Dump accessor content and info to xml
*/
void CTable::CDataAccessor::dumpToXml(NLMISC::IStream& xml, sint expandDepth)
{
if (xml.isReading())
return;
xml.xmlPushBegin("value");
xml.xmlSetAttrib("valid");
bool valid = isValid();
xml.serial(valid);
bool closeTag = true;
if (valid)
{
xml.xmlSetAttrib("name");
std::string columnName = _Column->getName();
xml.serial(columnName);
xml.xmlSetAttrib("type");
std::string typeName = getNameFromDataType(_Column->getDataType());
xml.serial(typeName);
std::string value;
xml.xmlSetAttrib("value");
switch (_Column->getDataType())
{
case PDS_bool:
xml.serial(*(bool*)_Data);
break;
case PDS_char:
xml.serial(*(char*)_Data);
break;
case PDS_uint8:
xml.serial(*(uint8*)_Data);
break;
case PDS_ucchar:
case PDS_uint16:
xml.serial(*(uint16*)_Data);
break;
case PDS_CSheetId:
case PDS_CNodeId:
case PDS_uint32:
xml.serial(*(uint32*)_Data);
break;
case PDS_uint64:
xml.serial(*(uint64*)_Data);
break;
case PDS_sint8:
xml.serial(*(sint8*)_Data);
break;
case PDS_sint16:
xml.serial(*(sint16*)_Data);
break;
case PDS_sint32:
xml.serial(*(sint32*)_Data);
break;
case PDS_sint64:
xml.serial(*(sint64*)_Data);
break;
case PDS_float:
xml.serial(*(float*)_Data);
break;
case PDS_double:
xml.serial(*(double*)_Data);
break;
case PDS_CEntityId:
{
std::string id = ((CEntityId*)_Data)->toString();
xml.serial(id);
}
break;
case PDS_enum:
{
std::string value;
const CType* type = _Table->getParent()->getType(_Column->getTypeId());
if (type == NULL || !type->isEnum())
{
xml.serial(*(uint32*)_Data);
}
else
{
std::string name = type->getIndexName(*(uint32*)_Data);
xml.serial(name);
}
}
break;
case PDS_dimension:
{
if (_Column->getByteSize() == 1)
{
xml.serial(*(uint8*)_Data);
}
else if (_Column->getByteSize() == 2)
{
xml.serial(*(uint16*)_Data);
}
else if (_Column->getByteSize() == 4)
{
xml.serial(*(uint32*)_Data);
}
else
{
std::string unknown = "non displayable";
xml.serial(unknown);
}
}
break;
case PDS_List:
{
std::string value = "list";
xml.serial(value);
xml.xmlPushEnd();
closeTag = false;
if (expandDepth != 0)
{
const RY_PDS::TIndexList& list = getSet().get();
if (!list.empty())
{
uint i;
for (i=0; i<list.size(); ++i)
_Table->_Parent->dumpToXml(list[i], xml, expandDepth-1);
}
}
}
break;
case PDS_Index:
{
std::string value = ((RY_PDS::CObjectIndex*)_Data)->toString(_Table->getParent());
xml.serial(value);
xml.xmlPushEnd();
closeTag = false;
if (expandDepth != 0 && _Attribute->getMetaType() != PDS_BackRef)
_Table->_Parent->dumpToXml(*(RY_PDS::CObjectIndex*)_Data, xml, expandDepth-1);
}
break;
default:
{
std::string unknown = "non displayable";
xml.serial(unknown);
}
break;
}
}
if (closeTag)
xml.xmlPushEnd();
xml.xmlPop();
}
/*
* Dump accessor content and info to xml
*/
void CTable::dumpToXml(RY_PDS::TRowIndex row, NLMISC::IStream& xml, sint expandDepth)
{
if (xml.isReading())
return;
xml.xmlPushBegin("object");
xml.xmlSetAttrib("valid");
bool valid = initialised();
xml.serial(valid);
if (initialised())
{
xml.xmlSetAttrib("name");
xml.serial(_Name);
xml.xmlSetAttrib("index");
RY_PDS::CObjectIndex index((RY_PDS::TTableIndex)_Id, (RY_PDS::TRowIndex)row);
std::string indexName = index.toString(_Parent);
xml.serial(indexName);
CTableBuffer::CAccessor rowaccess = _TableBuffer.getRow(row);
bool rowAllocated = rowaccess.allocated();
xml.xmlSetAttrib("allocated");
xml.serial(rowAllocated);
xml.xmlPushEnd();
if (rowAllocated)
{
uint i;
for (i=0; i<_Columns.size(); ++i)
{
CDataAccessor accessor(this, rowaccess, i);
const CColumn &col = _Columns[i];
accessor.dumpToXml(xml, expandDepth);
}
}
_TableBuffer.releaseRow(rowaccess);
}
else
{
xml.xmlPushEnd();
}
xml.xmlPop();
}