1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "../pd_lib/pds_common.h"
19 #include "pds_database.h"
20 #include "pds_table.h"
22 #include "database_adapter.h"
24 #include "db_manager.h"
26 #include "pd_lib/pd_server_utils.h"
27 #include "pd_lib/reference_builder.h"
28 #include "pd_lib/delta_builder.h"
30 #include <nel/misc/debug.h>
31 #include <nel/misc/path.h>
32 #include <nel/misc/config_file.h>
33 #include <nel/misc/file.h>
34 #include <nel/misc/variable.h>
35 #include <nel/misc/o_xml.h>
36 #include <nel/misc/i_xml.h>
38 #include <nel/net/service.h>
41 using namespace NLMISC
;
42 using namespace NLNET
;
44 // Hosted by db_manager.cpp
45 extern NLMISC::CVariable
<uint
> DeltaUpdateRate
;
50 CDatabase::CDatabase(uint32 id
) : _Reference(id
)
60 CDatabase::~CDatabase()
62 PDS_FULL_DEBUG("delete()");
69 * Massive Database clear
71 void CDatabase::clear()
75 for (i
=0; i
<_Types
.size(); ++i
)
77 if (_Types
[i
] != NULL
)
84 for (i
=0; i
<_Tables
.size(); ++i
)
86 if (_Tables
[i
] != NULL
)
94 //_ObjectList.clear();
97 _ServiceId
.set(0xffff);
103 * \param xmlStream is the xml description of the database
105 bool CDatabase::init()
109 // clear all before doing anything
112 const CDatabaseNode
& db
= _Description
.getDatabaseNode();
114 _State
.Name
= db
.Name
;
117 for (i
=0; i
<db
.Types
.size(); ++i
)
119 CType
* type
= new CType
;
121 if (!type
->init(this, db
.Types
[i
]))
123 PDS_WARNING("init(): failed to init type '%d'", i
);
127 if (type
->getId() != i
)
129 PDS_WARNING("init(): failed to init type '%d'", i
);
133 _Types
.push_back(type
);
136 for (i
=0; i
<db
.Tables
.size(); ++i
)
138 CTable
* table
= new CTable();
140 if (!table
->init(this, db
.Tables
[i
]) || table
->getId() != i
)
142 PDS_WARNING("init(): failed to init table '%d'", i
);
146 _Tables
.push_back(table
);
149 for (i
=0; i
<_Tables
.size(); ++i
)
151 CTable
* table
= _Tables
[i
];
153 if (!table
->buildColumns())
155 PDS_WARNING("init(): failed to build table '%d' columns", i
);
160 for (i
=0; i
<_Tables
.size(); ++i
)
162 _Tables
[i
]->postInit();
167 if (!notifyNewReference(false))
173 // save description in log dir
174 std::string logDir
= RY_PDS::CPDSLib::getLogDirectory(_State
.Id
);
175 if (!CFile::isExists(logDir
) || !CFile::isDirectory(logDir
))
177 if (!CFile::createDirectoryTree(logDir
))
179 PDS_WARNING("init(): failed to create log root directory '%s'", logDir
.c_str());
182 if (!CFile::setRWAccess(logDir
))
184 PDS_WARNING("init(): failed, can't set RW access to directory '%s'", logDir
.c_str());
188 // CTimestamp initDate;
189 // initDate.setToCurrent();
190 // _Description.saveDescription(logDir + initDate.toString() + ".description");
192 PDS_DEBUG("init() successful");
203 bool CDatabase::checkup()
205 PDS_DEBUG("checkup()");
209 PDS_WARNING("checkup(): database not initialised");
216 for (i
=0; i
<_Types
.size(); ++i
)
218 if (_Types
[i
] == NULL
|| !_Types
[i
]->initialised())
220 PDS_WARNING("checkup(): type '%d' not initialised", i
);
225 for (i
=0; i
<_Tables
.size(); ++i
)
227 if (_Tables
[i
] == NULL
|| !_Tables
[i
]->initialised())
229 PDS_WARNING("checkup(): table '%d' not initialised", i
);
233 CTable
* table
= _Tables
[i
];
235 const std::vector
<CColumn
> &columns
= table
->getColumns();
236 const std::vector
<CAttribute
*> &attributes
= table
->getAttributes();
240 // walk through attributes
241 for (j
=0; j
<attributes
.size(); ++j
)
243 if (attributes
[j
] == NULL
|| !attributes
[j
]->initialised())
245 PDS_WARNING("checkup(): attribute '%s:%d' not initialised", table
->getName().c_str(), i
);
250 const CAttribute
* attribute
= attributes
[j
];
252 if (attribute
->getParent() != table
)
254 PDS_WARNING("checkup(): attribute '%s:%s' doesn't point table '%d'", table
->getName().c_str(), attribute
->getName().c_str(), i
);
260 for (k
=attribute
->getOffset(); k
<attribute
->getOffset()+attribute
->getColumns(); ++k
)
262 if (k
>= columns
.size() || !columns
[k
].initialised())
264 PDS_WARNING("checkup(): attribute '%s:%s' points to not initialised column '%d'", table
->getName().c_str(), attribute
->getName().c_str(), k
);
267 else if (columns
[k
].getParent() != attribute
)
269 PDS_WARNING("checkup(): attribute '%s:%s' points to column '%s' that doesn't point back", table
->getName().c_str(), attribute
->getName().c_str(), columns
[k
].getName().c_str());
274 const CColumn
&column
= columns
[k
];
280 // walk through columns
281 for (j
=0; j
<columns
.size(); ++j
)
283 if (!columns
[j
].initialised())
285 PDS_WARNING("checkup(): column '%s:%d' not initialised", table
->getName().c_str(), j
);
294 PDS_DEBUG("checkup(): Database is correct");
297 PDS_DEBUG("checkup() finished");
303 * Initialise internal timestamps
305 void CDatabase::initTimestamps()
307 // initialise update and creation timestamps to now
308 _CreationTimestamp
.setToCurrent();
309 _MinuteUpdateTimestamp
= _CreationTimestamp
;
310 _HourUpdateTimestamp
= _CreationTimestamp
;
311 _DayUpdateTimestamp
= _CreationTimestamp
;
317 * Get value as a string
318 * \param path is of the form '[tableindex|tablename].[$key|index].attrib1.attrib2'
320 string
CDatabase::getValue(const CLocatePath::TLocatePath
&path
)
324 PDS_WARNING("getValue(): path is too short");
333 if (sscanf(path
[node
].Name
.c_str(), "%d", &tableId
) == 1)
334 table
= getTable(tableId
);
336 table
= getTable(path
[node
].Name
);
340 PDS_WARNING("getValue(): unable to select table '%s'", path
[node
].Name
.c_str());
347 RY_PDS::CObjectIndex object
;
349 if (path
[node
].Name
[0] == '$')
352 if (sscanf(path
[node
].Name
.c_str()+1, "%" NL_I64
"X", &key
) != 1)
354 PDS_WARNING("getValue(): unable to select mapped row '%s'", path
[node
].Name
.c_str());
358 object
= table
->getMappedRow(key
);
362 RY_PDS::TRowIndex row
;
364 if (sscanf(path
[node
].Name
.c_str(), "%u", &row
) != 1)
366 PDS_WARNING("getValue(): unable to select row '%s'", path
[node
].Name
.c_str());
370 object
= RY_PDS::CObjectIndex((RY_PDS::TTableIndex
)table
->getId(), row
);
373 if (!object
.isValid() || !isAllocated(object
))
375 PDS_WARNING("getValue(): object '%s' not accessible", path
[node
].Name
.c_str());
381 const CTable
* subTable
= table
;
383 // browse through row
384 while (node
< path
.size())
386 const CAttribute
* attribute
= subTable
->getAttribute(path
[node
].Name
);
388 if (attribute
== NULL
)
390 PDS_WARNING("getValue(): '%s' is not an attribute of '%s'", path
[node
].Name
.c_str(), subTable
->getName().c_str());
408 const CTable
* CDatabase::getTable(const std::string
&name
) const
411 for (i
=0; i
<_Tables
.size(); ++i
)
412 if (_Tables
[i
] != NULL
&& _Tables
[i
]->getName() == name
)
421 const CType
* CDatabase::getType(const std::string
&name
) const
424 for (i
=0; i
<_Types
.size(); ++i
)
425 if (_Types
[i
] != NULL
&& _Types
[i
]->getName() == name
)
435 const CAttribute
* CDatabase::getAttribute(uint32 tableId
, uint32 attributeId
) const
437 const CTable
* table
= getTable(tableId
);
442 return table
->getAttribute(attributeId
);
448 const CColumn
* CDatabase::getColumn(uint32 tableId
, uint32 columnId
) const
450 const CTable
* table
= getTable(tableId
);
455 return table
->getColumn(columnId
);
462 * Allocate a row in a table
463 * \param index is the table/row to allocate
464 * Return true if succeded
466 bool CDatabase::allocate(const RY_PDS::CObjectIndex
&index
)
468 H_AUTO(PDS_Database_allocate
);
472 PDS_WARNING("allocate(): database not initialised");
476 if (!index
.isValid())
478 PDS_WARNING("allocate(): index '%s' is not valid", index
.toString().c_str());
482 CTable
*table
= getNonConstTable(index
.table());
484 if (table
== NULL
|| !table
->initialised())
486 PDS_WARNING("allocate(): table '%d' is not initialised", index
.table());
490 bool success
= table
->allocate(index
.row());
493 PDS_FULL_DEBUG("allocated '%s' successfully", index
.toString().c_str());
499 * Deallocate a row in a table
500 * \param index is the table/row to deallocate
501 * Return true if succeded
503 bool CDatabase::deallocate(const RY_PDS::CObjectIndex
&index
)
505 H_AUTO(PDS_Database_deallocate
);
509 PDS_WARNING("deallocate(): database not initialised");
513 if (!index
.isValid())
515 PDS_WARNING("deallocate(): index '%s' is not valid", index
.toString().c_str());
519 CTable
*table
= getNonConstTable(index
.table());
521 if (table
== NULL
|| !table
->initialised())
523 PDS_WARNING("deallocate(): table '%d' is not initialised", index
.table());
527 bool success
= table
->deallocate(index
.row());
530 PDS_FULL_DEBUG("deallocated '%s' successfully", index
.toString().c_str());
536 * Tells if an object is allocated
537 * \param object is the object index to test
539 bool CDatabase::isAllocated(const RY_PDS::CObjectIndex
&index
) const
541 const CTable
* table
= getTable(index
.table());
543 return table
!= NULL
&& table
->isAllocated(index
.row());
548 * Map a row in a table
549 * \param index is the table/row to allocate
550 * \param key is the 64 bits row key
551 * Return true if succeded
553 bool CDatabase::mapRow(const RY_PDS::CObjectIndex
&index
, uint64 key
)
557 PDS_WARNING("mapRow(): database not initialised");
561 if (!index
.isValid())
563 PDS_WARNING("mapRow(): index '%s' is not valid", index
.toString().c_str());
567 CTable
*table
= getNonConstTable(index
.table());
569 if (table
== NULL
|| !table
->initialised())
571 PDS_WARNING("deallocate(): table '%d' is not initialised", index
.table());
575 bool success
= table
->mapRow(index
, key
);
578 PDS_FULL_DEBUG("mapped '%016" NL_I64
"X' to '%s' successfully", key
, index
.toString().c_str());
584 * Unmap a row in a table
585 * \param tableIndex is the table to find row
586 * \param key is the 64 bits row key
587 * Return true if succeded
589 bool CDatabase::unmapRow(RY_PDS::TTableIndex tableIndex
, uint64 key
)
593 PDS_WARNING("unmapRow(): database not initialised");
597 CTable
*table
= getNonConstTable(tableIndex
);
599 if (table
== NULL
|| !table
->initialised())
601 PDS_WARNING("deallocate(): table '%d' is not initialised", tableIndex
);
605 bool success
= table
->unmapRow(key
);
608 PDS_FULL_DEBUG("unmapped '%016" NL_I64
"X' successfully", key
);
615 * \param tableIndex is the table in which the row is mapped
616 * \param key is the 64 bits row key
617 * Return a valid CObjectIndex if success
619 RY_PDS::CObjectIndex
CDatabase::getMappedRow(RY_PDS::TTableIndex tableIndex
, uint64 key
) const
623 PDS_WARNING("getMappedRow(): database not initialised");
624 return RY_PDS::CObjectIndex();
627 const CTable
*table
= getTable(tableIndex
);
629 if (table
== NULL
|| !table
->initialised())
631 PDS_WARNING("getMappedRow(): table '%d' is not initialised", tableIndex
);
632 return RY_PDS::CObjectIndex();
635 return table
->getMappedRow(key
);
639 * Release a row in a table
640 * \param index is the table/row to release
641 * Return true if succeded
643 bool CDatabase::release(const RY_PDS::CObjectIndex
&index
)
647 PDS_WARNING("release(): database not initialised");
651 if (!index
.isValid())
653 PDS_WARNING("release(): index '%s' is not valid", index
.toString().c_str());
657 CTable
*table
= getNonConstTable(index
.table());
659 if (table
== NULL
|| !table
->initialised())
661 PDS_WARNING("release(): table '%d' is not initialised", index
.table());
665 bool success
= table
->release(index
.row());
668 PDS_FULL_DEBUG("released '%s' successfully", index
.toString().c_str());
674 * Release all rows in all table
675 * Typically, the client disconnected, there is no need to keep rows
677 bool CDatabase::releaseAll()
681 PDS_WARNING("releaseAll(): database not initialised");
686 for (i
=0; i
<_Tables
.size(); ++i
)
688 if (_Tables
[i
] != NULL
&& _Tables
[i
]->initialised())
690 _Tables
[i
]->releaseAll();
701 bool CDatabase::set(RY_PDS::TTableIndex table
, RY_PDS::TRowIndex row
, RY_PDS::TColumnIndex column
, uint datasize
, const void* dataptr
)
703 H_AUTO(PDS_Database_set
);
707 PDS_WARNING("set(): database not initialised");
711 CTable
* _table
= getNonConstTable(table
);
715 PDS_WARNING("set(): table '%d' is NULL", table
);
719 bool success
= _table
->set(row
, column
, datasize
, dataptr
);
722 PDS_FULL_DEBUG("set '%s' column '%d' successfully", RY_PDS::CObjectIndex(table
, row
).toString().c_str(), column
);
729 * Set an object parent
731 bool CDatabase::setParent(RY_PDS::TTableIndex table
, RY_PDS::TRowIndex row
, RY_PDS::TColumnIndex column
, const RY_PDS::CObjectIndex
&parent
)
733 H_AUTO(PDS_Database_setParent
);
737 PDS_WARNING("set(): database not initialised");
741 CTable
* _table
= getNonConstTable(table
);
745 PDS_WARNING("set(): table '%d' is NULL", table
);
749 bool success
= _table
->setParent(row
, column
, parent
);
752 PDS_FULL_DEBUG("set '%s' column '%d' successfully", RY_PDS::CObjectIndex(table
, row
).toString().c_str(), column
);
761 bool CDatabase::get(RY_PDS::TTableIndex table
, RY_PDS::TRowIndex row
, RY_PDS::TColumnIndex column
, uint
& datasize
, void* dataptr
, TDataType
&type
)
765 PDS_WARNING("set(): database not initialised");
769 CTable
* _table
= getNonConstTable(table
);
773 PDS_WARNING("set(): table '%d' is NULL", table
);
777 bool success
= _table
->get(row
, column
, datasize
, dataptr
, type
);
780 PDS_FULL_DEBUG("get '%s' column '%d' successfully", RY_PDS::CObjectIndex(table
, row
).toString().c_str(), column
);
791 void CDatabase::display(NLMISC::CLog
* log
, bool displayHeader
) const
795 log
->displayNL("Database not initialised");
798 log
->displayNL("Database '%s'", _State
.Name
.c_str());
799 log
->displayNL("%d types, %d tables", _Types
.size(), _Tables
.size());
803 log
->displayNL(" %-36s | %-15s | %-2s | %s", "TypeId/Name", "DataTypeId/Name", "Sz", "IsIndex");
804 for (i
=0; i
<_Types
.size(); ++i
)
806 if (_Types
[i
] == NULL
)
807 log
->display("** Type %d not initialised", i
);
809 _Types
[i
]->display(log
);
814 for (i
=0; i
<_Tables
.size(); ++i
)
816 if (_Tables
[i
] == NULL
)
818 log
->display("** Table %d not initialised", i
);
822 _Tables
[i
]->display(log
, false, first
);
829 * Dump database content and info of an object to xml
831 void CDatabase::dumpToXml(const RY_PDS::CObjectIndex
& index
, NLMISC::IStream
& xml
, sint expandDepth
)
833 if (xml
.isReading() || !initialised())
836 CTable
* table
= getNonConstTable(index
.table());
839 table
->dumpToXml(index
.row(), xml
, expandDepth
);
845 * Set value with human readable parameters
847 bool CDatabase::set(RY_PDS::TTableIndex table
, RY_PDS::TRowIndex row
, RY_PDS::TColumnIndex column
, const std::string
& type
, const std::string
&value
)
849 TDataType datatype
= getDataTypeFromName(type
);
851 if (!checkDataType(datatype
))
862 NLMISC::fromString(value
, data
);
863 return set(table
, row
, column
, sizeof(data
), &data
);
872 NLMISC::fromString(value
, data
);
873 return set(table
, row
, column
, sizeof(data
), &data
);
884 NLMISC::fromString(value
, data
);
885 return set(table
, row
, column
, sizeof(data
), &data
);
893 sscanf(value
.c_str(), "%016" NL_I64
"d", &data
);
894 return set(table
, row
, column
, sizeof(data
), &data
);
901 data
.fromString(value
.c_str());
902 return set(table
, row
, column
, sizeof(data
), &data
);
909 NLMISC::fromString(value
, data
);
910 return set(table
, row
, column
, sizeof(data
), &data
);
917 NLMISC::fromString(value
, data
);
918 return set(table
, row
, column
, sizeof(data
), &data
);
924 RY_PDS::CObjectIndex data
;
925 data
.fromString(value
.c_str());
926 return set(table
, row
, column
, sizeof(data
), &data
);
933 NLMISC::fromString(value
, data
);
934 return set(table
, row
, column
, sizeof(data
), &data
);
946 bool CDatabase::fetch(const RY_PDS::CObjectIndex
& index
, RY_PDS::CPData
&data
, bool fetchIndex
)
948 H_AUTO(PDS_Database_fetch
);
952 PDS_WARNING("set(): database not initialised");
956 CTable
* table
= getNonConstTable(index
.table());
960 PDS_WARNING("fetch(): unable to get table '%d'", index
.table());
964 bool success
= table
->fetch(index
.row(), data
, fetchIndex
);
967 PDS_FULL_DEBUG("fetch '%s' successfully", index
.toString().c_str());
976 * Build index allocators
979 bool CDatabase::buildIndexAllocators(std::vector
<RY_PDS::CIndexAllocator
> &allocators
)
981 PDS_DEBUG("buildIndexAllocators()");
985 PDS_WARNING("buildIndexAllocators(): database not initialised");
990 allocators
.resize(_Tables
.size());
993 for (i
=0; i
<_Tables
.size(); ++i
)
994 if (_Tables
[i
] != NULL
&& _Tables
[i
]->initialised())
995 _Tables
[i
]->buildIndexAllocator(allocators
[i
]);
1006 * Rebuild forwardrefs from backrefs
1008 bool CDatabase::rebuildForwardRefs()
1012 PDS_WARNING("rebuildForwardRefs(): database not initialised");
1016 // clear up object list
1019 // the rebuild forwardrefs
1021 for (i
=0; i
<_Tables
.size(); ++i
)
1023 if (_Tables
[i
] != NULL
&& _Tables
[i
]->initialised())
1025 if (!_Tables
[i
]->resetForwardRefs())
1027 PDS_WARNING("rebuildForwardRefs(): failed to resetForwardRefs(), abort");
1033 for (i
=0; i
<_Tables
.size(); ++i
)
1035 if (_Tables
[i
] != NULL
&& _Tables
[i
]->initialised())
1037 if (!_Tables
[i
]->rebuildForwardRefs())
1039 PDS_WARNING("rebuildForwardRefs(): failed to rebuildForwardRefs(), abort");
1045 for (i
=0; i
<_Tables
.size(); ++i
)
1047 if (_Tables
[i
] != NULL
&& _Tables
[i
]->initialised())
1049 if (!_Tables
[i
]->fixForwardRefs())
1051 PDS_WARNING("rebuildForwardRefs(): failed to fixForwardRefs(), abort");
1062 * Rebuild table maps
1064 bool CDatabase::rebuildTableMaps()
1068 PDS_WARNING("rebuildTableMaps(): database not initialised");
1073 for (i
=0; i
<_Tables
.size(); ++i
)
1075 if (_Tables
[i
] != NULL
&& _Tables
[i
]->initialised())
1077 _Tables
[i
]->buildRowMapper();
1087 bool CDatabase::resetDirtyTags()
1091 PDS_WARNING("resetDirtyLists(): database not initialised");
1096 for (i
=0; i
<_Tables
.size(); ++i
)
1098 if (_Tables
[i
] != NULL
&& _Tables
[i
]->initialised())
1100 _Tables
[i
]->resetDirtyTags();
1108 * Reset and rebuild all maps, references, lists...
1110 bool CDatabase::rebuildVolatileData()
1112 PDS_DEBUG("rebuildVolatileData()");
1116 PDS_WARNING("rebuildVolatileData(): database not initialised");
1120 // force database to notify a new reference is up to date
1121 notifyNewReference();
1123 if (!rebuildTableMaps())
1125 PDS_WARNING("rebuildVolatileData(): failed to rebuildTableMaps()");
1129 if (!rebuildForwardRefs())
1131 PDS_WARNING("rebuildVolatileData(): failed to rebuildForwardRefs()");
1136 if (!resetDirtyTags())
1138 PDS_WARNING("rebuildVolatileData(): failed to resetDirtyTags()");
1143 // preload data from reference files
1145 for (i
=0; i
<_Tables
.size(); ++i
)
1147 if (_Tables
[i
] != NULL
&& _Tables
[i
]->initialised())
1149 if (!_Tables
[i
]->preloadRefFiles())
1151 PDS_WARNING("rebuildVolatileData(): failed to preloadRefFiles() for table '%d'", i
);
1157 // load string manager
1158 // if (!_StringManager.load(_Reference.getPath()))
1160 // PDS_WARNING("rebuildVolatileData(): failed to load string manager");
1171 * Adapt database to new description
1172 * \param description is the latest xml description of the database
1173 * \returns true is adaptation succeded
1175 CDatabase
* CDatabase::adapt(const string
& description
)
1177 PDS_DEBUG("adapt()");
1181 PDS_WARNING("adapt(): failed, database not initialised");
1185 // get 'From' HashKey
1186 CHashKey hash1
= _Description
.getHashKey();
1187 // get 'Into' HashKey
1188 CHashKey hash2
= getSHA1((const uint8
*)(description
.c_str()), (uint32
)description
.size());
1190 // same hash, ok go on
1194 // build a clean reference if needed
1195 if (!isReferenceUpToDate())
1197 if (!buildReference())
1199 PDS_WARNING("adapt(): failed to buildReference()");
1204 // backup old reference
1205 if (!_Reference
.save(_Reference
.getPath()+"ref"))
1207 PDS_WARNING("adapt(): failed to backup reference index");
1210 // create a new destination database
1211 CDatabase
* into
= new CDatabase(_State
.Id
);
1213 if (!into
->createFromScratch(description
))
1215 PDS_WARNING("adapt(): failed to create new reference");
1220 CDatabaseAdapter adapter
;
1222 if (!adapter
.build(this, into
))
1224 PDS_WARNING("adapt(): failed to build() adapter");
1229 if (!adapter
.buildContent())
1231 PDS_WARNING("adapt(): adapter failed to buildContent()");
1236 // rebuild volatile data
1237 // that is all non persistant data that can be found from persistant data
1238 if (!into
->rebuildVolatileData())
1240 PDS_WARNING("adapt(): failed to rebuildVolatileData()");
1245 // init timestamps as database is ready to run
1246 into
->initTimestamps();
1263 * Load previous database state
1265 bool CDatabase::loadState()
1267 PDS_DEBUG("loadState()");
1271 PDS_WARNING("loadState(): cannot loadState(), database is already initialised");
1275 // check directory exists
1276 std::string directory
= _Reference
.getNominalRootPath();
1277 if (!CFile::isExists(directory
) || !CFile::isDirectory(directory
))
1282 // load database current state (or previous state if current state corrupted)
1283 if (!_State
.load(_Reference
) && !_State
.load(_Reference
, true))
1288 // get last valid reference
1289 if (!_Reference
.load())
1294 if (_Reference
.Index
!= _State
.CurrentIndex
)
1296 PDS_WARNING("loadState(): failed, Reference and State files have different current index", (_Reference
.getPath()+"description.xml").c_str());
1301 if (!_Description
.loadDescriptionFile(_Reference
.getPath()+"description.xml"))
1303 PDS_WARNING("loadState(): failed to load description file '%s'", (_Reference
.getPath()+"description.xml").c_str());
1310 PDS_WARNING("loadState(): failed to init db '%d'", _State
.Id
);
1314 PDS_DEBUG("loadState(): database '%d' init ok", _State
.Id
);
1316 // check reference is up to date
1317 if (!isReferenceUpToDate())
1319 if (!buildReference())
1321 PDS_WARNING("loadState(): failed to buildReference()");
1326 // rebuild volatile data
1327 // that is all non persistant data that can be found from persistant data
1328 if (!rebuildVolatileData())
1330 PDS_WARNING("loadState(): failed to rebuildVolatileData()");
1334 // init timestamps as database is ready to run
1342 * Check if reference is still the same
1343 * Reinit reference if reference changed
1345 bool CDatabase::checkReferenceChange()
1347 CRefIndex
ref(_State
.Id
);
1349 // load current reference
1352 PDS_WARNING("checkReferenceChange(): failed to load reference for database '%d', no change assumed", _State
.Id
);
1356 // check current database reference and current up to date reference
1357 if (ref
.Index
!= _Reference
.Index
)
1359 // check nothing went wrong
1360 if (ref
.Index
< _Reference
.Index
|| ref
.Timestamp
<= _Reference
.Timestamp
)
1362 PDS_WARNING("checkReferenceChange(): index changed (new index %d), but is lower than previous index (%d), no change assumed", ref
.Index
, _Reference
.Index
);
1366 // affect new reference
1369 notifyNewReference(false);
1378 * Check if reference is up to date
1379 * Returns true if reference is the latest valid database image
1381 bool CDatabase::isReferenceUpToDate()
1383 PDS_DEBUG("isReferenceUpToDate()");
1387 PDS_WARNING("isReferenceUpToDate(): failed, database is not initialised");
1391 vector
<string
> files
;
1393 NLMISC::CPath::getPathContent(_Reference
.getRootPath()+"hours", false, false, true, files
);
1394 NLMISC::CPath::getPathContent(_Reference
.getRootPath()+"minutes", false, false, true, files
);
1395 NLMISC::CPath::getPathContent(_Reference
.getRootPath()+"seconds", false, false, true, files
);
1397 bool upToDate
= true;
1400 for (i
=0; i
<files
.size(); ++i
)
1403 CTimestamp timestamp
;
1405 bool deltaFile
= CDBDeltaFile::isDeltaFileName(files
[i
], tableId
, timestamp
);
1406 // bool stringLogFile = RY_PDS::CPDStringManager::isLogFileName(files[i], timestamp);
1408 if (deltaFile
/*|| stringLogFile*/)
1410 if (timestamp
> _State
.EndTimestamp
)
1412 CFile::moveFile(files
[i
]+".disabled", files
[i
]);
1416 // compare file timestamp (in name) to current reference timestamp
1417 if (_Reference
.Timestamp
<= timestamp
&& timestamp
<= _State
.EndTimestamp
)
1423 PDS_DEBUG("isReferenceUpToDate(): database is up to date");
1429 * Build a up to date reference
1431 bool CDatabase::buildReference()
1433 PDS_DEBUG("buildReference()");
1435 CRefIndex prev
= _Reference
;
1436 CRefIndex
next(_State
.Id
);
1438 if (!next
.buildNext())
1440 PDS_WARNING("buildReference(): failed to build next ref");
1444 PDS_DEBUG("buildReference(): building from reference '%08X' to reference '%08X'", prev
.Index
, next
.Index
);
1446 // build reference but clamp to last valid state (
1447 next
.Timestamp
= _State
.EndTimestamp
;
1452 // this will release all references owned by the internal table buffers
1453 // so we can copy/modify reference files without problem
1455 if (!CReferenceBuilder::build(prev
, next
))
1457 PDS_WARNING("buildReference(): failed to build next reference");
1461 notifyNewReference();
1463 PDS_DEBUG("buildReference(): new reference is up to date");
1473 * Create new database from scratch, setup everything needed (references, etc.)
1474 * \param description is the xml database description
1476 bool CDatabase::createFromScratch(const string
& description
)
1478 PDS_DEBUG("createFromScratch()");
1480 // In order to create a new database from scratch, directory must no exist
1481 if (CFile::isExists(_Reference
.getNominalRootPath()))
1483 PDS_WARNING("createFromScratch(): failed, directory '%s' already exists for database '%d', manual cleanup is needed to proceed", _Reference
.getRootPath().c_str(), _Reference
.DatabaseId
);
1487 // build a brand new reference
1488 if (!_Reference
.buildNext())
1490 PDS_WARNING("createFromScratch(): failed, unable to init reference");
1496 PDS_WARNING("createFromScratch(): failed, database is initialised");
1500 if (!_Description
.loadDescription((uint8
*)description
.c_str()))
1502 PDS_WARNING("createFromScratch(): failed to load description");
1508 PDS_WARNING("createFromScratch(): failed to init() database");
1512 // // save description
1513 // if (!_Description.saveDescription(_Reference.getPath() + "description.xml"))
1515 // PDS_WARNING("createFromScratch(): failed to save description");
1519 // init timestamps as database is ready to run
1522 // a new reference is valid
1523 notifyNewReference();
1529 * Build the delta files and purge all dirty rows in tables
1531 bool CDatabase::buildDelta(const CTimestamp
& starttime
, const CTimestamp
& endtime
)
1533 H_AUTO(PDS_Database_buildDelta
);
1537 PDS_WARNING("buildDelta(): failed, database is not initialised");
1542 for (i
=0; i
<_Tables
.size(); ++i
)
1544 if (!_Tables
[i
]->buildDelta(starttime
, endtime
))
1546 PDS_WARNING("buildDelta(): failed to buildDelta() for table '%d' '%s'", i
, _Tables
[i
]->getName().c_str());
1551 // std::string logDir = RY_PDS::CPDSLib::getLogDirectory(_State.Id);
1552 // if (!CFile::isExists(logDir) || !CFile::isDirectory(logDir))
1554 // if (!CFile::createDirectoryTree(logDir))
1556 // PDS_WARNING("buildDelta(): failed to create log root directory '%s'", logDir.c_str());
1559 // if (!CFile::setRWAccess(logDir))
1561 // PDS_WARNING("buildDelta(): failed, can't set RW access to directory '%s'", logDir.c_str());
1565 // // save string manager logs
1566 // if (!_StringManager.logEmpty())
1570 // std::string smfilename = logDir + endtime.toString()+".string_log";
1571 // if (!smf.open(smfilename) || !smxml.init(&smf) || !_StringManager.storeLog(smxml))
1573 // PDS_WARNING("buildDelta(): failed to build string manager log file '%s'", smfilename.c_str());
1577 // save straight logs
1578 if (!_LogQueue
.empty())
1580 // std::string logfilename = logDir + endtime.toString()+"_0000.pd_log";
1582 // if (logf.open(logfilename))
1586 // logf.serialCont(_LogQueue);
1588 // catch (const Exception& e)
1590 // PDS_WARNING("buildDelta(): exception occurred while saving straight log : %s", e.what());
1595 // PDS_WARNING("buildDelta(): failed to build log file '%s'", logfilename.c_str());
1601 // State file swapping
1602 std::string statePath
= _Reference
.getRootPath();
1603 std::string stateName
= CDatabaseState::fileName();
1604 if (CFile::fileExists(statePath
+stateName
) &&
1605 !CFile::copyFile(statePath
+"previous_"+stateName
, statePath
+stateName
, false))
1607 PDS_WARNING("buildDelta(): failed copy state file to backup previous_state");
1610 // setup state timestamp
1611 _State
.EndTimestamp
= endtime
;
1613 _State
.save(_Reference
);
1619 * Flush database from released rows
1621 bool CDatabase::flushReleased()
1625 PDS_WARNING("flushReleased(): failed, database is not initialised");
1630 for (i
=0; i
<_Tables
.size(); ++i
)
1632 if (!_Tables
[i
]->flushReleased())
1634 PDS_WARNING("flushReleased(): failed to flushReleased() for table '%d' '%s'", i
, _Tables
[i
]->getName().c_str());
1645 * Notify a new reference is ready
1647 bool CDatabase::notifyNewReference(bool validateRef
)
1651 PDS_WARNING("notifyNewReference(): failed, database is not initialised");
1656 _Reference
.setAsValidRef();
1658 _State
.CurrentIndex
= _Reference
.Index
;
1661 for (i
=0; i
<_Tables
.size(); ++i
)
1662 _Tables
[i
]->notifyNewReference(_Reference
);
1673 void CDatabase::receiveUpdate(uint32 id
)
1675 // add to acknowledged updates
1676 _ReceivedUpdates
.push_back(id
);
1678 _State
.LastUpdateId
= id
;
1684 void CDatabase::flushUpdates(std::vector
<uint32
>& acknowledged
)
1686 // copy acknowledge and flush
1687 acknowledged
= _ReceivedUpdates
;
1688 _ReceivedUpdates
.clear();
1693 * Get Update Queue for id
1694 * May return NULL if message was already received
1696 RY_PDS::CDbMessageQueue
* CDatabase::getUpdateMessageQueue(uint32 updateId
)
1698 if (updateId
!= 0 && updateId
<= _State
.LastUpdateId
)
1701 _LogQueue
.push_back(RY_PDS::CUpdateLog());
1703 RY_PDS::CUpdateLog
& ulog
= _LogQueue
.back();
1705 ulog
.UpdateId
= updateId
;
1706 ulog
.createUpdates();
1707 ulog
.StartStamp
.setToCurrent();
1708 ulog
.EndStamp
.setToCurrent();
1710 return ulog
.getUpdates();
1716 CVariable
<uint
> MinuteUpdateRate("pds", "MinuteUpdateRate", "Number of seconds between two minute updates", 60, 0, true);
1717 CVariable
<uint
> HourUpdateRate("pds", "HourUpdateRate", "Number of seconds between two hour updates", 3600, 0, true);
1718 CVariable
<uint
> ReferenceUpdateRate("pds", "ReferenceUpdateRate", "Number of seconds between two reference builds", 86400, 0, true);
1720 CVariable
<uint
> KeepSecondsAtMinuteUpdate("pds", "KeepSecondsAtMinuteUpdate", "Number of seconds to keep (in number of minute updates)", 2, 0, true);
1721 CVariable
<uint
> KeepMinutesAtHourUpdate("pds", "KeepMinutesAtHourUpdate", "Number of minutes to keep (in number of hours updates)", 2, 0, true);
1722 CVariable
<uint
> KeepHoursAtReferenceUpdate("pds", "KeepHoursAtReferenceUpdate", "Number of hours to keep (in number of daily updates)", 2, 0, true);
1726 * Send Delta/Reference build commands
1728 bool CDatabase::sendBuildCommands(const CTimestamp
& current
)
1732 while (current
- _MinuteUpdateTimestamp
>= MinuteUpdateRate
)
1734 CTimestamp start
= _MinuteUpdateTimestamp
;
1735 CTimestamp end
= start
+ MinuteUpdateRate
;
1736 CTimestamp keep
= end
- MinuteUpdateRate
*KeepSecondsAtMinuteUpdate
;
1738 string outputPath
= _Reference
.getMinutesUpdatePath();
1739 string hoursUpdatePath
= _Reference
.getHoursUpdatePath();
1740 string minutesUpdatePath
= _Reference
.getMinutesUpdatePath();
1741 string secondsUpdatePath
= _Reference
.getSecondsUpdatePath();
1742 string mintimestamp
= start
.toString();
1743 string maxtimestamp
= end
.toString();
1744 CDeltaBuilder::TDelta type
= CDeltaBuilder::Minute
;
1745 string keeptimestamp
= keep
.toString();
1747 CMessage
& msgdelta
= CDbManager::addTask("RB_GEN_DELTA", NULL
, NULL
);
1748 msgdelta
.serial(outputPath
);
1749 msgdelta
.serial(hoursUpdatePath
);
1750 msgdelta
.serial(minutesUpdatePath
);
1751 msgdelta
.serial(secondsUpdatePath
);
1752 msgdelta
.serial(mintimestamp
);
1753 msgdelta
.serial(maxtimestamp
);
1754 msgdelta
.serialEnum(type
);
1755 msgdelta
.serial(keeptimestamp
);
1757 _MinuteUpdateTimestamp
= end
;
1761 while (current
- _HourUpdateTimestamp
>= HourUpdateRate
)
1763 CTimestamp start
= _HourUpdateTimestamp
;
1764 CTimestamp end
= start
+ HourUpdateRate
;
1765 CTimestamp keep
= end
- HourUpdateRate
*KeepMinutesAtHourUpdate
;
1767 string outputPath
= _Reference
.getHoursUpdatePath();
1768 string hoursUpdatePath
= _Reference
.getHoursUpdatePath();
1769 string minutesUpdatePath
= _Reference
.getMinutesUpdatePath();
1770 string secondsUpdatePath
= _Reference
.getSecondsUpdatePath();
1771 string mintimestamp
= start
.toString();
1772 string maxtimestamp
= end
.toString();
1773 CDeltaBuilder::TDelta type
= CDeltaBuilder::Hour
;
1774 string keeptimestamp
= keep
.toString();
1776 CMessage
& msgdelta
= CDbManager::addTask("RB_GEN_DELTA", NULL
, NULL
);
1777 msgdelta
.serial(outputPath
);
1778 msgdelta
.serial(hoursUpdatePath
);
1779 msgdelta
.serial(minutesUpdatePath
);
1780 msgdelta
.serial(secondsUpdatePath
);
1781 msgdelta
.serial(mintimestamp
);
1782 msgdelta
.serial(maxtimestamp
);
1783 msgdelta
.serialEnum(type
);
1784 msgdelta
.serial(keeptimestamp
);
1786 _HourUpdateTimestamp
= end
;
1789 while (current
- _DayUpdateTimestamp
>= ReferenceUpdateRate
)
1791 CTimestamp start
= _DayUpdateTimestamp
;
1792 CTimestamp end
= start
+ ReferenceUpdateRate
;
1793 CTimestamp keep
= end
- ReferenceUpdateRate
*KeepHoursAtReferenceUpdate
;
1795 CRefIndex
* next
= new CRefIndex(_State
.Id
);
1799 string rootRefPath
= _Reference
.getRootPath();
1800 string previousReferencePath
= _Reference
.getPath();
1801 string nextReferencePath
= next
->getPath();
1803 string logUpdatePath
= _Reference
.getLogPath();
1804 string hoursUpdatePath
= _Reference
.getHoursUpdatePath();
1805 string minutesUpdatePath
= _Reference
.getMinutesUpdatePath();
1806 string secondsUpdatePath
= _Reference
.getSecondsUpdatePath();
1808 string mintimestamp
= start
.toString();
1809 string maxtimestamp
= end
.toString();
1810 string keeptimestamp
= keep
.toString();
1812 CMessage
& msgref
= CDbManager::addTask("RB_GEN_REF", this, (void*)next
);
1813 msgref
.serial(rootRefPath
);
1814 msgref
.serial(previousReferencePath
);
1815 msgref
.serial(nextReferencePath
);
1816 msgref
.serial(hoursUpdatePath
);
1817 msgref
.serial(minutesUpdatePath
);
1818 msgref
.serial(secondsUpdatePath
);
1819 msgref
.serial(logUpdatePath
);
1820 msgref
.serial(mintimestamp
);
1821 msgref
.serial(maxtimestamp
);
1822 msgref
.serial(keeptimestamp
);
1824 _DayUpdateTimestamp
= end
;
1831 * Task ran successfully
1833 void CDatabase::taskSuccessful(void* arg
)
1835 // when reference is up to date, notify new reference
1836 CRefIndex
* ref
= (CRefIndex
*)arg
;
1839 notifyNewReference(true);
1847 void CDatabase::taskFailed(void* arg
)
1849 // ok, nothing to do in this case...
1853 * Static Check for update rates
1855 void CDatabase::checkUpdateRates()
1857 uint deltaRate
= DeltaUpdateRate
;
1858 uint minuteRate
= MinuteUpdateRate
;
1860 // minute rate must be a multiple of delta rate
1861 if ((minuteRate
% deltaRate
) != 0)
1863 minuteRate
= deltaRate
*(minuteRate
/deltaRate
+ 1);
1864 nlwarning("CDatabase::checkUpdateRate(): MinuteUpdateRate is not a multiple of DeltaUpdateRate! Rounded to %d seconds", minuteRate
);
1865 MinuteUpdateRate
= minuteRate
;
1868 uint hourRate
= HourUpdateRate
;
1870 // hour rate must be a multiple of minute rate
1871 if ((hourRate
% minuteRate
) != 0)
1873 hourRate
= minuteRate
*(hourRate
/minuteRate
+ 1);
1874 nlwarning("CDatabase::checkUpdateRate(): HourUpdateRate is not a multiple of MinuteUpdateRate! Rounded to %d seconds", hourRate
);
1875 HourUpdateRate
= hourRate
;
1878 uint referenceRate
= ReferenceUpdateRate
;
1880 // reference rate must be a multiple of hour rate
1881 if ((referenceRate
% hourRate
) != 0)
1883 referenceRate
= hourRate
*(referenceRate
/hourRate
+ 1);
1884 nlwarning("CDatabase::checkUpdateRate(): ReferenceUpdateRate is not a multiple of HourUpdateRate! Rounded to %d seconds", referenceRate
);
1885 ReferenceUpdateRate
= referenceRate
;
1892 * Serialise SheetId String Mapper
1894 void CDatabase::serialSheetIdStringMapper(NLMISC::IStream
& f
)
1897 _SheetIdStringMapper
.serial(f
);
1901 // if mapper is read, save it now in reference path
1902 std::string refPath
= _Reference
.getPath();
1905 if (!ofile
.open(refPath
+"sheetid_map.bin"))
1907 PDS_WARNING("serialSheetIdStringMapper(): failed to open reference sheetid_map file '%s' for save", (refPath
+"sheetid_map.bin").c_str());
1911 _SheetIdStringMapper
.serial(ofile
);
1918 * Get Table Index from name
1920 RY_PDS::TTableIndex
CDatabase::getTableIndex(const std::string
& tableName
) const
1922 const CTable
* table
= getTable(tableName
);
1923 return (table
== NULL
) ? RY_PDS::INVALID_TABLE_INDEX
: (RY_PDS::TTableIndex
)table
->getId();
1927 * Get Table Index from name
1929 std::string
CDatabase::getTableName(RY_PDS::TTableIndex index
) const
1931 if (index
>= _Tables
.size() || _Tables
[index
] == NULL
)
1934 return _Tables
[index
]->getName();
1941 * Search object in database using its key
1942 * \param key is the 64 bits row key to search through all tables
1944 bool CDatabase::searchObjectIndex(uint64 key
, std::set
<RY_PDS::CObjectIndex
>& indexes
) const
1949 for (i
=0; i
<_Tables
.size(); ++i
)
1951 if (_Tables
[i
] == NULL
|| !_Tables
[i
]->initialised() || !_Tables
[i
]->isMapped())
1954 RY_PDS::CObjectIndex index
= _Tables
[i
]->getMappedRow(key
);
1956 if (index
.isValid())
1957 indexes
.insert(index
);
1960 return !indexes
.empty();