cache returned objects for next round + add jeffrey test

This commit is contained in:
odain
2025-12-31 07:59:21 +01:00
parent 5ced223426
commit fc2901a5d3
2 changed files with 94 additions and 15 deletions

View File

@@ -122,9 +122,7 @@ class DBObjectSet implements iDBObjectSetIterator
*/
public function __destruct()
{
if (is_object($this->m_oSQLResult)) {
$this->m_oSQLResult->free();
}
$this->Free();
}
/**
@@ -711,11 +709,8 @@ class DBObjectSet implements iDBObjectSetIterator
$sSQL = $this->_makeSelectQuery($this->m_aAttToLoad);
if (is_object($this->m_oSQLResult)) {
// Free previous resultset if any
$this->m_oSQLResult->free();
$this->m_oSQLResult = null;
}
// Free previous resultset if any
$this->Free();
try {
$oKPI = new ExecutionKPI();
@@ -919,6 +914,14 @@ class DBObjectSet implements iDBObjectSetIterator
return $this->m_iNumLoadedDBRows + count($this->m_aAddedObjects);
}
private function Free()
{
if (is_object($this->m_oSQLResult)) {
CMDBSource::FreeResult($this->m_oSQLResult);
$this->m_oSQLResult = null;
}
}
/**
* Fetch an object (with the given class alias) at the current position in the set and move the cursor to the next position.
*
@@ -939,6 +942,7 @@ class DBObjectSet implements iDBObjectSetIterator
}
if ($this->m_iCurrRow >= $this->CountLoaded()) {
$this->Free();
return null;
}
@@ -946,7 +950,9 @@ class DBObjectSet implements iDBObjectSetIterator
$sRequestedClassAlias = $this->m_oFilter->GetClassAlias();
}
if ($this->m_iCurrRow < $this->m_iNumLoadedDBRows) {
if ($this->m_iCurrRow < count($this->m_aCacheObj)) {
$oRetObj = $this->m_aCacheObj[$this->m_iCurrRow][$sRequestedClassAlias];
} else if ($this->m_iCurrRow < $this->m_iNumLoadedDBRows) {
// Pick the row from the database
$aRow = CMDBSource::FetchArray($this->m_oSQLResult);
foreach ($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass) {
@@ -956,6 +962,7 @@ class DBObjectSet implements iDBObjectSetIterator
} else {
try {
$oRetObj = MetaModel::GetObjectByRow($sClass, $aRow, $sClassAlias, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
$this->m_aCacheObj[$this->m_iCurrRow] = [$sRequestedClassAlias => $oRetObj];
} catch (CoreException $e) {
$this->m_iCurrRow++;
$oRetObj = $this->Fetch($sRequestedClassAlias);
@@ -972,6 +979,8 @@ class DBObjectSet implements iDBObjectSetIterator
return $oRetObj;
}
private $m_aCacheObj = [];
/**
* Fetch the whole row of objects (if several classes have been specified in the query) and move the cursor to the next position
*
@@ -990,21 +999,29 @@ class DBObjectSet implements iDBObjectSetIterator
}
if ($this->m_iCurrRow >= $this->CountLoaded()) {
$this->Free();
return null;
}
if ($this->m_iCurrRow < $this->m_iNumLoadedDBRows) {
if ($this->m_iCurrRow < count($this->m_aCacheObj)) {
$aRetObjects = $this->m_aCacheObj[$this->m_iCurrRow];
} else if ($this->m_iCurrRow < $this->m_iNumLoadedDBRows) {
// Pick the row from the database
$aRow = CMDBSource::FetchArray($this->m_oSQLResult);
$aRetObjects = [];
foreach ($this->m_oFilter->GetSelectedClasses() as $sClassAlias => $sClass) {
if (is_null($aRow[$sClassAlias.'id'])) {
$oObj = null;
} else {
$oObj = MetaModel::GetObjectByRow($sClass, $aRow, $sClassAlias, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
$oObj = null;
if (!is_null($aRow[$sClassAlias.'id'])) {
try {
$oObj = MetaModel::GetObjectByRow($sClass, $aRow, $sClassAlias, $this->m_aAttToLoad, $this->m_aExtendedDataSpec);
}
catch (CoreException $e) {
}
}
$aRetObjects[$sClassAlias] = $oObj;
}
$this->m_aCacheObj[$this->m_iCurrRow] = $aRetObjects;
} else {
// Pick the row from the objects added *in memory*
$aRetObjects = [];
@@ -1047,6 +1064,10 @@ class DBObjectSet implements iDBObjectSetIterator
$this->Load();
}
if (is_null($this->m_oSQLResult)) {
return;
}
$this->m_iCurrRow = min($iRow, $this->Count());
if ($this->m_iCurrRow < $this->m_iNumLoadedDBRows) {
$this->m_oSQLResult->data_seek($this->m_iCurrRow);
@@ -1220,7 +1241,7 @@ class DBObjectSet implements iDBObjectSetIterator
*
* @throws \CoreException
*/
public function HasSameContents(DBObjectSet $oObjectSet, $aExcludeColumns = [])
public function HasSameContents(DBObjectSet $oObjectSet, $aExcludeColumns = []): bool
{
$oComparator = new DBObjectSetComparator($this, $oObjectSet, $aExcludeColumns);
return $oComparator->SetsAreEquivalent();

View File

@@ -60,6 +60,15 @@ class DBObjectSetTest extends ItopDataTestCase
}
public function testDBObjectSetComparator()
{
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT UserRequest");
$DBObjectSet1 = new DBObjectSet($oSearch);
$DBObjectSet3 = new DBObjectSet($oSearch);
$oDBObjectSetComparator = new DBObjectSetComparator($DBObjectSet1, $DBObjectSet3);
$this->assertTrue($oDBObjectSetComparator->SetsAreEquivalent());
}
public function testDBObjectSetComparator_CheckCache()
{
$oSearch = DBObjectSearch::FromOQL_AllData("SELECT UserRequest");
$DBObjectSet1 = new DBObjectSet($oSearch);
@@ -77,4 +86,53 @@ class DBObjectSetTest extends ItopDataTestCase
$this->expectExceptionMessage($sMsg, "should call DB again this time");
$this->assertTrue($oDBObjectSetComparator->SetsAreEquivalent());
}
public static function JeffreyProvider()
{
return [
'basic' => [false],
'opt trick' => [true],
];
}
/**
* @dataProvider JeffreyProvider
*/
public function testJeffrey(bool $bTrick)
{
echo '<p>-------</p>';
$this->doesNotPerformAssertions();
$iMax = 100;
$oFilter = DBObjectSearch::FromOQL_AllData('SELECT UserRequest');
$oSet = new DBObjectSet($oFilter);
$oSet->OptimizeColumnLoad([
'UserRequest' => ['ref', 'status', 'title'],
]);
if ($bTrick) {
$oNewSet = DBObjectSet::FromScratch($oSet->GetClass());
while ($oObj = $oSet->Fetch()) {
$oNewSet->AddObject($oObj);
}
$oSet = $oNewSet;
}
echo '<p>Start: '.date('Y-m-d H:i:s').'</p>';
$i = 0;
while ($i < $iMax) {
$oSet->Rewind();
while ($oObj = $oSet->Fetch()) {
// Do nothing
$s = $oObj->Get('title');
}
$i += 1;
}
$peak = memory_get_peak_usage(true) / 1000000;
echo '<p>End: '.date('Y-m-d H:i:s').'</p><p>Peak memory: '.$peak.'</p> \n';
}
}