Optimizations:

- Setup: grouped CREATE TABLE / ALTER TABLE statements in one single CREATE table
- Every page: cache the prepared queries and data model into APC
Cleanup: removed unused verb RegisterPlugin
Fix: GetObject could fail (could not find the reference from within the forum)

SVN:trunk[1154]
This commit is contained in:
Romain Quetiez
2011-03-30 08:30:42 +00:00
parent bdf46e8136
commit b423e98295
5 changed files with 260 additions and 104 deletions

View File

@@ -509,7 +509,6 @@ class UserRightsProfile extends UserRightsAddOnAPI
public function Init()
{
MetaModel::RegisterPlugin('userrights', 'ACbyProfile');
}

View File

@@ -660,7 +660,8 @@ class UserRightsProjection extends UserRightsAddOnAPI
public function Init()
{
MetaModel::RegisterPlugin('userrights', 'ACbyProfile', array($this, 'CacheData'));
// CacheData to be invoked in a module extension
//MetaModel::RegisterPlugin('userrights', 'ACbyProfile', array($this, 'CacheData'));
}
protected $m_aDimensions = array(); // id -> object

View File

@@ -232,21 +232,21 @@ class Dict
MyHelpers::var_dump_html(self::$m_aData);
}
public static function InCache()
public static function InCache($sApplicationPrefix)
{
if (function_exists('apc_fetch'))
{
$bResult = false;
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
//
self::$m_aData = apc_fetch('dict');
self::$m_aData = apc_fetch($sApplicationPrefix.'-dict');
if (is_bool(self::$m_aData) && (self::$m_aData === false))
{
self::$m_aData = array();
}
else
{
self::$m_aLanguages = apc_fetch('languages');
self::$m_aLanguages = apc_fetch($sApplicationPrefix.'-languages');
if (is_bool(self::$m_aLanguages) && (self::$m_aLanguages === false))
{
self::$m_aLanguages = array();
@@ -261,12 +261,21 @@ class Dict
return false;
}
public static function InitCache()
public static function InitCache($sApplicationPrefix)
{
if (function_exists('apc_store'))
{
apc_store('languages', self::$m_aLanguages);
apc_store('dict', self::$m_aData);
apc_store($sApplicationPrefix.'-languages', self::$m_aLanguages);
apc_store($sApplicationPrefix.'-dict', self::$m_aData);
}
}
public static function ResetCache($sApplicationPrefix)
{
if (function_exists('apc_delete'))
{
apc_delete($sApplicationPrefix.'-languages');
apc_delete($sApplicationPrefix.'-dict');
}
}
}

View File

@@ -538,7 +538,7 @@ abstract class MetaModel
final static protected function DBEnumTables()
{
// This API do not rely on our capability to query the DB and retrieve
// This API does not rely on our capability to query the DB and retrieve
// the list of existing tables
// Rather, it uses the list of expected tables, corresponding to the data model
$aTables = array();
@@ -1277,6 +1277,31 @@ abstract class MetaModel
// }
//}
}
// Build the list of available extensions
//
$aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension');
foreach($aInterfaces as $sInterface)
{
self::$m_aExtensionClasses[$sInterface] = array();
}
foreach(get_declared_classes() as $sPHPClass)
{
$oRefClass = new ReflectionClass($sPHPClass);
$oExtensionInstance = null;
foreach($aInterfaces as $sInterface)
{
if ($oRefClass->implementsInterface($sInterface))
{
if (is_null($oExtensionInstance))
{
$oExtensionInstance = new $sPHPClass;
}
self::$m_aExtensionClasses[$sInterface][] = $oExtensionInstance;
}
}
}
}
// To be overriden, must be called for any object class (optimization)
@@ -1782,6 +1807,22 @@ abstract class MetaModel
// hit!
$oSelect = clone self::$m_aQueryStructCache[$sOqlId];
}
elseif (function_exists('apc_fetch'))
{
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
//
$sAppIdentity = self::GetConfig()->Get('session_name');
$sOqlAPCCacheId = $sAppIdentity.'-query-cache-'.$sOqlId;
$oKPI = new ExecutionKPI();
$result = apc_fetch($sOqlAPCCacheId);
$oKPI->ComputeStats('Query APC (fetch)', $sOqlQuery);
if (is_object($result))
{
$oSelect = $result;
self::$m_aQueryStructCache[$sOqlId] = $oSelect;
}
}
}
if (!isset($oSelect))
@@ -1792,9 +1833,20 @@ abstract class MetaModel
$oKPI = new ExecutionKPI();
$oSelect = self::MakeQuery($oFilter->GetSelectedClasses(), $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, array(), true /* main query */);
$oSelect->SetSourceOQL($sOqlQuery);
$oKPI->ComputeStats('MakeQuery (select)', $sOqlQuery);
self::$m_aQueryStructCache[$sOqlId] = clone $oSelect;
if (self::$m_bQueryCacheEnabled)
{
if (function_exists('apc_store'))
{
$oKPI = new ExecutionKPI();
apc_store($sOqlAPCCacheId, $oSelect);
$oKPI->ComputeStats('Query APC (store)', $sOqlQuery);
}
self::$m_aQueryStructCache[$sOqlId] = clone $oSelect;
}
}
// Check the order by specification, and prefix with the class alias
@@ -2697,26 +2749,13 @@ abstract class MetaModel
protected static function DBCreateTables()
{
list($aErrors, $aSugFix) = self::DBCheckFormat();
list($aErrors, $aSugFix, $aCondensedQueries) = self::DBCheckFormat();
$aSQL = array();
foreach ($aSugFix as $sClass => $aTarget)
//$sSQL = implode('; ', $aCondensedQueries); Does not work - multiple queries not allowed
foreach($aCondensedQueries as $sQuery)
{
foreach ($aTarget as $aQueries)
{
foreach ($aQueries as $sQuery)
{
if (!empty($sQuery))
{
//$aSQL[] = $sQuery;
// forces a refresh of cached information
CMDBSource::CreateTable($sQuery);
}
}
}
CMDBSource::CreateTable($sQuery);
}
// does not work -how to have multiple statements in a single query?
// $sDoCreateAll = implode(" ; ", $aSQL);
}
protected static function DBCreateViews()
@@ -2906,6 +2945,12 @@ abstract class MetaModel
{
$aErrors = array();
$aSugFix = array();
// A new way of representing things to be done - quicker to execute !
$aCreateTable = array(); // array of <table> => <table options>
$aCreateTableItems = array(); // array of <table> => array of <create definition>
$aAlterTableItems = array(); // array of <table> => <alter specification>
foreach (self::GetClasses() as $sClass)
{
if (!self::HasTable($sClass)) continue;
@@ -2915,17 +2960,24 @@ abstract class MetaModel
$sTable = self::DBGetTable($sClass);
$sKeyField = self::DBGetKey($sClass);
$sAutoIncrement = (self::IsAutoIncrementKey($sClass) ? "AUTO_INCREMENT" : "");
$sKeyFieldDefinition = "`$sKeyField` INT(11) NOT NULL $sAutoIncrement PRIMARY KEY";
if (!CMDBSource::IsTable($sTable))
{
$aErrors[$sClass]['*'][] = "table '$sTable' could not be found into the DB";
$aSugFix[$sClass]['*'][] = "CREATE TABLE `$sTable` (`$sKeyField` INT(11) NOT NULL $sAutoIncrement PRIMARY KEY) ENGINE = ".MYSQL_ENGINE." CHARACTER SET utf8 COLLATE utf8_unicode_ci";
$aSugFix[$sClass]['*'][] = "CREATE TABLE `$sTable` ($sKeyFieldDefinition) ENGINE = ".MYSQL_ENGINE." CHARACTER SET utf8 COLLATE utf8_unicode_ci";
$aCreateTable[$sTable] = "ENGINE = ".MYSQL_ENGINE." CHARACTER SET utf8 COLLATE utf8_unicode_ci";
$aCreateTableItems[$sTable][$sKeyField] = $sKeyFieldDefinition;
}
// Check that the key field exists
//
elseif (!CMDBSource::IsField($sTable, $sKeyField))
{
$aErrors[$sClass]['id'][] = "key '$sKeyField' (table $sTable) could not be found";
$aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable` ADD `$sKeyField` INT(11) NOT NULL $sAutoIncrement PRIMARY KEY";
$aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable` ADD $sKeyFieldDefinition";
if (!array_key_exists($sTable, $aCreateTable))
{
$aAlterTableItems[$sTable][$sKeyField] = "ADD $sKeyFieldDefinition";
}
}
else
{
@@ -2935,11 +2987,19 @@ abstract class MetaModel
{
$aErrors[$sClass]['id'][] = "key '$sKeyField' is not a key for table '$sTable'";
$aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable`, DROP PRIMARY KEY, ADD PRIMARY key(`$sKeyField`)";
if (!array_key_exists($sTable, $aCreateTable))
{
$aAlterTableItems[$sTable][$sKeyField] = "CHANGE `$sKeyField` $sKeyFieldDefinition";
}
}
if (self::IsAutoIncrementKey($sClass) && !CMDBSource::IsAutoIncrement($sTable, $sKeyField))
{
$aErrors[$sClass]['id'][] = "key '$sKeyField' (table $sTable) is not automatically incremented";
$aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable` CHANGE `$sKeyField` `$sKeyField` INT(11) NOT NULL AUTO_INCREMENT";
$aSugFix[$sClass]['id'][] = "ALTER TABLE `$sTable` CHANGE `$sKeyField` $sKeyFieldDefinition";
if (!array_key_exists($sTable, $aCreateTable))
{
$aAlterTableItems[$sTable][$sKeyField] = "CHANGE `$sKeyField` $sKeyFieldDefinition";
}
}
}
@@ -2955,15 +3015,31 @@ abstract class MetaModel
foreach($oAttDef->GetSQLColumns() as $sField => $sDBFieldType)
{
$bIndexNeeded = $oAttDef->RequiresIndex();
$sFieldSpecs = $oAttDef->IsNullAllowed() ? "$sDBFieldType NULL" : "$sDBFieldType NOT NULL";
$sFieldDefinition = "`$sField` ".($oAttDef->IsNullAllowed() ? "$sDBFieldType NULL" : "$sDBFieldType NOT NULL");
if (!CMDBSource::IsField($sTable, $sField))
{
$aErrors[$sClass][$sAttCode][] = "field '$sField' could not be found in table '$sTable'";
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` ADD `$sField` $sFieldSpecs";
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` ADD $sFieldDefinition";
if ($bIndexNeeded)
{
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` ADD INDEX (`$sField`)";
}
if (array_key_exists($sTable, $aCreateTable))
{
$aCreateTableItems[$sTable][$sField] = $sFieldDefinition;
if ($bIndexNeeded)
{
$aCreateTableItems[$sTable][$sField.'_ix'] = "INDEX (`$sField`)";
}
}
else
{
$aAlterTableItems[$sTable][$sField] = "ADD $sFieldDefinition";
if ($bIndexNeeded)
{
$aAlterTableItems[$sTable][$sField.'_ix'] = "ADD INDEX (`$sField`)";
}
}
}
else
{
@@ -2990,7 +3066,8 @@ abstract class MetaModel
}
if ($bToBeChanged)
{
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` CHANGE `$sField` `$sField` $sFieldSpecs";
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` CHANGE `$sField` $sFieldDefinition";
$aAlterTableItems[$sTable][$sField] = "CHANGE `$sField` $sFieldDefinition";
}
// Create indexes (external keys only... so far)
@@ -2999,12 +3076,26 @@ abstract class MetaModel
{
$aErrors[$sClass][$sAttCode][] = "Foreign key '$sField' in table '$sTable' should have an index";
$aSugFix[$sClass][$sAttCode][] = "ALTER TABLE `$sTable` ADD INDEX (`$sField`)";
$aAlterTableItems[$sTable][$sField.'_ix'] = "ADD INDEX (`$sField`)";
}
}
}
}
}
return array($aErrors, $aSugFix);
$aCondensedQueries = array();
foreach($aCreateTable as $sTable => $sTableOptions)
{
$sTableItems = implode(', ', $aCreateTableItems[$sTable]);
$aCondensedQueries[] = "CREATE TABLE `$sTable` ($sTableItems) $sTableOptions";
}
foreach($aAlterTableItems as $sTable => $aChangeList)
{
$sChangeList = implode(', ', $aChangeList);
$aCondensedQueries[] = "ALTER TABLE `$sTable` $sChangeList";
}
return array($aErrors, $aSugFix, $aCondensedQueries);
}
public static function DBCheckViews()
@@ -3462,37 +3553,10 @@ abstract class MetaModel
{
self::LoadConfig($sConfigFile);
$aInterfaces = array('iApplicationUIExtension', 'iApplicationObjectExtension');
foreach($aInterfaces as $sInterface)
{
self::$m_aExtensionClasses[$sInterface] = array();
}
foreach(get_declared_classes() as $sPHPClass)
{
$oRefClass = new ReflectionClass($sPHPClass);
$oExtensionInstance = null;
foreach($aInterfaces as $sInterface)
{
if ($oRefClass->implementsInterface($sInterface))
{
if (is_null($oExtensionInstance))
{
$oExtensionInstance = new $sPHPClass;
}
self::$m_aExtensionClasses[$sInterface][] = $oExtensionInstance;
}
}
}
if ($bModelOnly) return;
CMDBSource::SelectDB(self::$m_sDBName);
// Some of the init could not be done earlier (requiring classes to be declared and DB to be accessible)
// To be deprecated
self::InitPlugins();
foreach(get_declared_classes() as $sPHPClass)
{
if (is_subclass_of($sPHPClass, 'ModuleHandlerAPI'))
@@ -3550,13 +3614,14 @@ abstract class MetaModel
// Note: load the dictionary as soon as possible, because it might be
// needed when some error occur
if (!Dict::InCache())
$sAppIdentity = self::GetConfig()->Get('session_name');
if (!Dict::InCache($sAppIdentity))
{
foreach (self::$m_oConfig->GetDictionaries() as $sModule => $sToInclude)
{
self::Plugin($sConfigFile, 'dictionaries', $sToInclude);
self::IncludeModule($sConfigFile, 'dictionaries', $sToInclude);
}
Dict::InitCache();
Dict::InitCache($sAppIdentity);
}
// Set the language... after the dictionaries have been loaded!
Dict::SetDefaultLanguage(self::$m_oConfig->GetDefaultLanguage());
@@ -3567,19 +3632,19 @@ abstract class MetaModel
foreach (self::$m_oConfig->GetAppModules() as $sModule => $sToInclude)
{
self::Plugin($sConfigFile, 'application', $sToInclude);
self::IncludeModule($sConfigFile, 'application', $sToInclude);
}
foreach (self::$m_oConfig->GetDataModels() as $sModule => $sToInclude)
{
self::Plugin($sConfigFile, 'business', $sToInclude);
self::IncludeModule($sConfigFile, 'business', $sToInclude);
}
foreach (self::$m_oConfig->GetWebServiceCategories() as $sModule => $sToInclude)
{
self::Plugin($sConfigFile, 'webservice', $sToInclude);
self::IncludeModule($sConfigFile, 'webservice', $sToInclude);
}
foreach (self::$m_oConfig->GetAddons() as $sModule => $sToInclude)
{
self::Plugin($sConfigFile, 'addons', $sToInclude);
self::IncludeModule($sConfigFile, 'addons', $sToInclude);
}
$sServer = self::$m_oConfig->GetDBHost();
@@ -3590,13 +3655,76 @@ abstract class MetaModel
$sCharacterSet = self::$m_oConfig->GetDBCharacterSet();
$sCollation = self::$m_oConfig->GetDBCollation();
$oKPI = new ExecutionKPI();
if (function_exists('apc_fetch'))
{
$oKPI = new ExecutionKPI();
// Note: For versions of APC older than 3.0.17, fetch() accepts only one parameter
//
$sAppIdentity = self::GetConfig()->Get('session_name');
$sOqlAPCCacheId = $sAppIdentity.'-metamodel';
$result = apc_fetch($sOqlAPCCacheId);
// The include have been included, let's browse the existing classes and
// develop some data based on the proposed model
self::InitClasses($sTablePrefix);
if (is_array($result))
{
// todo - verifier que toutes les classes mentionnees ici sont chargees dans InitClasses()
self::$m_aExtensionClasses = $result['m_aExtensionClasses'];
self::$m_Category2Class = $result['m_Category2Class'];
self::$m_aRootClasses = $result['m_aRootClasses'];
self::$m_aParentClasses = $result['m_aParentClasses'];
self::$m_aChildClasses = $result['m_aChildClasses'];
self::$m_aClassParams = $result['m_aClassParams'];
self::$m_aAttribDefs = $result['m_aAttribDefs'];
self::$m_aAttribOrigins = $result['m_aAttribOrigins'];
self::$m_aExtKeyFriends = $result['m_aExtKeyFriends'];
self::$m_aIgnoredAttributes = $result['m_aIgnoredAttributes'];
self::$m_aFilterDefs = $result['m_aFilterDefs'];
self::$m_aFilterOrigins = $result['m_aFilterOrigins'];
self::$m_aListInfos = $result['m_aListInfos'];
self::$m_aListData = $result['m_aListData'];
self::$m_aRelationInfos = $result['m_aRelationInfos'];
self::$m_aStates = $result['m_aStates'];
self::$m_aStimuli = $result['m_aStimuli'];
self::$m_aTransitions = $result['m_aTransitions'];
}
$oKPI->ComputeAndReport('Metamodel APC (fetch + read)');
}
$oKPI->ComputeAndReport('Initialization of Data model structures');
if (count(self::$m_aAttribDefs) == 0)
{
// The includes have been included, let's browse the existing classes and
// develop some data based on the proposed model
$oKPI = new ExecutionKPI();
self::InitClasses($sTablePrefix);
$oKPI->ComputeAndReport('Initialization of Data model structures');
if (function_exists('apc_store'))
{
$oKPI = new ExecutionKPI();
$aCache = array();
$aCache['m_aExtensionClasses'] = self::$m_aExtensionClasses;
$aCache['m_Category2Class'] = self::$m_Category2Class;
$aCache['m_aRootClasses'] = self::$m_aRootClasses; // array of "classname" => "rootclass"
$aCache['m_aParentClasses'] = self::$m_aParentClasses; // array of ("classname" => array of "parentclass")
$aCache['m_aChildClasses'] = self::$m_aChildClasses; // array of ("classname" => array of "childclass")
$aCache['m_aClassParams'] = self::$m_aClassParams; // array of ("classname" => array of class information)
$aCache['m_aAttribDefs'] = self::$m_aAttribDefs; // array of ("classname" => array of attributes)
$aCache['m_aAttribOrigins'] = self::$m_aAttribOrigins; // array of ("classname" => array of ("attcode"=>"sourceclass"))
$aCache['m_aExtKeyFriends'] = self::$m_aExtKeyFriends; // array of ("classname" => array of ("indirect ext key attcode"=> array of ("relative ext field")))
$aCache['m_aIgnoredAttributes'] = self::$m_aIgnoredAttributes; //array of ("classname" => array of ("attcode")
$aCache['m_aFilterDefs'] = self::$m_aFilterDefs; // array of ("classname" => array filterdef)
$aCache['m_aFilterOrigins'] = self::$m_aFilterOrigins; // array of ("classname" => array of ("attcode"=>"sourceclass"))
$aCache['m_aListInfos'] = self::$m_aListInfos; // array of ("listcode" => various info on the list, common to every classes)
$aCache['m_aListData'] = self::$m_aListData; // array of ("classname" => array of "listcode" => list)
$aCache['m_aRelationInfos'] = self::$m_aRelationInfos; // array of ("relcode" => various info on the list, common to every classes)
$aCache['m_aStates'] = self::$m_aStates; // array of ("classname" => array of "statecode"=>array('label'=>..., attribute_inherit=> attribute_list=>...))
$aCache['m_aStimuli'] = self::$m_aStimuli; // array of ("classname" => array of ("stimuluscode"=>array('label'=>...)))
$aCache['m_aTransitions'] = self::$m_aTransitions; // array of ("classname" => array of ("statcode_from"=>array of ("stimuluscode" => array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD)))
apc_store($sOqlAPCCacheId, $aCache);
$oKPI->ComputeAndReport('Metamodel APC (store)');
}
}
self::$m_sDBName = $sSource;
self::$m_sTablePrefix = $sTablePrefix;
@@ -3617,16 +3745,7 @@ abstract class MetaModel
protected static $m_aExtensionClasses = array();
protected static $m_aPlugins = array();
public static function RegisterPlugin($sType, $sName, $aInitCallSpec = array())
{
self::$m_aPlugins[$sName] = array(
'type' => $sType,
'init' => $aInitCallSpec,
);
}
protected static function Plugin($sConfigFile, $sModuleType, $sToInclude)
protected static function IncludeModule($sConfigFile, $sModuleType, $sToInclude)
{
$sFirstChar = substr($sToInclude, 0, 1);
$sSecondChar = substr($sToInclude, 1, 1);
@@ -3656,24 +3775,6 @@ abstract class MetaModel
require_once($sFile);
}
// #@# to be deprecated!
//
protected static function InitPlugins()
{
foreach(self::$m_aPlugins as $sName => $aData)
{
$aCallSpec = @$aData['init'];
if (count($aCallSpec) == 2)
{
if (!is_callable($aCallSpec))
{
throw new CoreException('Wrong declaration in plugin', array('plugin' => $aData['name'], 'type' => $aData['type'], 'class' => $aData['class'], 'init' => $aData['init']));
}
call_user_func($aCallSpec);
}
}
}
// Building an object
//
//
@@ -3698,8 +3799,8 @@ abstract class MetaModel
// 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!
// This could be simplified a little, relying solely on the query cache,
// but this would slow down -by how much time?- the application
$oFilter = new DBObjectSearch($sClass);
$oFilter->AddCondition('id', 987654321, '=');
if ($bAllowAllData)
@@ -3717,7 +3818,7 @@ abstract class MetaModel
self::$aQueryCacheGetObjectHits[$sClass] += 1;
// echo " -load $sClass/$iKey- ".self::$aQueryCacheGetObjectHits[$sClass]."<br/>\n";
}
$sSQL = str_replace('987654321', CMDBSource::Quote($iKey), $sSQL);
$sSQL = str_replace(CMDBSource::Quote(987654321), CMDBSource::Quote($iKey), $sSQL);
$res = CMDBSource::Query($sSQL);
$aRow = CMDBSource::FetchArray($res);
@@ -4021,6 +4122,41 @@ abstract class MetaModel
return array();
}
}
public static function GetCacheEntries()
{
if (!function_exists('apc_cache_info')) return array();
$aCacheUserData = apc_cache_info('user');
$sAppIdentity = MetaModel::GetConfig()->Get('session_name');
$sPrefix = $sAppIdentity.'-';
$aEntries = array();
foreach($aCacheUserData['cache_list'] as $i => $aEntry)
{
$sEntryKey = $aEntry['info'];
if (strpos($sEntryKey, $sPrefix) === 0)
{
$sCleanKey = substr($sEntryKey, strlen($sPrefix));
$aEntries[$sCleanKey] = $aEntry;
}
}
return $aEntries;
}
public static function ResetCache()
{
if (!function_exists('apc_delete')) return;
$sAppIdentity = MetaModel::GetConfig()->Get('session_name');
Dict::ResetCache($sAppIdentity);
foreach(self::GetCacheEntries() as $sKey => $aAPCInfo)
{
$sAPCKey = $aAPCInfo['info'];
apc_delete($sAPCKey);
}
}
} // class MetaModel

View File

@@ -37,6 +37,7 @@ require_once('cmdbsource.class.inc.php');
class SQLQuery
{
private $m_SourceOQL = '';
private $m_sTable = '';
private $m_sTableAlias = '';
private $m_aFields = array();
@@ -69,6 +70,16 @@ class SQLQuery
$this->m_oSelectedIdField = $oSelectedIdField;
}
public function SetSourceOQL($sOQL)
{
$this->m_SourceOQL = $sOQL;
}
public function GetSourceOQL()
{
return $this->m_SourceOQL;
}
public function DisplayHtml()
{
if (count($this->m_aFields) == 0) $sFields = "";