From a4663ebed150cc306f7d2180268eff6616868992 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 27 Apr 2009 07:27:54 +0000 Subject: [PATCH] Optimized the setup (not only), shortened to 37s (used to be 120s) - DBObject::DBInsert(Tracked)NoReload() must be used when it is not required to use the object - MetaModel::GetObject() has a cache, the operation is 5 times faster - Changes tracking do not store the initial value, but only value changes Reworked the CSV import to have it rely on the bulk change API Bulk change to check the external keys (DB integrity) Replaced trigger_error (Core only!) by the use of Exceptions (still, some new Exception classes should be defined) Unit tests do display the call stack in a user friendly format SVN:code[52] --- .../userrights/userrightsmatrix.class.inc.php | 22 +- application/dialogstack.class.inc.php | 4 +- application/utils.inc.php | 2 +- business/business_test.class.inc.php | 4 +- core/MyHelpers.class.inc.php | 8 +- core/attributedef.class.inc.php | 16 +- core/bulkchange.class.inc.php | 46 +- core/cmdbchangeop.class.inc.php | 6 +- core/cmdbobject.class.inc.php | 56 +-- core/coreexception.class.inc.php | 11 + core/dbobject.class.php | 81 +++- core/dbobjectsearch.class.php | 16 +- core/dbobjectset.class.php | 11 +- core/filterdef.class.inc.php | 10 +- core/metamodel.class.php | 79 ++-- core/sqlquery.class.inc.php | 10 +- core/stimulus.class.inc.php | 2 +- core/test.class.inc.php | 8 +- core/userrights.class.inc.php | 4 +- pages/csvimport.php | 433 +++--------------- pages/testlist.inc.php | 157 ++++++- setup/xmldataloader.class.inc.php | 8 +- webservices/import.php | 3 + 23 files changed, 464 insertions(+), 533 deletions(-) diff --git a/addons/userrights/userrightsmatrix.class.inc.php b/addons/userrights/userrightsmatrix.class.inc.php index 710f741b8..de90baa47 100644 --- a/addons/userrights/userrightsmatrix.class.inc.php +++ b/addons/userrights/userrightsmatrix.class.inc.php @@ -173,8 +173,8 @@ class UserRightsMatrix extends UserRightsAddOnAPI $oUser->Set('login', $sAdminUser); $oUser->Set('password', $sAdminPwd); $oUser->Set('userid', 1); // one is for root ! - $oUser->DBInsert(); - $this->SetupUser($oUser, true); + $iUserId = $oUser->DBInsertNoReload(); + $this->SetupUser($iUserId, true); return true; } @@ -185,15 +185,13 @@ class UserRightsMatrix extends UserRightsAddOnAPI $oUserSet = new DBObjectSet(DBObjectSearch::FromSibuSQL("UserRightsMatrixUsers")); while ($oUser = $oUserSet->Fetch()) { - $this->SetupUser($oUser); + $this->SetupUser($oUser->GetKey()); } return true; } - protected function SetupUser($oUser, $bNewUser = false) + protected function SetupUser($iUserId, $bNewUser = false) { - $iUserId = $oUser->GetKey(); - foreach(array('bizmodel', 'application', 'gui', 'core/cmdb') as $sCategory) { foreach (MetaModel::GetClasses($sCategory) as $sClass) @@ -213,11 +211,11 @@ class UserRightsMatrix extends UserRightsAddOnAPI { // Create a new entry $oMyClassGrant = MetaModel::NewObject("UserRightsMatrixClassGrant"); - $oMyClassGrant->Set("userid", $oUser->GetKey()); + $oMyClassGrant->Set("userid", $iUserId); $oMyClassGrant->Set("class", $sClass); $oMyClassGrant->Set("action", $sAction); $oMyClassGrant->Set("permission", "yes"); - $iId = $oMyClassGrant->DBInsert(); + $iId = $oMyClassGrant->DBInsertNoReload(); } } foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus) @@ -235,11 +233,11 @@ class UserRightsMatrix extends UserRightsAddOnAPI { // Create a new entry $oMyClassGrant = MetaModel::NewObject("UserRightsMatrixClassStimulusGrant"); - $oMyClassGrant->Set("userid", $oUser->GetKey()); + $oMyClassGrant->Set("userid", $iUserId); $oMyClassGrant->Set("class", $sClass); $oMyClassGrant->Set("stimulus", $sStimulusCode); $oMyClassGrant->Set("permission", "yes"); - $iId = $oMyClassGrant->DBInsert(); + $iId = $oMyClassGrant->DBInsertNoReload(); } } foreach (MetaModel::GetAttributesList($sClass) as $sAttCode) @@ -259,12 +257,12 @@ class UserRightsMatrix extends UserRightsAddOnAPI { // Create a new entry $oMyAttGrant = MetaModel::NewObject("UserRightsMatrixAttributeGrant"); - $oMyAttGrant->Set("userid", $oUser->GetKey()); + $oMyAttGrant->Set("userid", $iUserId); $oMyAttGrant->Set("class", $sClass); $oMyAttGrant->Set("attcode", $sAttCode); $oMyAttGrant->Set("action", $sAction); $oMyAttGrant->Set("permission", "yes"); - $iId = $oMyAttGrant->DBInsert(); + $iId = $oMyAttGrant->DBInsertNoReload(); } } } diff --git a/application/dialogstack.class.inc.php b/application/dialogstack.class.inc.php index a81f62166..f3e54bbf3 100644 --- a/application/dialogstack.class.inc.php +++ b/application/dialogstack.class.inc.php @@ -122,7 +122,7 @@ class dialogstack if (self::$m_bCurrPageDeclared) { - trigger_error("DeclareCaller() must not be called before StartDialog()", E_USER_ERROR); + throw new Exception("DeclareCaller() must not be called before StartDialog()"); } $aCall = array( @@ -242,7 +242,7 @@ class dialogstack } else { - trigger_error("Wrong value for button style ($iButtonStyle)", E_USER_ERROR); + throw new Exception("Wrong value for button style ($iButtonStyle)"); } $sRet .= "\n"; $sRet .= "\n"; diff --git a/application/utils.inc.php b/application/utils.inc.php index fb7e0ab08..00aa4c8d3 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -39,7 +39,7 @@ class utils self::$m_aConfig = array(); $sConfigContents = self::ReadFromFile(CONFIGFILE); - if (!$sConfigContents) trigger_error("Could not load file ".CONFIGFILE); + if (!$sConfigContents) throw new Exception("Could not load file ".CONFIGFILE); foreach (explode("\n", $sConfigContents) as $sLine) { diff --git a/business/business_test.class.inc.php b/business/business_test.class.inc.php index 6255208fe..3a51b8f93 100644 --- a/business/business_test.class.inc.php +++ b/business/business_test.class.inc.php @@ -310,7 +310,7 @@ class cmdbLiens extends cmdbObjectHomeMade public static function GetRelationQueries($sRelCode) { - trigger_error("GetRelationQueries: cmdbLiens"); + throw new CoreException("GetRelationQueries: cmdbLiens"); return array("Relies on" => array("sQuery"=>"", "bPropagate"=>true, "iDistance"=>3)); } } @@ -357,7 +357,7 @@ class cmdbWorkshop extends cmdbObjectHomeMade public static function GetRelationQueries($sRelCode) { - trigger_error("GetRelationQueries: cmdbWorkshop"); + throw new CoreException("GetRelationQueries: cmdbWorkshop"); return array("Relies on" => array("sQuery"=>"", "bPropagate"=>true, "iDistance"=>3)); } } diff --git a/core/MyHelpers.class.inc.php b/core/MyHelpers.class.inc.php index 59ba892d0..8ee89a5ff 100644 --- a/core/MyHelpers.class.inc.php +++ b/core/MyHelpers.class.inc.php @@ -42,7 +42,7 @@ class MyHelpers $sArrayDesc = "{".implode(", ", $aData)."}"; } // exit! - trigger_error("Wrong value for $sDescription, found '$value' while expecting a value in $sArrayDesc", E_USER_ERROR); + throw new CoreException("Wrong value for $sDescription, found '$value' while expecting a value in $sArrayDesc"); } // getmicrotime() @@ -281,10 +281,10 @@ class MyHelpers /////////////////////////////////////////////////////////////////////////////// public static function make_table_from_assoc_array(&$aData) { - if (!is_array($aData)) trigger_error("make_table_from_assoc_array: Error - the passed argument is not an array", E_USER_ERROR); + if (!is_array($aData)) throw new CoreException("make_table_from_assoc_array: Error - the passed argument is not an array"); $aFirstRow = reset($aData); if (count($aData) == 0) return ''; - if (!is_array($aFirstRow)) trigger_error("make_table_from_assoc_array: Error - the passed argument is not a bi-dimensional array", E_USER_ERROR); + if (!is_array($aFirstRow)) throw new CoreException("make_table_from_assoc_array: Error - the passed argument is not a bi-dimensional array"); $sOutput = ""; $sOutput .= "\n"; @@ -364,7 +364,7 @@ class MyHelpers /////////////////////////////////////////////////////////////////////////////// public static function beautifulstr($sLongString, $iMaxLen, $bShowLen=false, $bShowTooltip=true) { - if (!is_string($sLongString)) trigger_error("beautifulstr: expect a string as 1st argument", E_USER_ERROR); + if (!is_string($sLongString)) throw new CoreException("beautifulstr: expect a string as 1st argument"); // Nothing to do if the string is short if (strlen($sLongString) <= $iMaxLen) return $sLongString; diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 342e68ee3..d1af378a4 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -53,7 +53,7 @@ abstract class AttributeDefinition { if (!array_key_exists($sParam, $this->m_aParams)) { - trigger_error("Unknown attribute definition parameter '$sParam', please select a value in {".implode(", ", $this->m_aParams)."}"); + throw new CoreException("Unknown attribute definition parameter '$sParam', please select a value in {".implode(", ", $this->m_aParams)."}"); } else { @@ -90,7 +90,7 @@ abstract class AttributeDefinition $aBacktrace = debug_backtrace(); $sTargetClass = $aBacktrace[2]["class"]; $sCodeInfo = $aBacktrace[1]["file"]." - ".$aBacktrace[1]["line"]; - trigger_error("ERROR missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)
\n", E_USER_ERROR); + throw new Exception("ERROR missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)"); } } } @@ -390,7 +390,7 @@ class AttributeInteger extends AttributeDBField return $this->GetSQLExpr()." <= $sQValue"; break; case 'in': - if (!is_array($value)) trigger_error("Expected an array for argument value (sOpCode='$sOpCode')"); + if (!is_array($value)) throw new CoreException("Expected an array for argument value (sOpCode='$sOpCode')"); return $this->GetSQLExpr()." IN ('".implode("', '", $value)."')"; break; @@ -485,7 +485,7 @@ class AttributeString extends AttributeDBField return (string)$proposedValue; // if (!settype($proposedValue, "string")) // { - // trigger_error("Failed to change the type of '$proposedValue' to a string", E_USER_WARNING); + // throw new CoreException("Failed to change the type of '$proposedValue' to a string"); // } } public function RealValueToSQLValue($value) @@ -692,7 +692,7 @@ class AttributeDate extends AttributeDBField { return date("Y-m-d H:i:s", $proposedValue); } - trigger_error("Invalid type for a date (found ".gettype($proposedValue)." and accepting string/int/DateTime)", E_USER_ERROR); + throw new CoreException("Invalid type for a date (found ".gettype($proposedValue)." and accepting string/int/DateTime)"); return null; } public function RealValueToSQLValue($value) @@ -814,7 +814,7 @@ class AttributeExternalField extends AttributeDefinition public function GetEditClass() {return "ExtField";} public function GetDBFieldType() { - trigger_error("external attribute: does it make any sense to request its type ?", E_USER_WARNING); + // throw new CoreException("external attribute: does it make any sense to request its type ?"); $oExtAttDef = $this->GetExtAttDef(); return $oExtAttDef->GetDBFieldType(); } @@ -832,7 +832,7 @@ class AttributeExternalField extends AttributeDefinition return false; default: - trigger_error("Unexpected value for argument iType: '$iType'", E_USER_ERROR); + throw new CoreException("Unexpected value for argument iType: '$iType'"); } } @@ -866,7 +866,7 @@ class AttributeExternalField extends AttributeDefinition return MetaModel::GetAttributeDef($this->GetHostClass(), $this->Get("extkey_attcode")); default: - trigger_error("Unexpected value for argument iType: '$iType'", E_USER_ERROR); + throw new CoreException("Unexpected value for argument iType: '$iType'"); } } diff --git a/core/bulkchange.class.inc.php b/core/bulkchange.class.inc.php index e52521a7d..491852a22 100644 --- a/core/bulkchange.class.inc.php +++ b/core/bulkchange.class.inc.php @@ -62,6 +62,14 @@ class CellChangeSpec_Unchanged extends CellChangeSpec } } +class CellChangeSpec_Init extends CellChangeSpec +{ + public function GetDescription() + { + return $this->GetValue(); + } +} + class CellChangeSpec_Modify extends CellChangeSpec { protected $m_previousValue; @@ -94,7 +102,7 @@ class CellChangeSpec_Issue extends CellChangeSpec_Modify { return 'Could not be changed - reason: '.$this->m_sReason; } - return 'Could not be changed to "'.$this->GetValue().' - reason:'.$this->m_sReason.' (previous: '.$this->m_previousValue.')'; + return 'Could not be changed to "'.$this->GetValue().'" - reason: '.$this->m_sReason.' (previous: '.$this->m_previousValue.')'; } } @@ -168,7 +176,7 @@ class RowStatus_Issue extends RowStatus { protected $m_sReason; - public function __construct($proposedValue, $previousValue, $sReason) + public function __construct($sReason) { $this->m_sReason = $sReason; } @@ -240,9 +248,16 @@ class BulkChange $oTargetObj->Set($sAttCode, $oForeignObj->GetKey()); // Report it - if (array_key_exists($sAttCode, $oTargetObj->ListChanges(false))) + if (array_key_exists($sAttCode, $oTargetObj->ListChanges())) { - $aResults[$sAttCode]= new CellChangeSpec_Modify($oForeignObj->GetKey(), $oTargetObj->Get($sAttCode), $oTargetObj->GetOriginal($sAttCode)); + if ($oTargetObj->IsNew()) + { + $aResults[$sAttCode]= new CellChangeSpec_Init($oForeignObj->GetKey(), $oTargetObj->Get($sAttCode), $oTargetObj->GetOriginal($sAttCode)); + } + else + { + $aResults[$sAttCode]= new CellChangeSpec_Modify($oForeignObj->GetKey(), $oTargetObj->Get($sAttCode), $oTargetObj->GetOriginal($sAttCode)); + } } else { @@ -259,7 +274,14 @@ class BulkChange // foreach ($this->m_aAttList as $sAttCode => $iCol) { - $oTargetObj->Set($sAttCode, $aRowData[$iCol]); + if (!$oTargetObj->CheckValue($sAttCode, $aRowData[$iCol])) + { + $aErrors[$sAttCode] = "Unexpected value"; + } + else + { + $oTargetObj->Set($sAttCode, $aRowData[$iCol]); + } } // Reporting on fields @@ -269,11 +291,19 @@ class BulkChange { if (isset($aErrors[$sAttCode])) { - $aResults["col$iCol"]= new CellChangeSpec_Issue($aRowData[$iCol], $previousValue, $sReason); + $aResults["col$iCol"]= new CellChangeSpec_Issue($aRowData[$iCol], $oTargetObj->Get($sAttCode), $aErrors[$sAttCode]); } elseif (array_key_exists($sAttCode, $aChangedFields)) { - $aResults["col$iCol"]= new CellChangeSpec_Modify($aRowData[$iCol], $oTargetObj->Get($sAttCode), $oTargetObj->GetOriginal($sAttCode)); + $originalValue = $oTargetObj->GetOriginal($sAttCode); + if ($oTargetObj->IsNew()) + { + $aResults["col$iCol"]= new CellChangeSpec_Init($aRowData[$iCol], $oTargetObj->Get($sAttCode), $originalValue); + } + else + { + $aResults["col$iCol"]= new CellChangeSpec_Modify($aRowData[$iCol], $oTargetObj->Get($sAttCode), $originalValue); + } } else { @@ -315,7 +345,7 @@ class BulkChange // if ($oChange) { - $newID = $oTargetObj->DBInsertTracked($oChange); + $newID = $oTargetObj->DBInsertTrackedNoReload($oChange); $aResult[$iRow]["__STATUS__"] = new RowStatus_NewObj($newID); } else diff --git a/core/cmdbchangeop.class.inc.php b/core/cmdbchangeop.class.inc.php index 9c019d0c4..0ed8776de 100644 --- a/core/cmdbchangeop.class.inc.php +++ b/core/cmdbchangeop.class.inc.php @@ -150,15 +150,17 @@ class CMDBChangeOpSetAttribute extends CMDBChangeOp MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); MetaModel::Init_AddAttribute(new AttributeString("attcode", array("label"=>"Attribute", "description"=>"code of the modified property", "allowed_values"=>null, "sql"=>"attcode", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("oldvalue", array("label"=>"Previous value", "description"=>"previous value of the attribute", "allowed_values"=>null, "sql"=>"oldvalue", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("newvalue", array("label"=>"New value", "description"=>"new value of the attribute", "allowed_values"=>null, "sql"=>"newvalue", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("attcode"); + MetaModel::Init_AddFilterFromAttribute("oldvalue"); MetaModel::Init_AddFilterFromAttribute("newvalue"); // Display lists - MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode', 'newvalue')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode', 'newvalue')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode', 'oldvalue', 'newvalue')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode', 'oldvalue', 'newvalue')); // Attributes to be displayed for a list } } diff --git a/core/cmdbobject.class.inc.php b/core/cmdbobject.class.inc.php index 3bd4935f5..d49f7280b 100644 --- a/core/cmdbobject.class.inc.php +++ b/core/cmdbobject.class.inc.php @@ -144,7 +144,7 @@ abstract class CMDBObject extends DBObject $oMyChangeOp->Set("change", $oChange->GetKey()); $oMyChangeOp->Set("objclass", get_class($this)); $oMyChangeOp->Set("objkey", $this->GetKey()); - $iId = $oMyChangeOp->DBInsert(); + $iId = $oMyChangeOp->DBInsertNoReload(); } private function RecordObjDeletion(CMDBChange $oChange, $objkey) { @@ -152,22 +152,12 @@ abstract class CMDBObject extends DBObject $oMyChangeOp->Set("change", $oChange->GetKey()); $oMyChangeOp->Set("objclass", get_class($this)); $oMyChangeOp->Set("objkey", $objkey); - $iId = $oMyChangeOp->DBInsert(); + $iId = $oMyChangeOp->DBInsertNoReload(); } - private function RecordAttChanges(CMDBChange $oChange, array $aValues = array()) + private function RecordAttChanges(CMDBChange $oChange, array $aValues) { // $aValues is an array of $sAttCode => $value - // ... some values... // - if (empty($aValues)) - { - // ... or every object values - foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef) - { - if ($oAttDef->IsLinkSet()) continue; // #@# temporary - $aValues[$sAttCode] = $this->Get($sAttCode); - } - } foreach ($aValues as $sAttCode=> $value) { $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode); @@ -177,8 +167,9 @@ abstract class CMDBObject extends DBObject $oMyChangeOp->Set("objclass", get_class($this)); $oMyChangeOp->Set("objkey", $this->GetKey()); $oMyChangeOp->Set("attcode", $sAttCode); + $oMyChangeOp->Set("oldvalue", $this->GetOriginal($sAttCode)); $oMyChangeOp->Set("newvalue", $value); - $iId = $oMyChangeOp->DBInsert(); + $iId = $oMyChangeOp->DBInsertNoReload(); } } @@ -186,7 +177,7 @@ abstract class CMDBObject extends DBObject { if(!is_object(self::$m_oCurrChange)) { - trigger_error("DBInsert() could not be used here, please use DBInsertTracked() instead", E_USER_ERROR); + throw new CoreException("DBInsert() could not be used here, please use DBInsertTracked() instead"); } return $this->DBInsertTracked_Internal(); } @@ -199,11 +190,25 @@ abstract class CMDBObject extends DBObject return $ret; } - protected function DBInsertTracked_Internal() + public function DBInsertTrackedNoReload(CMDBChange $oChange) { - $ret = parent::DBInsert(); + self::$m_oCurrChange = $oChange; + $ret = $this->DBInsertTracked_Internal(true); + self::$m_oCurrChange = null; + return $ret; + } + + protected function DBInsertTracked_Internal($bDoNotReload = false) + { + if ($bDoNotReload) + { + $ret = parent::DBInsertNoReload(); + } + else + { + $ret = parent::DBInsert(); + } $this->RecordObjCreation(self::$m_oCurrChange); - $this->RecordAttChanges(self::$m_oCurrChange); return $ret; } @@ -211,7 +216,7 @@ abstract class CMDBObject extends DBObject { if(!self::$m_oCurrChange) { - trigger_error("DBClone() could not be used here, please use DBCloneTracked() instead", E_USER_ERROR); + throw new CoreException("DBClone() could not be used here, please use DBCloneTracked() instead"); } return $this->DBCloneTracked_Internal(); } @@ -229,7 +234,6 @@ abstract class CMDBObject extends DBObject $oClone = MetaModel::GetObject(get_class($this), $newKey); $oClone->RecordObjCreation(self::$m_oCurrChange); - $oClone->RecordAttChanges(self::$m_oCurrChange); return $newKey; } @@ -237,7 +241,7 @@ abstract class CMDBObject extends DBObject { if(!self::$m_oCurrChange) { - trigger_error("DBUpdate() could not be used here, please use DBUpdateTracked() instead", E_USER_ERROR); + throw new CoreException("DBUpdate() could not be used here, please use DBUpdateTracked() instead"); } return $this->DBUpdateTracked_internal(); } @@ -255,7 +259,7 @@ abstract class CMDBObject extends DBObject $aChanges = $this->ListChanges(); if (count($aChanges) == 0) { - trigger_error("Attempting to update an unchanged object", E_USER_NOTICE); + throw new CoreWarning("Attempting to update an unchanged object"); return; } @@ -268,7 +272,7 @@ abstract class CMDBObject extends DBObject { if(!self::$m_oCurrChange) { - trigger_error("DBDelete() could not be used here, please use DBDeleteTracked() instead", E_USER_ERROR); + throw new CoreException("DBDelete() could not be used here, please use DBDeleteTracked() instead"); } return $this->DBDeleteTracked_Internal(); } @@ -292,7 +296,7 @@ abstract class CMDBObject extends DBObject { if(!self::$m_oCurrChange) { - trigger_error("BulkDelete() could not be used here, please use BulkDeleteTracked() instead", E_USER_ERROR); + throw new CoreException("BulkDelete() could not be used here, please use BulkDeleteTracked() instead"); } return $this->BulkDeleteTracked_Internal($oFilter); } @@ -306,7 +310,7 @@ abstract class CMDBObject extends DBObject protected static function BulkDeleteTracked_Internal(DBObjectSearch $oFilter) { - trigger_error("Change tracking not tested for bulk operations", E_USER_WARNING); + throw new CoreWarning("Change tracking not tested for bulk operations"); // Get the list of objects to delete (and record data before deleting the DB records) $oObjSet = new CMDBObjectSet($oFilter); @@ -331,7 +335,7 @@ abstract class CMDBObject extends DBObject { if(!self::$m_oCurrChange) { - trigger_error("BulkUpdate() could not be used here, please use BulkUpdateTracked() instead", E_USER_ERROR); + throw new CoreException("BulkUpdate() could not be used here, please use BulkUpdateTracked() instead"); } return $this->BulkUpdateTracked_Internal($oFilter, $aValues); } diff --git a/core/coreexception.class.inc.php b/core/coreexception.class.inc.php index d720fc419..eb5ae8b9d 100644 --- a/core/coreexception.class.inc.php +++ b/core/coreexception.class.inc.php @@ -47,6 +47,17 @@ class CoreException extends Exception { return $this->getMessage(); } + + public function getTraceAsHtml() + { + $aBackTrace = $this->getTrace(); + return MyHelpers::get_callstack_html(0, $this->getTrace()); + // return "
\n".$this->getTraceAsString()."
\n"; + } +} + +class CoreWarning extends CoreException +{ } ?> diff --git a/core/dbobject.class.php b/core/dbobject.class.php index 732f0d901..c9d69a0b9 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -57,10 +57,25 @@ abstract class DBObject { $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); $this->m_aOrigValues[$sAttCode] = null; - // ??? $this->m_aLoadedAtt[$sAttCode] = true; + if ($oAttDef->IsExternalField()) + { + // This field has to be read from the DB + $this->m_aLoadedAtt[$sAttCode] = false; + } + else + { + // No need to trigger a reload for that attribute + // Let's consider it as being already fully loaded + $this->m_aLoadedAtt[$sAttCode] = true; + } } } + public function IsNew() + { + return (!$this->m_bIsInDB); + } + // Returns an Id for memory objects static protected function GetNextTempId($sClass) { @@ -113,7 +128,7 @@ abstract class DBObject $aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey); if (empty($aRow)) { - trigger_error("Failed to reload object of class '".get_class($this)."', id = ".$this->m_iKey, E_USER_ERROR); + throw new CoreException("Failed to reload object of class '".get_class($this)."', id = ".$this->m_iKey); } $this->FromRow($aRow); @@ -161,14 +176,14 @@ abstract class DBObject if (!array_key_exists($sKeyField, $aRow)) { // #@# Bug ? - trigger_error("Missing key for class '".get_class($this)."'", E_USER_ERROR); + throw new CoreException("Missing key for class '".get_class($this)."'"); } else { $iPKey = $aRow[$sKeyField]; if (!self::IsValidPKey($iPKey)) { - trigger_error("An object id must be an integer value ($iPKey)", E_USER_NOTICE); + throw new CoreWarning("An object id must be an integer value ($iPKey)"); } $this->m_iKey = $iPKey; } @@ -204,7 +219,7 @@ abstract class DBObject { if (!array_key_exists($sAttCode, MetaModel::ListAttributeDefs(get_class($this)))) { - trigger_error("Unknown attribute code '$sAttCode' for the class ".get_class($this), E_USER_ERROR); + throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this)); } $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode); if ($this->m_bIsInDB && !$this->m_bFullyLoaded) @@ -216,7 +231,7 @@ abstract class DBObject } if($oAttDef->IsScalar() && !$oAttDef->IsNullAllowed() && is_null($value)) { - trigger_error("null not allowed for attribute '$sAttCode', setting default value", E_USER_NOTICE); + throw new CoreWarning("null not allowed for attribute '$sAttCode', setting default value"); $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); return; } @@ -227,7 +242,7 @@ abstract class DBObject // (useful when building objects in memory and not from a query) if ( (get_class($value) != $oAttDef->GetTargetClass()) && (!is_subclass_of($value, $oAttDef->GetTargetClass()))) { - trigger_error("Trying to set the value of '$sAttCode', to an object of class '".get_class($value)."', whereas it's an ExtKey to '".$oAttDef->GetTargetClass()."'. Ignored", E_USER_NOTICE); + throw new CoreWarning("Trying to set the value of '$sAttCode', to an object of class '".get_class($value)."', whereas it's an ExtKey to '".$oAttDef->GetTargetClass()."'. Ignored"); $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); } else @@ -245,7 +260,7 @@ abstract class DBObject } if(!$oAttDef->IsScalar() && !is_object($value)) { - trigger_error("scalar not allowed for attribute '$sAttCode', setting default value (empty list)", E_USER_NOTICE); + throw new CoreWarning("scalar not allowed for attribute '$sAttCode', setting default value (empty list)"); $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); return; } @@ -253,7 +268,7 @@ abstract class DBObject { if((get_class($value) != 'DBObjectSet') && !is_subclass_of($value, 'DBObjectSet')) { - trigger_error("expecting a set of persistent objects (found a '".get_class($value)."'), setting default value (empty list)", E_USER_NOTICE); + throw new CoreWarning("expecting a set of persistent objects (found a '".get_class($value)."'), setting default value (empty list)"); $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); return; } @@ -264,7 +279,7 @@ abstract class DBObject // not working fine :-( if (!is_subclass_of($sSetClass, $sLinkClass)) if ($sSetClass != $sLinkClass) { - trigger_error("expecting a set of '$sLinkClass' objects (found a set of '$sSetClass'), setting default value (empty list)", E_USER_NOTICE); + throw new CoreWarning("expecting a set of '$sLinkClass' objects (found a set of '$sSetClass'), setting default value (empty list)"); $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); return; } @@ -276,7 +291,7 @@ abstract class DBObject { if (!array_key_exists($sAttCode, MetaModel::ListAttributeDefs(get_class($this)))) { - trigger_error("Unknown attribute code '$sAttCode' for the class ".get_class($this), E_USER_ERROR); + throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this)); } if ($this->m_bIsInDB && !$this->m_aLoadedAtt[$sAttCode]) { @@ -291,7 +306,7 @@ abstract class DBObject { if (!array_key_exists($sAttCode, MetaModel::ListAttributeDefs(get_class($this)))) { - trigger_error("Unknown attribute code '$sAttCode' for the class ".get_class($this), E_USER_ERROR); + throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this)); } return $this->m_aOrigValues[$sAttCode]; } @@ -374,12 +389,12 @@ abstract class DBObject { if (!self::IsValidPKey($iNewKey)) { - trigger_error("An object id must be an integer value ($iNewKey)", E_USER_ERROR); + throw new CoreException("An object id must be an integer value ($iNewKey)"); } if ($this->m_bIsInDB && !empty($this->m_iKey) && ($this->m_iKey != $iNewKey)) { - trigger_error("Changing the key ({$this->m_iKey} to $iNewKey) on an object (class {".get_class($this).") wich already exists in the Database", E_USER_NOTICE); + throw new CoreException("Changing the key ({$this->m_iKey} to $iNewKey) on an object (class {".get_class($this).") wich already exists in the Database"); } $this->m_iKey = $iNewKey; } @@ -431,15 +446,27 @@ abstract class DBObject // check if the given (or current) value is suitable for the attribute public function CheckValue($sAttCode, $value = null) { + if (!is_null($value)) + { + $toCheck = $value; + } + else + { + $toCheck = $this->Get($sAttCode); + } + $oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode); if ($oAtt->IsExternalKey()) { - if (!$oAtt->IsNullAllowed() || ($this->Get($sAttCode) != 0) ) + if (!$oAtt->IsNullAllowed() || ($toCheck != 0) ) { - $oTargetObj = MetaModel::GetObject($oAtt->GetTargetClass(), $this->Get($sAttCode)); - if (!$oTargetObj) + try + { + $oTargetObj = MetaModel::GetObject($oAtt->GetTargetClass(), $toCheck); + return true; + } + catch (CoreException $e) { - echo "Invalid value (".$this->Get($sAttCode).") for ExtKey $sAttCode."; return false; } } @@ -580,11 +607,11 @@ abstract class DBObject // Insert of record for the new object into the database // Returns the key of the newly created object - public function DBInsert() + public function DBInsertNoReload() { if ($this->m_bIsInDB) { - trigger_error("The object already exists into the Database, you may want to use the clone function", E_USER_ERROR); + throw new CoreException("The object already exists into the Database, you may want to use the clone function"); } $sClass = get_class($this); @@ -604,7 +631,7 @@ abstract class DBObject { if (empty($this->m_iKey)) { - trigger_error("Missing key for the object to write - This class is supposed to have a user defined key, not an autonumber", E_USER_NOTICE); + throw new CoreWarning("Missing key for the object to write - This class is supposed to have a user defined key, not an autonumber"); } } @@ -629,6 +656,12 @@ abstract class DBObject // Reload to update the external attributes $this->m_bIsInDB = true; + return $this->m_iKey; + } + + public function DBInsert() + { + $this->DBInsertNoReload(); $this->Reload(); return $this->m_iKey; } @@ -647,12 +680,12 @@ abstract class DBObject { if (!$this->m_bIsInDB) { - trigger_error("DBUpdate: could not update a newly created object, please call DBInsert instead", E_USER_ERROR); + throw new CoreException("DBUpdate: could not update a newly created object, please call DBInsert instead"); } $aChanges = $this->ListChanges(); if (count($aChanges) == 0) { - trigger_error("Attempting to update an unchanged object", E_USER_NOTICE); + throw new CoreWarning("Attempting to update an unchanged object"); return; } $bHasANewExternalKeyValue = false; @@ -741,7 +774,7 @@ abstract class DBObject if (!is_callable($aActionCallSpec)) { - trigger_error("Unable to call action: ".get_class($this)."::$sActionHandler", E_USER_ERROR); + throw new CoreException("Unable to call action: ".get_class($this)."::$sActionHandler"); return; } $bRet = call_user_func($aActionCallSpec, $sStimulusCode); diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index e7fa9dd95..86d6e057a 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -309,12 +309,12 @@ class DBObjectSearch { if (!MetaModel::IsValidKeyAttCode($this->GetClass(), $sExtKeyAttCode)) { - trigger_error("The attribute code '$sExtKeyAttCode' is not an external key of the class '{$this->GetClass()}' - the condition will be ignored", E_USER_WARNING); + throw new CoreWarning("The attribute code '$sExtKeyAttCode' is not an external key of the class '{$this->GetClass()}' - the condition will be ignored"); } $oAttExtKey = MetaModel::GetAttributeDef($this->GetClass(), $sExtKeyAttCode); if(!MetaModel::IsSameFamilyBranch($oFilter->GetClass(), $oAttExtKey->GetTargetClass())) { - trigger_error("The specified filter (pointing to {$oFilter->GetClass()}) is not compatible with the key '{$this->GetClass()}::$sExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}", E_USER_ERROR); + throw new CoreException("The specified filter (pointing to {$oFilter->GetClass()}) is not compatible with the key '{$this->GetClass()}::$sExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}"); } if (array_key_exists($sExtKeyAttCode, $this->m_aPointingTo)) @@ -347,12 +347,12 @@ class DBObjectSearch $sForeignClassAlias = $oFilter->GetClassAlias(); if (!MetaModel::IsValidKeyAttCode($sForeignClass, $sForeignExtKeyAttCode)) { - trigger_error("The attribute code '$sForeignExtKeyAttCode' is not an external key of the class '{$sForeignClass}' - the condition will be ignored", E_USER_WARNING); + throw new CoreException("The attribute code '$sForeignExtKeyAttCode' is not an external key of the class '{$sForeignClass}' - the condition will be ignored"); } $oAttExtKey = MetaModel::GetAttributeDef($sForeignClass, $sForeignExtKeyAttCode); if(!MetaModel::IsSameFamilyBranch($this->GetClass(), $oAttExtKey->GetTargetClass())) { - trigger_error("The specified filter (objects referencing an object of class {$this->GetClass()}) is not compatible with the key '{$sForeignClass}::$sForeignExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}", E_USER_ERROR); + throw new CoreException("The specified filter (objects referencing an object of class {$this->GetClass()}) is not compatible with the key '{$sForeignClass}::$sForeignExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}"); } if (array_key_exists($sForeignClass, $this->m_aReferencedBy) && array_key_exists($sForeignExtKeyAttCode, $this->m_aReferencedBy[$sForeignClass])) { @@ -396,7 +396,7 @@ class DBObjectSearch { if ($this->GetClass() != $oFilter->GetClass()) { - trigger_error("Attempting to merge a filter of class '{$this->GetClass()}' with a filter of class '{$oFilter->GetClass()}'", E_USER_ERROR); + throw new CoreException("Attempting to merge a filter of class '{$this->GetClass()}' with a filter of class '{$oFilter->GetClass()}'"); } // Translate search condition into our aliasing scheme @@ -546,7 +546,7 @@ class DBObjectSearch $iMaxDepth = $aCondition[3]; $oFilter->AddCondition_RelatedTo($oSubFilter, $sRelCode, $iMaxDepth); default: - trigger_error("invalid filter definition (cannot unserialize the data, clear text = '$sClearText')", E_USER_ERROR); + throw new CoreException("invalid filter definition (cannot unserialize the data, clear text = '$sClearText')"); } } return $oFilter; @@ -662,7 +662,7 @@ class DBObjectSearch } if (count($aParams) > 0) { - trigger_error("Unused parameter(s) for this SibusQL expression: (".implode(', ', array_keys($aParams)).")"); + throw new CoreException("Unused parameter(s) for this SibusQL expression: (".implode(', ', array_keys($aParams)).")"); } return $sQuery; } @@ -931,7 +931,7 @@ class DBObjectSearch } else { - trigger_error("Wrong format for filter definition: '$sQuery'"); + throw new CoreException("Wrong format for filter definition: '$sQuery'"); } } } diff --git a/core/dbobjectset.class.php b/core/dbobjectset.class.php index 4ab3761a9..d2719c5cd 100644 --- a/core/dbobjectset.class.php +++ b/core/dbobjectset.class.php @@ -170,11 +170,6 @@ class DBObjectSet $this->m_aId2Row[$oObject->GetKey()] = $iNextPos; } - public function RemoveObject($iRow) - { - trigger_error("#@# not implemented! ca sert a quoi ?"); - } - public function AddObjectArray($aObjects) { foreach ($aObjects as $oObj) @@ -187,7 +182,7 @@ class DBObjectSet { if ($this->GetRootClass() != $oObjectSet->GetRootClass()) { - trigger_error("Could not merge two objects sets if they don't have the same root class"); + throw new CoreException("Could not merge two objects sets if they don't have the same root class"); } if (!$this->m_bLoaded) $this->Load(); @@ -202,7 +197,7 @@ class DBObjectSet { if ($this->GetRootClass() != $oObjectSet->GetRootClass()) { - trigger_error("Could not 'intersect' two objects sets if they don't have the same root class"); + throw new CoreException("Could not 'intersect' two objects sets if they don't have the same root class"); } if (!$this->m_bLoaded) $this->Load(); @@ -223,7 +218,7 @@ class DBObjectSet { if ($this->GetRootClass() != $oObjectSet->GetRootClass()) { - trigger_error("Could not 'delta' two objects sets if they don't have the same root class"); + throw new CoreException("Could not 'delta' two objects sets if they don't have the same root class"); } if (!$this->m_bLoaded) $this->Load(); diff --git a/core/filterdef.class.inc.php b/core/filterdef.class.inc.php index ec09f50a0..371eadbd5 100644 --- a/core/filterdef.class.inc.php +++ b/core/filterdef.class.inc.php @@ -37,7 +37,7 @@ abstract class FilterDefinition { if (!array_key_exists($sParam, $this->m_aParams)) { - trigger_error("Unknown attribute definition parameter '$sParam', please select a value in {".implode(", ", $this->m_aParams)."}"); + throw new CoreException("Unknown attribute definition parameter '$sParam', please select a value in {".implode(", ", $this->m_aParams)."}"); } else { @@ -64,7 +64,7 @@ abstract class FilterDefinition $aBacktrace = debug_backtrace(); $sTargetClass = $aBacktrace[2]["class"]; $sCodeInfo = $aBacktrace[1]["file"]." - ".$aBacktrace[1]["line"]; - trigger_error("ERROR missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)
\n", E_USER_ERROR); + throw new CoreException("ERROR missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)"); } } } @@ -86,7 +86,7 @@ abstract class FilterDefinition $aOperators = $this->GetOperators(); if (!array_key_exists($sOpCode, $aOperators)) { - trigger_error("Unknown operator '$sOpCode'", E_USER_ERROR); + throw new CoreException("Unknown operator '$sOpCode'"); } return $aOperators[$sOpCode]; @@ -145,11 +145,11 @@ class FilterPrivateKey extends FilterDefinition switch($sOpCode) { case "IN": - if (!is_array($sQValue)) trigger_error("Expected an array for argument value (sOpCode='$sOpCode')"); + if (!is_array($sQValue)) throw new CoreException("Expected an array for argument value (sOpCode='$sOpCode')"); return "$sFieldName IN (".implode(", ", $sQValue).")"; case "NOTIN": - if (!is_array($sQValue)) trigger_error("Expected an array for argument value (sOpCode='$sOpCode')"); + if (!is_array($sQValue)) throw new CoreException("Expected an array for argument value (sOpCode='$sOpCode')"); return "$sFieldName NOT IN (".implode(", ", $sQValue).")"; case "!=": diff --git a/core/metamodel.class.php b/core/metamodel.class.php index e1927f8f5..275ad454a 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -108,7 +108,7 @@ abstract class MetaModel // (it is not possible to guess it when called as myderived::...) if (!array_key_exists($sClass, self::$m_aClassParams)) { - trigger_error("Unknown class '$sClass', expected a value in {".implode(', ', array_keys(self::$m_aClassParams))."}", E_USER_ERROR); + throw new CoreException("Unknown class '$sClass', expected a value in {".implode(', ', array_keys(self::$m_aClassParams))."}"); } } @@ -685,7 +685,7 @@ abstract class MetaModel { if (!array_key_exists($sParamName, $aListInfo)) { - trigger_error("Declaration of list $sListCode - missing parameter $sParamName", E_USER_ERROR); + throw new CoreException("Declaration of list $sListCode - missing parameter $sParamName"); } } @@ -704,7 +704,7 @@ abstract class MetaModel { if (!array_key_exists($sParamName, $aRelationInfo)) { - trigger_error("Declaration of relation $sRelCode - missing parameter $sParamName", E_USER_ERROR); + throw new CoreException("Declaration of relation $sRelCode - missing parameter $sParamName"); } } @@ -716,7 +716,7 @@ abstract class MetaModel { if (count(self::GetClasses()) > 0) { - trigger_error("InitClasses should not be called more than once -skipped"); + throw new CoreException("InitClasses should not be called more than once -skipped"); return; } @@ -769,11 +769,11 @@ abstract class MetaModel // if (array_key_exists('id', self::$m_aAttribDefs[$sClass])) { - trigger_error("Class $sClass, 'id' is a reserved keyword, it cannot be used as an attribute code", E_USER_ERROR); + throw new CoreException("Class $sClass, 'id' is a reserved keyword, it cannot be used as an attribute code"); } if (array_key_exists('id', self::$m_aFilterDefs[$sClass])) { - trigger_error("Class $sClass, 'id' is a reserved keyword, it cannot be used as a filter code", E_USER_ERROR); + throw new CoreException("Class $sClass, 'id' is a reserved keyword, it cannot be used as a filter code"); } $oFilter = new FilterPrivateKey('id', array('id_field' => self::DBGetKey($sClass))); self::$m_aFilterDefs[$sClass]['id'] = $oFilter; @@ -785,11 +785,11 @@ abstract class MetaModel { if (array_key_exists('finalclass', self::$m_aAttribDefs[$sClass])) { - trigger_error("Class $sClass, 'finalclass' is a reserved keyword, it cannot be used as an attribute code", E_USER_ERROR); + throw new CoreException("Class $sClass, 'finalclass' is a reserved keyword, it cannot be used as an attribute code"); } if (array_key_exists('finalclass', self::$m_aFilterDefs[$sClass])) { - trigger_error("Class $sClass, 'finalclass' is a reserved keyword, it cannot be used as a filter code", E_USER_ERROR); + throw new CoreException("Class $sClass, 'finalclass' is a reserved keyword, it cannot be used as a filter code"); } $sClassAttCode = 'finalclass'; $sRootClass = self::GetRootClass($sClass); @@ -846,14 +846,14 @@ abstract class MetaModel $sClass = self::GetCallersPHPClass("Init"); if (!array_key_exists("name", $aParams)) { - trigger_error("Declaration of class $sClass: missing name ({$aMandatParams["name"]})", E_USER_ERROR); + throw new CoreException("Declaration of class $sClass: missing name ({$aMandatParams["name"]})"); } foreach($aMandatParams as $sParamName=>$sParamDesc) { if (!array_key_exists($sParamName, $aParams)) { - trigger_error("Declaration of class $sClass - missing parameter $sParamName", E_USER_ERROR); + throw new CoreException("Declaration of class $sClass - missing parameter $sParamName"); } } @@ -938,7 +938,7 @@ abstract class MetaModel if (!self::IsValidAttCode($sTargetClass, $sAttCode)) { - trigger_error("Could not overload '$sAttCode', expecting a code from {".implode(", ", self::GetAttributesList($sTargetClass))."}"); + throw new CoreException("Could not overload '$sAttCode', expecting a code from {".implode(", ", self::GetAttributesList($sTargetClass))."}"); } self::$m_aAttribDefs[$sTargetClass][$sAttCode]->OverloadParams($aParams); } @@ -1000,7 +1000,7 @@ abstract class MetaModel if (!self::IsValidFilterCode($sTargetClass, $sFltCode)) { - trigger_error("Could not overload '$sFltCode', expecting a code from {".implode(", ", self::GetFiltersList($sTargetClass))."}"); + throw new CoreException("Could not overload '$sFltCode', expecting a code from {".implode(", ", self::GetFiltersList($sTargetClass))."}"); } self::$m_aFilterDefs[$sTargetClass][$sFltCode]->OverloadParams($aParams); } @@ -1150,7 +1150,7 @@ abstract class MetaModel if (count(self::$m_Category2Class) > 0) { - trigger_error("unkown class category '$sCategory', expecting a value in {".implode(', ', array_keys(self::$m_Category2Class))."}"); + throw new CoreException("unkown class category '$sCategory', expecting a value in {".implode(', ', array_keys(self::$m_Category2Class))."}"); } return array(); } @@ -1175,7 +1175,7 @@ abstract class MetaModel MyHelpers::CheckValueInArray('field name in ORDER BY spec', $sFieldAlias, self::GetAttributesList($oFilter->GetClass())); if (!is_bool($bAscending)) { - trigger_error("Wrong direction in ORDER BY spec, found '$bAscending' and expecting a boolean value"); + throw new CoreException("Wrong direction in ORDER BY spec, found '$bAscending' and expecting a boolean value"); } } if (empty($aOrderBy)) @@ -1565,8 +1565,7 @@ abstract class MetaModel { if (count(self::GetClasses()) == 0) { - trigger_error("MetaModel::InitClasses() has not been called, or no class has been declared ?!?!"); - exit; + throw new CoreException("MetaModel::InitClasses() has not been called, or no class has been declared ?!?!"); } $aErrors = array(); @@ -2229,7 +2228,7 @@ abstract class MetaModel } if ($iPlannedDel > $iMaxDel) { - trigger_error("DB Integrity Check safety net - Exceeding the limit of $iMaxDel planned record deletion", E_USER_WARNING); + throw new CoreWarning("DB Integrity Check safety net - Exceeding the limit of $iMaxDel planned record deletion"); break; } // Safety net #2 - limit the iterations @@ -2238,7 +2237,7 @@ abstract class MetaModel $iMaxLoops = 10; if ($iLoopCount > $iMaxLoops) { - trigger_error("DB Integrity Check safety net - Reached the limit of $iMaxLoops loops", E_USER_WARNING); + throw new CoreWarning("DB Integrity Check safety net - Reached the limit of $iMaxLoops loops"); break; } } @@ -2410,21 +2409,50 @@ abstract class MetaModel // Building an object // // + private static $aQueryCacheGetObject = array(); + private static $aQueryCacheGetObjectHits = array(); + public static function GetQueryCacheStatus() + { + $aRes = array(); + $iTotalHits = 0; + foreach(self::$aQueryCacheGetObjectHits as $sClass => $iHits) + { + $aRes[] = "$sClass: $iHits"; + $iTotalHits += $iHits; + } + return $iTotalHits.' ('.implode(', ', $aRes).')'; + } public static function MakeSingleRow($sClass, $iKey) { - $oFilter = new DBObjectSearch($sClass); - $oFilter->AddCondition('id', $iKey, '='); - - $sSQL = self::MakeSelectQuery($oFilter); - //echo "$sSQL
\n"; + if (!array_key_exists($sClass, self::$aQueryCacheGetObject)) + { + // NOTE: Quick and VERY dirty caching mechanism which relies on + // the fact that the string '987654321' will never appear in the + // standard query + // This will be replaced for sure with a prepared statement + // or a view... next optimization to come! + $oFilter = new DBObjectSearch($sClass); + $oFilter->AddCondition('id', 987654321, '='); + + $sSQL = self::MakeSelectQuery($oFilter); + self::$aQueryCacheGetObject[$sClass] = $sSQL; + self::$aQueryCacheGetObjectHits[$sClass] = 0; + } + else + { + $sSQL = self::$aQueryCacheGetObject[$sClass]; + self::$aQueryCacheGetObjectHits[$sClass] += 1; +// echo " -load $sClass/$iKey- ".self::$aQueryCacheGetObjectHits[$sClass]."
\n"; + } + $sSQL = str_replace('987654321', CMDBSource::Quote($iKey), $sSQL); $res = CMDBSource::Query($sSQL); $aRow = CMDBSource::FetchArray($res); CMDBSource::FreeResult($res); if (empty($aRow)) { - trigger_error("No result for the single row query: '$sSQL'"); + throw new CoreException("No result for the single row query: '$sSQL'"); } return $aRow; } @@ -2446,7 +2474,7 @@ abstract class MetaModel // @#@ possible improvement: check that the class is valid ! $sRootClass = self::GetRootClass($sClass); $sFinalClassField = self::DBGetClassField($sRootClass); - trigger_error("Empty class name for object $sClass::{$aRow["id"]} (root class '$sRootClass', field '{$sFinalClassField}' is empty)", E_USER_ERROR); + throw new CoreException("Empty class name for object $sClass::{$aRow["id"]} (root class '$sRootClass', field '{$sFinalClassField}' is empty)"); } else { @@ -2462,7 +2490,6 @@ abstract class MetaModel $aRow = self::MakeSingleRow($sClass, $iKey); if (empty($aRow)) { - // #@# exception ? return null; } return self::GetObjectByRow($sClass, $aRow); diff --git a/core/sqlquery.class.inc.php b/core/sqlquery.class.inc.php index 06a8bd35f..8a55d33c8 100644 --- a/core/sqlquery.class.inc.php +++ b/core/sqlquery.class.inc.php @@ -46,7 +46,7 @@ class SQLQuery { if (!CMDBSource::IsTable($sTable)) { - trigger_error("Unknown table '$sTable'", E_USER_ERROR); + throw new CoreException("Unknown table '$sTable'"); } // $aFields must be an array of "alias"=>"expr" // $oConditionExpr must be a condition tree @@ -137,7 +137,7 @@ class SQLQuery assert((get_class($oSQLQuery) == __CLASS__) || is_subclass_of($oSQLQuery, __CLASS__)); if (!CMDBSource::IsField($this->m_sTable, $sLeftField)) { - trigger_error("Unknown field '$sLeftField' in table '".$this->m_sTable, E_USER_ERROR); + throw new CoreException("Unknown field '$sLeftField' in table '".$this->m_sTable); } if (empty($sRightTableAlias)) { @@ -147,7 +147,7 @@ class SQLQuery // // if (!CMDBSource::IsField($sRightTable, $sRightField)) // { -// trigger_error("Unknown field '$sRightField' in table '".$sRightTable."'", E_USER_ERROR); +// throw new CoreException("Unknown field '$sRightField' in table '".$sRightTable."'"); // } $this->m_aJoinSelects[] = array( @@ -187,7 +187,7 @@ class SQLQuery if ($this->m_oConditionExpr->IsAny()) -- if (count($aConditions) == 0) -- { - trigger_error("Building a request wich will delete every object of a given table -looks suspicious- please use truncate instead...", E_USER_ERROR); + throw new CoreException("Building a request wich will delete every object of a given table -looks suspicious- please use truncate instead..."); } */ $sWhere = self::ClauseWhere($oCondition); @@ -277,7 +277,7 @@ class SQLQuery $sFrom .= ") ON ".$aJoinInfo["joincondition"]; break; default: - trigger_error("Unknown jointype: '".$aJoinInfo["jointype"]."'"); + throw new CoreException("Unknown jointype: '".$aJoinInfo["jointype"]."'"); } } return $sFrom; diff --git a/core/stimulus.class.inc.php b/core/stimulus.class.inc.php index ee22fdd81..d2326f6fc 100644 --- a/core/stimulus.class.inc.php +++ b/core/stimulus.class.inc.php @@ -44,7 +44,7 @@ class ObjectStimulus $aBacktrace = debug_backtrace(); $sTargetClass = $aBacktrace[2]["class"]; $sCodeInfo = $aBacktrace[1]["file"]." - ".$aBacktrace[1]["line"]; - trigger_error("ERROR missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)
\n", E_USER_ERROR); + throw new CoreException("missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)"); } } } diff --git a/core/test.class.inc.php b/core/test.class.inc.php index 286228c82..f20ebec07 100644 --- a/core/test.class.inc.php +++ b/core/test.class.inc.php @@ -150,11 +150,17 @@ abstract class TestHandler { $this->ReportError($e->getMessage().' - '.$e->getTraceAsHtml()); } + catch (CoreException $e) + { + //$this->ReportError($e->getMessage()); + //$this->ReportError($e->__tostring()); + $this->ReportError($e->getMessage().' - '.$e->getTraceAsHtml()); + } catch (Exception $e) { //$this->ReportError($e->getMessage()); //$this->ReportError($e->__tostring()); - $this->ReportError($e->getMessage().' - '.$e->getTraceAsString()); + $this->ReportError('class '.get_class($e).' --- '.$e->getMessage().' - '.$e->getTraceAsString()); } restore_error_handler(); $this->m_sOutput = ob_get_clean(); diff --git a/core/userrights.class.inc.php b/core/userrights.class.inc.php index fd1a0bee3..b62c8a042 100644 --- a/core/userrights.class.inc.php +++ b/core/userrights.class.inc.php @@ -77,12 +77,12 @@ class UserRights { if (!class_exists($sModuleName)) { - trigger_error("Could not select this module, '$sModuleName' in not a valid class name", E_USER_ERROR); + throw new CoreException("Could not select this module, '$sModuleName' in not a valid class name"); return; } if (!is_subclass_of($sModuleName, 'UserRightsAddOnAPI')) { - trigger_error("Could not select this module, the class '$sModuleName' is not derived from UserRightsAddOnAPI", E_USER_ERROR); + throw new CoreException("Could not select this module, the class '$sModuleName' is not derived from UserRightsAddOnAPI"); return; } self::$m_oAddOn = new $sModuleName; diff --git a/pages/csvimport.php b/pages/csvimport.php index 64f720726..0110e1f53 100644 --- a/pages/csvimport.php +++ b/pages/csvimport.php @@ -17,165 +17,6 @@ $oPage = new iTopWebPage("iTop - Bulk import", $currentOrganization); define ('EXTKEY_SEP', '::::'); define ('EXTKEY_LABELSEP', ' -> '); -class CSVParser -{ - private $m_sCSVData; - private $m_sSep; - private $m_iSkip; - - public function __construct($sTxt) - { - $this->m_sCSVData = $sTxt; - } - - public function SetSeparator($sSep) - { - $this->m_sSep = $sSep; - } - public function GetSeparator() - { - return $this->m_sSep; - } - - public function SetSkipLines($iSkip) - { - $this->m_iSkip = $iSkip; - } - public function GetSkipLines() - { - return $this->m_iSkip; - } - - public function GuessSeparator() - { - // Note: skip the first line anyway - - $aKnownSeps = array(';', ',', "\t"); // Use double quote for special chars!!! - $aStatsBySeparator = array(); - foreach ($aKnownSeps as $sSep) - { - $aStatsBySeparator[$sSep] = array(); - } - - foreach(split("\n", $this->m_sCSVData) as $sLine) - { - $sLine = trim($sLine); - if (substr($sLine, 0, 1) == '#') continue; - if (empty($sLine)) continue; - - $aLineCharsCount = count_chars($sLine, 0); - foreach ($aKnownSeps as $sSep) - { - $aStatsBySeparator[$sSep][] = $aLineCharsCount[ord($sSep)]; - } - } - - // Default to ',' - $this->SetSeparator(","); - - foreach ($aKnownSeps as $sSep) - { - // Note: this function is NOT available :-( - // stats_variance($aStatsBySeparator[$sSep]); - $iMin = min($aStatsBySeparator[$sSep]); - $iMax = max($aStatsBySeparator[$sSep]); - if (($iMin == $iMax) && ($iMax > 0)) - { - $this->SetSeparator($sSep); - break; - } - } - return $this->GetSeparator(); - } - - public function GuessSkipLines() - { - // Take the FIRST -valuable- LINE ONLY - // If there is a number, then for sure this is not a header line - // Otherwise, we may consider that there is one line to skip - foreach(split("\n", $this->m_sCSVData) as $sLine) - { - $sLine = trim($sLine); - if (substr($sLine, 0, 1) == '#') continue; - if (empty($sLine)) continue; - - foreach (split($this->m_sSep, $sLine) as $value) - { - if (is_numeric($value)) - { - $this->SetSkipLines(0); - return 0; - } - } - $this->SetSkipLines(1); - return 1; - } - } - - function ToArray($aFieldMap, $iMax = 0) - { - // $aFieldMap is an array of col_index=>col_name - // $iMax is a limit - $aRes = array(); - - $iCount = 0; - $iSkipped = 0; - foreach(split("\n", $this->m_sCSVData) as $sLine) - { - $sLine = trim($sLine); - if (substr($sLine, 0, 1) == '#') continue; - if (empty($sLine)) continue; - - if ($iSkipped < $this->m_iSkip) - { - $iSkipped++; - continue; - } - - foreach (split($this->m_sSep, $sLine) as $iCol=>$sValue) - { - if (is_array($aFieldMap)) $sColRef = $aFieldMap[$iCol]; - else $sColRef = "field$iCol"; - $aRes[$iCount][$sColRef] = $sValue; - } - - $iCount++; - if (($iMax > 0) && ($iCount >= $iMax)) break; - } - return $aRes; - } - - public function ListFields() - { - // Take the first valuable line - foreach(split("\n", $this->m_sCSVData) as $sLine) - { - $sLine = trim($sLine); - if (substr($sLine, 0, 1) == '#') continue; - if (empty($sLine)) continue; - // We've got the first valuable line, that's it! - break; - } - - $aRet = array(); - foreach (split($this->m_sSep, $sLine) as $iCol=>$value) - { - if ($this->m_iSkip == 0) - { - // No header to help us - $sLabel = "field $iCol"; - } - else - { - $sLabel = "$value"; - } - $aRet[] = $sLabel; - } - return $aRet; - } -} - - /////////////////////////////////////////////////////////////////////////////// // External key/field naming conventions (sharing the naming space with std attributes /////////////////////////////////////////////////////////////////////////////// @@ -291,176 +132,11 @@ function ShowTableForm($oPage, $oCSVParser, $sClass) $sSelField .= " "; $aFields["field$iFieldIndex"]["label"] = $sSelField; - $aFields["field$iFieldIndex"]["value"] = $aColToRow["field$iFieldIndex"]; + $aFields["field$iFieldIndex"]["value"] = $aColToRow[$iFieldIndex]; } $oPage->details($aFields); } -function PrepareObject(&$oTargetObj, $aRowData, $aAttList, $aExtKeys, &$aWarnings, &$aErrors) -{ - $aResults = array(); - $aWarnings = array(); - $aErrors = array(); - - // External keys reconciliation - // - foreach($aExtKeys as $sAttCode=>$aKeyConfig) - { - $oExtKey = MetaModel::GetAttributeDef(get_class($oTargetObj), $sAttCode); - $oReconFilter = new CMDBSearchFilter($oExtKey->GetTargetClass()); - foreach ($aKeyConfig as $iCol => $sForeignAttCode) - { - // The foreign attribute is one of our reconciliation key - $sFieldId = MakeExtFieldSelectValue($sAttCode, $sForeignAttCode); - $oReconFilter->AddCondition($sForeignAttCode, $aRowData[$sFieldId], '='); - $aResults["col$iCol"]= "
".$aRowData[$sFieldId]."
"; - } - $oExtObjects = new CMDBObjectSet($oReconFilter); - switch($oExtObjects->Count()) - { - case 0: - $aErrors[$sAttCode] = "Object not found"; - $aResults[$sAttCode]= "
".$aErrors[$sAttCode]."
"; - break; - case 1: - // Do change the external key attribute - $oForeignObj = $oExtObjects->Fetch(); - $oTargetObj->Set($sAttCode, $oForeignObj->GetKey()); - - // Report it - if (array_key_exists($sAttCode, $oTargetObj->ListChanges())) - { - $aResults[$sAttCode]= "
".$oForeignObj->GetHyperLink()."
"; - } - else - { - $aResults[$sAttCode]= "
".$oForeignObj->GetHyperLink()."
"; - } - break; - default: - $aErrors[$sAttCode] = "Found ".$oExtObjects->Count()." matches"; - $aResults[$sAttCode]= "
".$aErrors[$sAttCode]."
"; - } - } - - // Set the object attributes - // - foreach ($aAttList as $iCol => $sAttCode) - { - $oTargetObj->Set($sAttCode, $aRowData[$sAttCode]); - } - - // Reporting on fields - // - $aChangedFields = $oTargetObj->ListChanges(); - foreach ($aAttList as $iCol => $sAttCode) - { - // By default... nothing happens - $sClass = ""; - $sMoreInfo = ""; - - // Override if the attribute has changed - if (array_key_exists($sAttCode, $aChangedFields)) - { - $sClass = "csvimport_ok"; - } - - // Override if a warning is found - if (isset($aWarnings[$sAttCode])) - { - $sClass = "csvimport_warning"; - $sMoreInfo .= ", ".$aWarnings[$sAttCode]; - } - - // Override if an error is found - if (isset($aErrors[$sAttCode])) - { - $sClass = "csvimport_error"; - $sMoreInfo = ", ".$aErrors[$sAttCode]; - } - - $aResults["col$iCol"]= "
".$aRowData[$sAttCode].$sMoreInfo."
"; - } - - // Checks - // - if (!$oTargetObj->CheckConsistency()) - { - $aErrors["GLOBAL"] = "Attributes not consistent with each others"; - } - return $aResults; -} - - -function CreateObject(&$aResult, $iRow, $sClass, $aRowData, $aAttList, $aExtKeys, CMDBChange $oChange = null) -{ - $oTargetObj = MetaModel::NewObject($sClass); - $aResult[$iRow] = PrepareObject($oTargetObj, $aRowData, $aAttList, $aExtKeys, $aWarnings, $aErrors); - - if (count($aErrors) > 0) - { - $sErrors = implode(', ', $aErrors); - $aResult[$iRow]["__STATUS__"] = "Unexpected attribute value(s)"; - return; - } - - // Check that any external key will have a value proposed - // Could be said once for all rows !!! - foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode=>$oAtt) - { - if (!$oAtt->IsExternalKey()) continue; - //if (!in_array($sAttCode, $aAttList)) - //{ - // $aResult[$iRow]["__STATUS__"] = "Could not be created - Missing external key (".$oAtt->GetLabel().")"; - // return; - //} - } - - // Optionaly record the results - // - if ($oChange) - { - $newID = $oTargetObj->DBInsertTracked($oChange); - $aResult[$iRow]["__STATUS__"] = "Created: ".$oTargetObj->GetHyperLink($newID); - } - else - { - $aResult[$iRow]["__STATUS__"] = "Create"; - } - -} - -function UpdateObject(&$aResult, $iRow, $oTargetObj, $aRowData, $aAttList, $aExtKeys, CMDBChange $oChange = null) -{ - $aResult[$iRow] = PrepareObject($oTargetObj, $aRowData, $aAttList, $aExtKeys, $aWarnings, $aErrors); - - // Reporting - // - if (count($aErrors) > 0) - { - $sErrors = implode(', ', $aErrors); - $aResult[$iRow]["__STATUS__"] = "Unexpected attribute value(s)"; - return; - } - - $aChangedFields = $oTargetObj->ListChanges(); - if (count($aChangedFields) > 0) - { - $sVerb = $oChange ? "Updated" : "Update"; - $aResult[$iRow]["__STATUS__"] = "$sVerb ".count($aChangedFields)." cols"; - - // Optionaly record the results - // - if ($oChange) - { - $oTargetObj->DBUpdateTracked($oChange); - } - } - else - { - $aResult[$iRow]["__STATUS__"] = "No change"; - } -} function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CMDBChange $oChange = null) { @@ -487,18 +163,18 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM elseif (IsExtKeyField($sColDesc)) { list($sExtKeyAttCode, $sExtReconcKeyAttCode) = GetExtKeyFieldCodes($sColDesc); - $aExtKeys[$sExtKeyAttCode][$iFieldId] = $sExtReconcKeyAttCode; + $aExtKeys[$sExtKeyAttCode][$sExtReconcKeyAttCode] = $iFieldId; } elseif (array_key_exists($sFieldId, $aIsReconcKey)) { - $aReconcilKeys[$iFieldId] = $sColDesc; - $aAttList[$iFieldId] = $sColDesc; // A reconciliation key is also a field + $aReconcilKeys[$sColDesc] = $iFieldId; + $aAttList[$sColDesc] = $iFieldId; // A reconciliation key is also a field } else { // $sColDesc is an attribute code // - $aAttList[$iFieldId] = $sColDesc; + $aAttList[$sColDesc] = $iFieldId; } } @@ -511,7 +187,7 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM { $aDisplayConfig["col$iPKeyId"] = array("label"=>"pkey", "description"=>""); } - foreach($aReconcilKeys as $iCol=>$sAttCode) + foreach($aReconcilKeys as $sAttCode => $iCol) { $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel(); $aDisplayConfig["col$iCol"] = array("label"=>"$sLabel", "description"=>""); @@ -521,7 +197,7 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM $oExtKeyAtt = MetaModel::GetAttributeDef($sClass, $sAttCode); $sLabel = $oExtKeyAtt->GetLabel(); $aDisplayConfig[$sAttCode] = array("label"=>"$sLabel", "description"=>""); - foreach ($aKeyConfig as $iCol => $sForeignAttCode) + foreach ($aKeyConfig as $sForeignAttCode => $iCol) { // The foreign attribute is one of our reconciliation key @@ -529,7 +205,7 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM $aDisplayConfig["col$iCol"] = array("label"=>"$sLabel", "description"=>""); } } - foreach ($aAttList as $iCol => $sAttCode) + foreach ($aAttList as $sAttCode => $iCol) { $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel(); $aDisplayConfig["col$iCol"] = array("label"=>"$sLabel", "description"=>""); @@ -537,53 +213,58 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM // Compute the results // - $aResult = array(); - foreach($oCSVParser->ToArray(array_values($aFieldMap)) as $iRow => $aRowData) - { - $oReconciliationFilter = new CMDBSearchFilter($sClass); - if (isset($iPKeyId)) - { - $oReconciliationFilter->AddCondition("pkey", $aRowData["pkey"], '='); - } - foreach($aReconcilKeys as $iCol=>$sAttCode) - { - $sSearchAttCode = $aFieldMap['field'.$iCol]; - $oReconciliationFilter->AddCondition($sSearchAttCode, $aRowData[$sSearchAttCode], '='); - } - $oReconciliationSet = new CMDBObjectSet($oReconciliationFilter); - switch($oReconciliationSet->Count()) - { - case 0: - CreateObject($aResult, $iRow, $sClass, $aRowData, $aAttList, $aExtKeys, $oChange); - // $aResult[$iRow]["__STATUS__"]=> set in CreateObject - $aResult[$iRow]["__RECONCILIATION__"] = "Object not found"; - break; - case 1: - $oTargetObj = $oReconciliationSet->Fetch(); - UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $aAttList, $aExtKeys, $oChange); - $aResult[$iRow]["__RECONCILIATION__"] = "Found a ".$oTargetObj->GetHyperLink("match"); - // $aResult[$iRow]["__STATUS__"]=> set in UpdateObject - break; - default: - foreach ($aAttList as $iCol => $sAttCode) - { - $aResult[$iRow]["col$iCol"]= $aRowData[$sAttCode]; - } - $aResult[$iRow]["__RECONCILIATION__"] = "Found ".$oReconciliationSet->Count()." matches"; - $aResult[$iRow]["__STATUS__"]= "skipped"; - } + $aData = $oCSVParser->ToArray(); - // Whatever happened, do report the reconciliation values - if (isset($iPKeyId)) + $oBulk = new BulkChange( + $sClass, + $aData, + $aAttList, + array_keys($aReconcilKeys), + $aExtKeys + ); + $aRes = $oBulk->Process($oChange); + + $aResultDisp = array(); // to be displayed + foreach($aRes as $iRow => $aRowData) + { + $aRowDisp = array(); + $aRowDisp["__RECONCILIATION__"] = $aRowData["__RECONCILIATION__"]; + $aRowDisp["__STATUS__"] = $aRowData["__STATUS__"]->GetDescription(); + foreach($aRowData as $sKey => $value) { - $aResult[$iRow]["col$iPKeyId"] = "
".$aRowData["pkey"]."
"; - } - foreach($aReconcilKeys as $iCol=>$sAttCode) - { - $aResult[$iRow]["col$iCol"] = "
".$aRowData[$sAttCode]."
"; + if ($sKey == '__RECONCILIATION__') continue; + if ($sKey == '__STATUS__') continue; + + switch (get_class($value)) + { + case 'CellChangeSpec_Void': + $sClass = ''; + break; + case 'CellChangeSpec_Unchanged': + $sClass = ''; + break; + case 'CellChangeSpec_Modify': + $sClass = 'csvimport_ok'; + break; + case 'CellChangeSpec_Init': + $sClass = 'csvimport_init'; + break; + case 'CellChangeSpec_Issue': + $sClass = 'csvimport_error'; + break; + } + if (empty($sClass)) + { + $aRowDisp[$sKey] = $value->GetDescription(); + } + else + { + $aRowDisp[$sKey] = "
".$value->GetDescription()."
"; + } } + $aResultDisp[$iRow] = $aRowDisp; } - $oPage->table($aDisplayConfig, $aResult); + $oPage->table($aDisplayConfig, $aResultDisp); } /////////////////////////////////////////////////////////////////////////////// @@ -710,7 +391,7 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) $oPage->p("

Goal summary

"); $oPage->p("Target: $iTarget rows"); - $aSampleData = $oCSVParser->ToArray(null, 5); + $aSampleData = $oCSVParser->ToArray(array_keys($aFieldMap), 5); $aDisplayConfig = array(); foreach ($aFieldMap as $sFieldId=>$sColDesc) { diff --git a/pages/testlist.inc.php b/pages/testlist.inc.php index 23f259dc9..947d48489 100644 --- a/pages/testlist.inc.php +++ b/pages/testlist.inc.php @@ -612,8 +612,23 @@ class TestQueriesOnFarm extends TestBizModel MetaModel::DBCheckIntegrity(); } + protected $m_oChange; + protected function ObjectToDB(CMDBObject $oNew) + { + if (!isset($this->m_oChange)) + { + new CMDBChange(); + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Administrator"); + $iChangeId = $oMyChange->DBInsertNoReload(); + $this->m_oChange = $oMyChange; + } + $iId = $oNew->DBInsertTrackedNoReload($this->m_oChange); + return $iId; +} - private function InsertMammal($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $sName, $iHeight, $sBirth) + protected function InsertMammal($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $sName, $iHeight, $sBirth) { $oNew = MetaModel::NewObject('Mammal'); $oNew->Set('species', $sSpecies); @@ -624,11 +639,10 @@ class TestQueriesOnFarm extends TestBizModel $oNew->Set('name', $sName); $oNew->Set('height', $iHeight); $oNew->Set('birth', $sBirth); - $iId = $oNew->DBInsert(); - return $iId; + return $this->ObjectToDB($oNew); } - private function InsertBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId) + protected function InsertBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId) { $oNew = MetaModel::NewObject('Bird'); $oNew->Set('species', $sSpecies); @@ -636,11 +650,10 @@ class TestQueriesOnFarm extends TestBizModel $oNew->Set('speed', $iSpeed); $oNew->Set('mother', $iMotherid); $oNew->Set('father', $iFatherId); - $iId = $oNew->DBInsert(); - return $iId; + return $this->ObjectToDB($oNew); } - private function InsertFlyingBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $iFlyingSpeed) + protected function InsertFlyingBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $iFlyingSpeed) { $oNew = MetaModel::NewObject('FlyingBird'); $oNew->Set('species', $sSpecies); @@ -649,8 +662,7 @@ class TestQueriesOnFarm extends TestBizModel $oNew->Set('mother', $iMotherid); $oNew->Set('father', $iFatherId); $oNew->Set('flyingspeed', $iFlyingSpeed); - $iId = $oNew->DBInsert(); - return $iId; + return $this->ObjectToDB($oNew); } private function InsertGroup($sName, $iLeaderId) @@ -658,7 +670,7 @@ class TestQueriesOnFarm extends TestBizModel $oNew = MetaModel::NewObject('Group'); $oNew->Set('name', $sName); $oNew->Set('leader', $iLeaderId); - $iId = $oNew->DBInsert(); + $iId = $oNew->DBInsertNoReload(); return $iId; } @@ -926,6 +938,131 @@ class TestBulkChangeOnFarm extends TestBizModel } } + +/////////////////////////////////////////////////////////////////////////// +// Benchmark queries +/////////////////////////////////////////////////////////////////////////// + +class TestItopEfficiency extends TestBizModel +{ + static public function GetName() + { + return 'Itop - benchmark'; + } + + static public function GetDescription() + { + return 'Measure time to perform the queries'; + } + + static public function GetConfigFile() {return '../config-itop.php';} + + protected function DoBenchmark($sOqlQuery) + { + echo "

Testing query: $sOqlQuery

"; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $oFilter = DBObjectSearch::FromOQL($sOqlQuery); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fParsingDuration = $fDuration / COUNT_BENCHMARK; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $sSQL = MetaModel::MakeSelectQuery($oFilter); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fBuildDuration = $fDuration / COUNT_BENCHMARK; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $res = CMDBSource::Query($sSQL); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fQueryDuration = $fDuration / COUNT_BENCHMARK; + + // The fetch could not be repeated with the same results + // But we've seen so far that is was very very quick to exec + // So it makes sense to benchmark it a single time + $fStart = MyHelpers::getmicrotime(); + $aRow = CMDBSource::FetchArray($res); + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fFetchDuration = $fDuration; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $sOql = $oFilter->ToOQL(); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fToOqlDuration = $fDuration / COUNT_BENCHMARK; + + echo "\n"; + + // Everything but the ToOQL (wich is interesting, anyhow) + $fTotal = $fParsingDuration + $fBuildDuration + $fQueryDuration + $fFetchDuration; + + return array( + 'rows' => CMDBSource::NbRows($res), + 'duration (s)' => round($fTotal, 4), + 'parsing (%)' => round(100 * $fParsingDuration / $fTotal, 1), + 'build SQL (%)' => round(100 * $fBuildDuration / $fTotal, 1), + 'query exec (%)' => round(100 * $fQueryDuration / $fTotal, 1), + 'fetch (%)' => round(100 * $fFetchDuration / $fTotal, 1), + 'to OQL (%)' => round(100 * $fToOqlDuration / $fTotal, 1), + 'parsing+build (%)' => round(100 * ($fParsingDuration + $fBuildDuration) / $fTotal, 1), + ); + } + + protected function DoExecute() + { + define ('COUNT_BENCHMARK', 3); + echo "

The test will be repeated ".COUNT_BENCHMARK." times

"; + + $aQueries = array( + 'SELECT CMDBChangeOpSetAttribute', + 'SELECT CMDBChangeOpSetAttribute WHERE id=10', + 'SELECT CMDBChangeOpSetAttribute WHERE id=123456789', + 'SELECT CMDBChangeOpSetAttribute WHERE CMDBChangeOpSetAttribute.id=10', + 'SELECT bizIncidentTicket', + 'SELECT bizIncidentTicket WHERE id=1', + 'SELECT bizPerson', + 'SELECT bizPerson WHERE id=1', + 'SELECT bizIncidentTicket JOIN bizPerson ON bizIncidentTicket.agent_id = bizPerson.id WHERE bizPerson.id = 5', + ); + $aStats = array(); + foreach ($aQueries as $sOQL) + { + $aStats[$sOQL] = $this->DoBenchmark($sOQL); + } + + $aData = array(); + foreach ($aStats as $sOQL => $aResults) + { + $aValues = array(); + $aValues['OQL'] = htmlentities($sOQL); + + foreach($aResults as $sDesc => $sInfo) + { + $aValues[$sDesc] = htmlentities($sInfo); + } + $aData[] = $aValues; + } + echo MyHelpers::make_table_from_assoc_array($aData); + return true; + } +} + /////////////////////////////////////////////////////////////////////////// // Test data load /////////////////////////////////////////////////////////////////////////// diff --git a/setup/xmldataloader.class.inc.php b/setup/xmldataloader.class.inc.php index 5af0bebd9..f4149aa41 100644 --- a/setup/xmldataloader.class.inc.php +++ b/setup/xmldataloader.class.inc.php @@ -163,10 +163,14 @@ class XMLDataLoader { $iExtKey = -$iDstObj; // Convention: Unresolved keys are stored as negative ! } + // tested by Romain, little impact on perf (not significant on the intial setup) + //$oTargetObj->CheckValue($sAttCode, $iExtKey); $oTargetObj->Set($sAttCode, $iExtKey); } else { + // tested by Romain, little impact on perf (not significant on the intial setup) + //$oTargetObj->CheckValue($sAttCode, (string)$oXmlObj->$sAttCode); $oTargetObj->Set($sAttCode, (string)$oXmlObj->$sAttCode); } } @@ -200,11 +204,11 @@ class XMLDataLoader { if (is_subclass_of($oTargetObj, 'CMDBObject')) { - $iObjId = $oTargetObj->DBInsertTracked($this->m_oChange); + $iObjId = $oTargetObj->DBInsertTrackedNoReload($this->m_oChange); } else { - $iObjId = $oTargetObj->DBInsert(); + $iObjId = $oTargetObj->DBInsertNoReload(); } } diff --git a/webservices/import.php b/webservices/import.php index 34a846297..59b9a2cc6 100644 --- a/webservices/import.php +++ b/webservices/import.php @@ -138,6 +138,9 @@ try case 'CellChangeSpec_Modify': $sClass = 'csvimport_ok'; break; + case 'CellChangeSpec_Init': + $sClass = 'csvimport_init'; + break; case 'CellChangeSpec_Issue': $sClass = 'csvimport_error'; break;