Protection against reentrance for DBUpdate

This commit is contained in:
Eric Espie
2022-05-30 17:03:47 +02:00
parent af4a5e1b8d
commit aed8337c51
4 changed files with 35 additions and 24 deletions

View File

@@ -4488,7 +4488,7 @@ HTML;
// Protection against reentrance (e.g. cascading the update of ticket logs)
// Note: This is based on the fix made on r 3190 in DBObject::DBUpdate()
if (!MetaModel::StartReentranceProtection($this)) {
if (!MetaModel::StartReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this)) {
$sClass = get_class($this);
$sKey = $this->GetKey();
IssueLog::Debug("CRUD: DBUpdate $sClass::$sKey Rejected (reentrance)", LogChannels::DM_CRUD);
@@ -4507,7 +4507,7 @@ HTML;
}
finally
{
MetaModel::StopReentranceProtection($this);
MetaModel::StopReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this);
}
if ($this->IsModified()) {

View File

@@ -3063,7 +3063,7 @@ abstract class DBObject implements iDisplay
}
// Prevent DBUpdate at this point (reentrance protection)
MetaModel::StartReentranceProtection($this);
MetaModel::StartReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this);
$this->AfterInsert();
@@ -3086,7 +3086,7 @@ abstract class DBObject implements iDisplay
// - TriggerOnObjectMention
$this->ActivateOnMentionTriggers(true);
MetaModel::StopReentranceProtection($this);
MetaModel::StopReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this);
if ($this->IsModified()) {
$this->DBUpdate();
@@ -3157,7 +3157,7 @@ abstract class DBObject implements iDisplay
// Protect against reentrance (e.g. cascading the update of ticket logs)
$sClass = get_class($this);
$sKey = $this->GetKey();
if (!MetaModel::StartReentranceProtection($this)) {
if (!MetaModel::StartReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this)) {
IssueLog::Debug("CRUD: DBUpdate $sClass::$sKey Rejected (reentrance)", LogChannels::DM_CRUD);
@@ -3195,7 +3195,7 @@ abstract class DBObject implements iDisplay
if (count($aChanges) == 0)
{
// Attempting to update an unchanged object
MetaModel::StopReentranceProtection($this);
MetaModel::StopReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this);
IssueLog::Debug("CRUD: DBUpdate $sClass::$sKey Aborted (no change)", LogChannels::DM_CRUD);
return $this->m_iKey;
@@ -3411,7 +3411,7 @@ abstract class DBObject implements iDisplay
}
finally
{
MetaModel::StopReentranceProtection($this);
MetaModel::StopReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $this);
}
if ($this->IsModified()) {

View File

@@ -128,7 +128,9 @@ abstract class MetaModel
/** @var string */
protected static $m_sEnvironment = 'production';
protected static $m_aReentryProtection = [];
public const REENTRANCE_TYPE_UPDATE = 'update';
protected static $m_aReentranceProtection = [];
/**
* MetaModel constructor.
@@ -6800,6 +6802,19 @@ abstract class MetaModel
}
$sClass = $aRow[$sClassAlias."finalclass"];
}
// if an object is already being updated, then this method will return this object instead of recreating a new one.
// At this point the method DBUpdate of a new object with the same class and id won't do anything due to reentrance protection,
// so to ensure that the potential modifications are correctly saved, the object currently being updated is returned.
// DBUpdate() method then will take care that all the modifications will be saved.
if (array_key_exists($sClassAlias.'id', $aRow)) {
$iKey = $aRow[$sClassAlias."id"];
$oObject = self::GetReentranceObject(Metamodel::REENTRANCE_TYPE_UPDATE, $sClass, $iKey);
if ($oObject !== false) {
return $oObject;
}
}
return new $sClass($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec);
}
@@ -6825,11 +6840,6 @@ abstract class MetaModel
*/
public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null)
{
$oObject = self::GetReentryObject($sClass, $iKey);
if ($oObject !== false) {
return $oObject;
}
$oObject = self::GetObjectWithArchive($sClass, $iKey, $bMustBeFound, $bAllowAllData, $aModifierProperties);
if (empty($oObject))
@@ -7567,32 +7577,33 @@ abstract class MetaModel
return $oAttDef->GetStyle($sValue);
}
protected static function GetReentryObject($sClass, $sKey)
protected static function GetReentranceObject($sType, $sClass, $sKey)
{
if (isset(self::$m_aReentryProtection[$sClass][$sKey])) {
return self::$m_aReentryProtection[$sClass][$sKey];
if (isset(self::$m_aReentranceProtection[$sType][$sClass][$sKey])) {
return self::$m_aReentranceProtection[$sType][$sClass][$sKey];
}
return false;
}
/**
* @param $sType
* @param \DBObject $oObject
*
* @return bool true if reentry possible
*/
public static function StartReentranceProtection(DBObject $oObject)
public static function StartReentranceProtection($sType, DBObject $oObject)
{
if (isset(self::$m_aReentryProtection[get_class($oObject)][$oObject->GetKey()])) {
if (isset(self::$m_aReentranceProtection[$sType][get_class($oObject)][$oObject->GetKey()])) {
return false;
}
self::$m_aReentryProtection[get_class($oObject)][$oObject->GetKey()] = $oObject;
self::$m_aReentranceProtection[$sType][get_class($oObject)][$oObject->GetKey()] = $oObject;
return true;
}
public static function StopReentranceProtection(DBObject $oObject)
public static function StopReentranceProtection($sType, DBObject $oObject)
{
if (isset(self::$m_aReentryProtection[get_class($oObject)][$oObject->GetKey()])) {
unset(self::$m_aReentryProtection[get_class($oObject)][$oObject->GetKey()]);
if (isset(self::$m_aReentranceProtection[$sType][get_class($oObject)][$oObject->GetKey()])) {
unset(self::$m_aReentranceProtection[$sType][get_class($oObject)][$oObject->GetKey()]);
}
}
}

View File

@@ -151,7 +151,7 @@ class DBObjectTest extends ItopDataTestCase
$oNewPerson = MetaModel::GetObject('Person', $oPerson->GetKey());
$this->assertNotEquals($oPerson->GetObjectUniqId(), $oNewPerson->GetObjectUniqId());
MetaModel::StartReentranceProtection($oPerson);
MetaModel::StartReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $oPerson);
$oPerson->Set('email', 'test1@combodo.com');
$oPerson->DBUpdate();
@@ -161,7 +161,7 @@ class DBObjectTest extends ItopDataTestCase
$oNewPerson = MetaModel::GetObject('Person', $oPerson->GetKey());
$this->assertEquals($oPerson->GetObjectUniqId(), $oNewPerson->GetObjectUniqId());
MetaModel::StopReentranceProtection($oPerson);
MetaModel::StopReentranceProtection(Metamodel::REENTRANCE_TYPE_UPDATE, $oPerson);
}
public function testObjectIsReadOnly()