// Ryzom - MMORPG Framework // 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 . #ifndef RY_PDS_TABLE_H #define RY_PDS_TABLE_H // // NeL includes // #include #include // // STL includes // #include // // PDS includes // #include "pds_database.h" #include "pds_type.h" #include "pds_attribute.h" #include "pds_column.h" // // PDS Lib includes // #include "../pd_lib/pd_lib.h" #include "../pd_lib/pds_common.h" #include "../pd_lib/pds_types.h" #include "../pd_lib/pd_server_utils.h" #include "../pd_lib/db_reference_file.h" #include "../pd_lib/pds_table_buffer.h" class IDbFileStream; class CDatabase; class CTableNode; //#define DEBUG_DATA_ACCESSOR /** * A table able to update values into it * \author Benjamin Legros * \author Nevrax France * \date 2004 */ class CTable : public IRowProcessor, public CPDSLogger { public: /** * Constructor */ CTable(); /** * Destructor */ ~CTable(); /** * Clear table */ void clear(); /** * Init table * \return true iff success */ bool init(CDatabase *database, const CTableNode& table); /// Initialized yet? bool initialised() const { return _Init; } /** * Post Init, called once all tables have been initialised */ bool postInit(); /** * Build columns */ bool buildColumns(); /** * Get parent Database */ const CDatabase* getParent() const { return _Parent; } /** * Get parent Database */ CDatabase* getParent() { return _Parent; } /** * Get name */ const std::string& getName() const { return _Name; } /** * Get Id */ TTypeId getId() const { return _Id; } /** * Get Class Key */ uint32 getKey() const { return _Key; } /** * Get inherited table */ TTypeId getInheritTable() const { return _Inheritance; } /** * Get Attributes */ const std::vector& getAttributes() const { return _Attributes; } /** * Get Attribute */ const CAttribute* getAttribute(uint32 attribute) const { return attribute < _Attributes.size() ? _Attributes[attribute] : NULL; } /** * Get Attribute */ const CAttribute* getAttribute(const std::string &name) const; /** * Get Columns */ const std::vector& getColumns() const { return _Columns; } /** * Get Column */ const CColumn* getColumn(uint32 column) const { return column < _Columns.size() && _Columns[column].initialised() ? &_Columns[column] : NULL; } /** * Get Column */ const CColumn* getColumn(CLocatePath &path, bool verbose = true) const; /// \name Row Management // @{ /** * Allocate a row in a table * \param row is the row to allocate * Return true if succeded */ bool allocate(RY_PDS::TRowIndex row, bool acquireRow = false); /** * Deallocate a row in a table * \param row is the row to deallocate * Return true if succeded */ bool deallocate(RY_PDS::TRowIndex row); /** * Tells if a row is allocated * \param row is the row to check */ bool isAllocated(RY_PDS::TRowIndex row) const; /** * Is Table Mapped */ bool isMapped() const { return _Mapped; } /** * Map a row * \param index is the index to map * \param key is the 64 bits row key * Return true if succeded */ bool mapRow(const RY_PDS::CObjectIndex &index, uint64 key); /** * Unmap a row in a table * \param key is the 64 bits row key * Return true if succeded */ bool unmapRow(uint64 key); /** * Release a row in table * \param row is the row to release * Return true if succeded */ bool release(RY_PDS::TRowIndex row); /** * Release all rows in table */ bool releaseAll(); /** * Get a mapped row * \param key is the 64 bits row key * Return a valid CObjectIndex if success */ RY_PDS::CObjectIndex getMappedRow(uint64 key) const; /** * Get the next unallocated row index in table */ RY_PDS::TRowIndex nextUnallocatedRow() const { return _TableBuffer.nextUnallocatedRow(); } /** * Get memory load */ uint32 getMemoryLoad() const { return _TableBuffer.getMemoryLoad(); } // @} /** * Set value */ bool set(RY_PDS::TRowIndex row, RY_PDS::TColumnIndex column, uint datasize, const void* dataptr); /** * Set Parent */ bool setParent(RY_PDS::TRowIndex row, RY_PDS::TColumnIndex column, const RY_PDS::CObjectIndex& parent); /** * Get value */ bool get(RY_PDS::TRowIndex row, RY_PDS::TColumnIndex column, uint& datasize, void* dataptr, TDataType& type); /** * 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 fetch(RY_PDS::TRowIndex row, RY_PDS::CPData &data, bool fetchIndex = true); /** * Build the index allocator for this table */ bool buildIndexAllocator(RY_PDS::CIndexAllocator& alloc); public: /** * A data accessor, that handles embedded access to an object' value */ class CDataAccessor { public: /** * Default constructor, this accessor is invalid */ #ifdef DEBUG_DATA_ACCESSOR CDataAccessor() : _IsValid(false), _Table(NULL), _Attribute(NULL), _Column(NULL), _Data(NULL), _IsDebug(false) {} #else CDataAccessor() : _IsValid(false), _Table(NULL), _Attribute(NULL), _Column(NULL), _Data(NULL) {} #endif /** * Destructor */ ~CDataAccessor(); /// Is accessor valid? bool isValid() const { return _IsValid; } /** * Get data as CObjectIndex * Return false if something went wrong (mainly, CDataAccessor is not valid, or pointed data is not an CObjectIndex) */ bool getIndex(RY_PDS::CObjectIndex &index) const; /** * Set data as CObjectIndex * Return false if something went wrong (mainly, CDataAccessor is not valid, or pointed data is not an CObjectIndex) */ bool setIndex(const RY_PDS::CObjectIndex &index); /** * Get data as a Set * Return an accessor on a set, which is valid only if not issue occured * Thus, you are able to modify the list own your own, add/remove items... */ RY_PDS::CSetMap::CAccessor getSet(); /** * Get data as a Set * Return an accessor on a set, which is valid only if not issue occured */ const RY_PDS::CSetMap::CAccessor getSet() const; /** * Get data as simple type value * \param dataptr is the destination databuffer * \param datasize is the destination buffer size * Return false if something went wrong (mainly, CDataAccessor is not valid, or pointed data is not an simple type) * If datasize doesn't match, data are loaded anyway, but shrinked or truncated, and a warning is issued */ bool getValue(void* dataptr, uint32 datasize) const; /** * Set data as simple type value * \param dataptr is the source databuffer * \param datasize is the source buffer size * Return false if something went wrong (mainly, CDataAccessor is not valid, or pointed data is not an simple type) * If datasize doesn't match, data are stored anyway, but shrinked or truncated, and a warning is issued */ bool setValue(const void* dataptr, uint32 datasize); /** * Get as enum/dimension */ bool getAsIndexType(uint32& value) const; /** * Set as enum/dimension */ bool setAsIndexType(uint32 value); /** * Get Back Ref Key value */ bool getBackRefKey(uint32& key); /** * Fetch column data into stream */ bool fetch(RY_PDS::CPData &data); /** * Ask Parent Table to release row */ //bool releaseRow(); /** * Acquire row */ void acquire(); /** * Unacquire row */ void unacquire(); /** * toString() */ std::string toString() const { return isValid() ? (_Table->getName()+":"+NLMISC::toString(_Accessor.row())+":"+_Column->getName()) : ""; } /** * Get table */ CTable* table() { return isValid() ? _Table : NULL; } /** * Get attribute */ const CAttribute* attribute() const { return isValid() ? _Attribute : NULL; } /** * Get column */ const CColumn* column() const { return isValid() ? _Column : NULL; } /** * Get Row */ RY_PDS::TRowIndex row() const { return isValid() ? _Accessor.row() : RY_PDS::INVALID_ROW_INDEX; } /** * Perform column integrity check */ bool check() const; /** * Get value as string */ std::string valueAsString(bool expandSet = false) const; /** * Get Column Index * Return the Column Index of the column accessed */ RY_PDS::CColumnIndex getColumnIndex() const { return isValid() ? RY_PDS::CColumnIndex((RY_PDS::TTableIndex)_Table->getId(), _Accessor.row(), (RY_PDS::TColumnIndex)_Column->getId()) : RY_PDS::CColumnIndex(); } /** * Get Object Index * Return the Object Index of the whole row accessed */ RY_PDS::CObjectIndex getObjectIndex() const { return isValid() ? RY_PDS::CObjectIndex((RY_PDS::TTableIndex)_Table->getId(), _Accessor.row()) : RY_PDS::CObjectIndex(); } /** * Dump accessor content and info to xml */ void dumpToXml(NLMISC::IStream& xml, sint expandDepth = -1); private: /** * Constructor * Build a data accessor from a table, a row and an attribute id * This constructor doesn't support Class and ArrayClass attributes * \param object is the CObjectIndex of the row to peek/poke * \param attribute is the attribute in the row * \param index is the array index if the attribute is an array, set to 0 for other type */ CDataAccessor(CDatabase* root, const RY_PDS::CObjectIndex& object, uint32 attribute, TEnumValue arrayIndex); /** * Constructor * Build a data accessor from a table, a row and an column id * This constructor supports Class and ArrayClass attributes * \param table is the table peek/poke into * \param row is index to access * \param column in the row */ CDataAccessor(CDatabase* root, const RY_PDS::CObjectIndex& object, RY_PDS::TColumnIndex column); /** * Constructor * Build a data accessor from a table, a data accessor and a column index */ CDataAccessor(CTable* table, CTableBuffer::CAccessor& data, RY_PDS::TColumnIndex column); /** * Constructor * Build a data accessor from another accessor and a column index. * Used to access another field in a row */ CDataAccessor(const CDataAccessor& accessor, RY_PDS::TColumnIndex column); #ifdef DEBUG_DATA_ACCESSOR /** * Constructor * Build a **debug** data accessor from row data and a column index. */ CDataAccessor(CTable* table, uint8* data, RY_PDS::TColumnIndex column); #endif /** * Seek to array index * \param seek index in the array previously setup * Seek doesn't apply to ArrayClass attributes * Return true if success, false if not and accessor is invalidated */ bool seek(TEnumValue index); /** * Seek to array index * \param backref is the back reference associated the current forward reference * Seek doesn't apply to ArrayClass attributes * Return true if success, false if not and accessor is invalidated */ bool seek(CDataAccessor& backref); /** * Check type of an index */ bool checkType(const RY_PDS::CObjectIndex &object) const; /** * Check an accessor as a PDS_Type accessor */ bool checkAsTypeAccessor() const; /** * Check an accessor as a PDS_BackRef or PDS_ForwardRef accessor */ bool checkAsRefAccessor() const; /** * Check an accessor as a PDS_Set accessor */ bool checkAsSetAccessor() const; /** * Dirty row * Mark the whole row as modified, and to be stored at next delta backup time */ bool dirtyRow(); /** * Setup Row Accessor */ bool setupAccessor(RY_PDS::TRowIndex row); /** * Invalidate accessor */ void invalidate(); /// Table to look into CTable* _Table; /// Attribute const CAttribute* _Attribute; /// Column const CColumn* _Column; /// Row data accessor CTableBuffer::CAccessor _Accessor; /// Pointer to object data (start pointer, in case of array) uint8* _Data; /// Is valid bool _IsValid; #ifdef DEBUG_DATA_ACCESSOR /// Is debug bool _IsDebug; #endif friend class CTable; }; protected: virtual std::string getLoggerIdentifier() const { return NLMISC::toString("tab:%s", (_Name.empty() ? "" : _Name.c_str())); } public: /** * Get accessor on data from a path */ CDataAccessor getAccessor(CLocatePath &path); /// Display void display(NLMISC::CLog *log = NLMISC::InfoLog, bool expanded = false, bool displayHeader = false) const; /// Display row void displayRow(RY_PDS::TRowIndex row, NLMISC::CLog *log = NLMISC::InfoLog, bool displayHeader = false); /// Display row void displayValue(RY_PDS::TRowIndex row, NLMISC::CLog *log = NLMISC::InfoLog); /// Dump Delta file content void dumpDeltaFileContent(const std::string& filename, NLMISC::CLog *log = NLMISC::InfoLog) const; /** * Dump table content and info at row to xml */ void dumpToXml(RY_PDS::TRowIndex row, NLMISC::IStream& xml, sint expandDepth = -1); /// Rebuild forwardrefs from backrefs bool rebuildForwardRefs(); /// Reset forwardrefs bool resetForwardRefs(); /// Reset table map bool resetTableMap(); /// Rebuild table map bool rebuildTableMap(); /** * Reset dirty tags * Reset all rows so no one is marked as being dirty. * This method fixes broken list issues */ bool resetDirtyTags(); /** * Preload reference files */ bool preloadRefFiles(); /** * Notify new Reference * Internal table reference is reset and table is flushed from its released rows */ bool notifyNewReference(CRefIndex& newref); /** * Apply delta changes from a file */ bool applyDeltaChanges(const std::string& filename); /** * Build the delta file and purge all dirty rows in this table */ bool buildDelta(const CTimestamp& starttime, const CTimestamp& endtime); /** * Flush table from released rows */ bool flushReleased(); /** * Build RowMapper */ bool buildRowMapper(); /** * Process Row */ virtual bool processRow(RY_PDS::TTableIndex table, CTableBuffer::CAccessor& accessor); /** * Fix broken forward refs */ bool fixForwardRefs(); /** * Fix row broken forward refs * For each forward ref that should not contain null index, allocate a new row in * the child table, then setup back and forward references */ bool fixRowForwardRefs(RY_PDS::TRowIndex row); private: /// Initialised yet? bool _Init; /// Name of the table std::string _Name; /// Id of the table TTypeId _Id; /// Inheritance of the table TTypeId _Inheritance; /// Parent database CDatabase* _Parent; /// Attributes of the table std::vector _Attributes; /// Columns of the table std::vector _Columns; /// Key Attribute TTypeId _Key; /// Table buffer CTableBuffer _TableBuffer; /// Size of a row uint32 _RowSize; /// Table is mapped bool _Mapped; /// Empty Row pattern std::vector _EmptyRow; private: friend class CDataAccessor; friend class CAttribute; friend class CDatabaseAdapter; /** * Reset row to initial value */ bool resetRow(uint8* rowData); /** * Get Root Inheritance Table */ CTable* getRootTable(); /** * Get Root Inheritance Table */ const CTable* getRootTable() const; /** * 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 * \param child is a remember of the child index */ bool link(CDataAccessor &backref, const RY_PDS::CObjectIndex &parent, const RY_PDS::CObjectIndex &child); /** * 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 forwardLink(CDataAccessor &backref, CDataAccessor &forwardref, const RY_PDS::CObjectIndex &child); /** * Unlink a BackRef * This method will perfom a full unlinking of child and current parent * \param backref is an accessor on the BackRef * \param child is a remember of the child index */ bool unlink(CDataAccessor &backref, const RY_PDS::CObjectIndex &child); /** * unlink a ForwardRef to a child * This method will only unlink parent of child * \param forwardref is an accessor on the BackRef * \param child is an index on the child to link */ bool forwardUnlink(CDataAccessor &backref, CDataAccessor &forwardref, const RY_PDS::CObjectIndex &child); /** * Clear dirty list * Reset dirty rows in the table. Only works properly if list is not broken */ bool clearDirtyList(); struct CBackRefFiller { const CColumn* Column; const CAttribute* Referenced; CTable* ParentTable; }; std::vector BackRefInfo; struct CAutoForwardRefFiller { const CColumn* Column; }; std::vector ForwardRefInfo; std::vector BrokenForwardRefs; /** * Fill up Backward and Forward References information */ bool fillRefInfo(); /** * Process Back Reference to a set */ bool processBackRefToSet(CDataAccessor& parent, RY_PDS::CObjectIndex child); /** * Process Back Reference to a set */ bool processBackRefToForwardRef(CDataAccessor& parent, RY_PDS::CObjectIndex child); }; // include inlines #include "pds_table_inline.h" #endif //RY_PDS_TABLE_H