371 lines
15 KiB
PHP
371 lines
15 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Utility functions to manage a workspace.
|
|
*
|
|
* @author Alexandre Rosenfeld
|
|
*/
|
|
class workspaceTools {
|
|
var $workspaceName = NULL;
|
|
var $dbFile = NULL;
|
|
var $dbInfoRegExp = "/( *define *\( *'(?P<key>.*?)' *, *\n* *')(?P<value>.*?)(' *\) *;.*)/";
|
|
|
|
/**
|
|
* Create a workspace tools object
|
|
*
|
|
* @author Alexandre Rosenfeld <alexandre@colosa.com>
|
|
* @access public
|
|
* @param string $workspaceName name of the workspace
|
|
* @return void
|
|
*/
|
|
function __construct($workspaceName)
|
|
{
|
|
$this->workspaceName = $workspaceName;
|
|
$this->dbFile = PATH_DB . $this->workspaceName . '/db.php';
|
|
}
|
|
|
|
function listWorkspaces() {
|
|
$oDirectory = dir(PATH_DB);
|
|
$aWorkspaces = array ();
|
|
while( ($sObject = $oDirectory->read()) ) {
|
|
if( is_dir(PATH_DB . $sObject) && substr($sObject, 0, 1) != '.' && file_exists(PATH_DB . $sObject . PATH_SEP . 'db.php') ) {
|
|
$aWorkspaces[] = new workspaceTools($sObject);
|
|
}
|
|
}
|
|
return $aWorkspaces;
|
|
}
|
|
|
|
function getDBInfo() {
|
|
if( file_exists($this->dbFile) ) {
|
|
$sDbFile = file_get_contents($this->dbFile);
|
|
/* This regular expression will match any "define ('<key>', '<value>');"
|
|
* with any combination of whitespace between words.
|
|
* Each match will have these groups:
|
|
* ((define('(<key>)2', ')1 (<value>)3 (');)4 )0
|
|
*/
|
|
preg_match_all($this->dbInfoRegExp, $sDbFile, $matches, PREG_SET_ORDER);
|
|
$config = array();
|
|
foreach ($matches as $match) {
|
|
$config[$match['key']] = $match['value'];
|
|
}
|
|
return $config;
|
|
} else {
|
|
throw new Exception("Workspace db.php not found.");
|
|
}
|
|
}
|
|
|
|
function getSchema() {
|
|
$dbInfo = $this->getDBInfo();
|
|
$DB_ADAPTER = $dbInfo["DB_ADAPTER"];
|
|
$DB_HOST = $dbInfo["DB_HOST"];
|
|
$DB_USER = $dbInfo["DB_USER"];
|
|
$DB_PASS = $dbInfo["DB_PASS"];
|
|
$DB_NAME = $dbInfo["DB_NAME"];
|
|
|
|
try {
|
|
G::LoadSystem( 'database_' . strtolower($DB_ADAPTER));
|
|
|
|
$aOldSchema = array();
|
|
$oDataBase = new database($DB_ADAPTER, $DB_HOST, $DB_USER, $DB_PASS, $DB_NAME);
|
|
|
|
if ( !$oDataBase->isConnected() ) {
|
|
$oDataBase->logQuery ('Not exists an available connection!');
|
|
return NULL;
|
|
}
|
|
|
|
$oDataBase->iFetchType = MYSQL_NUM;
|
|
$oDataset1 = $oDataBase->executeQuery($oDataBase->generateShowTablesSQL());
|
|
|
|
} catch ( Exception $e ) {
|
|
$oDataBase->logQuery ( $e->getmessage() );
|
|
return NULL;
|
|
}
|
|
|
|
//going thru all tables in current WF_ database
|
|
while ($aRow1 = $oDataBase->getRegistry( $oDataset1) ) {
|
|
$aPrimaryKeys = array();
|
|
$sTable = strtoupper($aRow1[0]);
|
|
|
|
//get description of each table, ( column and primary keys )
|
|
//$oDataset2 = $oDataBase->executeQuery( $oDataBase->generateDescTableSQL($aRow1[0]) );
|
|
$oDataset2 = $oDataBase->executeQuery( $oDataBase->generateDescTableSQL($sTable ) );
|
|
$aOldSchema[ $sTable ] = array();
|
|
$oDataBase->iFetchType = MYSQL_ASSOC;
|
|
while ($aRow2 = $oDataBase->getRegistry($oDataset2)) {
|
|
$aOldSchema[$sTable][$aRow2['Field']]['Field'] = $aRow2['Field'];
|
|
$aOldSchema[$sTable][$aRow2['Field']]['Type'] = $aRow2['Type'];
|
|
$aOldSchema[$sTable][$aRow2['Field']]['Null'] = $aRow2['Null'];
|
|
$aOldSchema[$sTable][$aRow2['Field']]['Default'] = $aRow2['Default'];
|
|
}
|
|
|
|
//get indexes of each table SHOW INDEX FROM `ADDITIONAL_TABLES`; -- WHERE Key_name <> 'PRIMARY'
|
|
$oDataset2 = $oDataBase->executeQuery($oDataBase->generateTableIndexSQL($aRow1[0]));
|
|
$oDataBase->iFetchType = MYSQL_ASSOC;
|
|
while ($aRow2 = $oDataBase->getRegistry($oDataset2)) {
|
|
if ( !isset($aOldSchema[$sTable]['INDEXES']) ) {
|
|
$aOldSchema[$sTable]['INDEXES'] = array();
|
|
}
|
|
if (!isset($aOldSchema[$sTable]['INDEXES'][$aRow2['Key_name']] ) ) {
|
|
$aOldSchema[$sTable]['INDEXES'][$aRow2['Key_name']] = array();
|
|
}
|
|
$aOldSchema[$sTable]['INDEXES'][$aRow2['Key_name']][] = $aRow2['Column_name'];
|
|
}
|
|
|
|
$oDataBase->iFetchType = MYSQL_NUM; //this line is neccesary because the next fetch needs to be with MYSQL_NUM
|
|
}
|
|
//finally return the array with old schema obtained from the Database
|
|
if ( count($aOldSchema) == 0 ) $aOldSchema = null;
|
|
return $aOldSchema;
|
|
}
|
|
|
|
function getSchemaFromFile($sSchemaFile, $dbAdapter) {
|
|
$aSchema = array();
|
|
$oXml = new DomDocument();
|
|
$oXml->load($sSchemaFile);
|
|
$aTables = $oXml->getElementsByTagName('table');
|
|
foreach ($aTables as $oTable) {
|
|
$aPrimaryKeys = array();
|
|
$sTableName = $oTable->getAttribute('name');
|
|
$aSchema[$sTableName] = array();
|
|
$aColumns = $oTable->getElementsByTagName('column');
|
|
foreach ($aColumns as $oColumn) {
|
|
$sColumName = $oColumn->getAttribute('name');
|
|
$aSchema[$sTableName][$sColumName] = array();
|
|
$aVendors = $oColumn->getElementsByTagName('vendor');
|
|
foreach ($aVendors as $oVendor) {
|
|
if ($oVendor->getAttribute('type') == $dbAdapter) {
|
|
break;
|
|
}
|
|
}
|
|
$aParameters = $oColumn->getElementsByTagName('parameter');
|
|
foreach ($aParameters as $oParameter) {
|
|
$parameterName = ucwords($oParameter->getAttribute('name'));
|
|
if ( $parameterName == 'Key' && strtoupper($oParameter->getAttribute('value')) == 'PRI' ) {
|
|
$aPrimaryKeys[] = $oColumn->getAttribute('name');
|
|
}
|
|
|
|
if ( in_array ( $parameterName, array('Field','Type','Null','Default') ) ) {
|
|
$aSchema[$sTableName][$sColumName][$parameterName] = $oParameter->getAttribute('value');
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( is_array($aPrimaryKeys) && count($aPrimaryKeys) > 0 ) {
|
|
$aSchema[$sTableName]['INDEXES']['PRIMARY'] = $aPrimaryKeys;
|
|
}
|
|
$aIndexes = $oTable->getElementsByTagName('index');
|
|
foreach ($aIndexes as $oIndex) {
|
|
$aIndex = array();
|
|
$aIndexesColumns = $oIndex->getElementsByTagName('index-column');
|
|
foreach ($aIndexesColumns as $oIndexColumn) {
|
|
$aIndex[] = $oIndexColumn->getAttribute('name');
|
|
}
|
|
$aSchema[$sTableName]['INDEXES'][ $oIndex->getAttribute('name') ] = $aIndex;
|
|
}
|
|
}
|
|
return $aSchema;
|
|
}
|
|
|
|
function getSchemaChanges($aOldSchema, $aNewSchema) {
|
|
//$aChanges = array('tablesToDelete' => array(), 'tablesToAdd' => array(), 'tablesToAlter' => array());
|
|
//Tables to delete, but this is disabled
|
|
//foreach ($aOldSchema as $sTableName => $aColumns) {
|
|
// if ( !isset($aNewSchema[$sTableName])) {
|
|
// if (!in_array($sTableName, array('KT_APPLICATION', 'KT_DOCUMENT', 'KT_PROCESS'))) {
|
|
// $aChanges['tablesToDelete'][] = $sTableName;
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
$aChanges = array('tablesToAdd' => array(), 'tablesToAlter' => array(), 'tablesWithNewIndex' => array(), 'tablesToAlterIndex'=> array());
|
|
|
|
//new tables to create and alter
|
|
foreach ($aNewSchema as $sTableName => $aColumns) {
|
|
if (!isset($aOldSchema[$sTableName])) {
|
|
$aChanges['tablesToAdd'][$sTableName] = $aColumns;
|
|
}
|
|
else {
|
|
//drop old columns
|
|
foreach ($aOldSchema[$sTableName] as $sColumName => $aParameters) {
|
|
if (!isset($aNewSchema[$sTableName][$sColumName])) {
|
|
if (!isset($aChanges['tablesToAlter'][$sTableName])) {
|
|
$aChanges['tablesToAlter'][$sTableName] = array('DROP' => array(), 'ADD' => array(), 'CHANGE' => array());
|
|
}
|
|
$aChanges['tablesToAlter'][$sTableName]['DROP'][$sColumName] = $sColumName;
|
|
}
|
|
}
|
|
|
|
//create new columns
|
|
//foreach ($aNewSchema[$sTableName] as $sColumName => $aParameters) {
|
|
foreach ($aColumns as $sColumName => $aParameters) {
|
|
if ($sColumName != 'INDEXES') {
|
|
if (!isset($aOldSchema[$sTableName][$sColumName])) { //this column doesnt exist in oldschema
|
|
if (!isset($aChanges['tablesToAlter'][$sTableName])) {
|
|
$aChanges['tablesToAlter'][$sTableName] = array('DROP' => array(), 'ADD' => array(), 'CHANGE' => array());
|
|
}
|
|
$aChanges['tablesToAlter'][$sTableName]['ADD'][$sColumName] = $aParameters;
|
|
}
|
|
else { //the column exists
|
|
$newField = $aNewSchema[$sTableName][$sColumName];
|
|
$oldField = $aOldSchema[$sTableName][$sColumName];
|
|
//both are null, no change is required
|
|
if ( !isset($newField['Default']) && !isset($oldField['Default'])) $changeDefaultAttr = false;
|
|
//one of them is null, change IS required
|
|
if ( !isset($newField['Default']) && isset($oldField['Default']) && $oldField['Default']!= '') $changeDefaultAttr = true;
|
|
if ( isset($newField['Default']) && !isset($oldField['Default'])) $changeDefaultAttr = true;
|
|
//both are defined and they are different.
|
|
if ( isset($newField['Default']) && isset($oldField['Default']) ) {
|
|
if ( $newField['Default'] != $oldField['Default'] )
|
|
$changeDefaultAttr = true;
|
|
else
|
|
$changeDefaultAttr = false;
|
|
}
|
|
//special cases
|
|
// BLOB and TEXT columns cannot have DEFAULT values. http://dev.mysql.com/doc/refman/5.0/en/blob.html
|
|
if ( in_array(strtolower($newField['Type']), array('text','mediumtext') ) )
|
|
$changeDefaultAttr = false;
|
|
|
|
//#1067 - Invalid default value for datetime field
|
|
if ( in_array($newField['Type'], array('datetime')) && isset($newField['Default']) && $newField['Default']== '' )
|
|
$changeDefaultAttr = false;
|
|
|
|
//#1067 - Invalid default value for int field
|
|
if ( substr($newField['Type'], 0, 3 ) && isset($newField['Default']) && $newField['Default']== '' )
|
|
$changeDefaultAttr = false;
|
|
|
|
//if any difference exists, then insert the difference in aChanges
|
|
if ( $newField['Field'] != $oldField['Field'] ||
|
|
$newField['Type'] != $oldField['Type'] ||
|
|
$newField['Null'] != $oldField['Null'] ||
|
|
$changeDefaultAttr ) {
|
|
if (!isset($aChanges['tablesToAlter'][$sTableName])) {
|
|
$aChanges['tablesToAlter'][$sTableName] = array('DROP' => array(), 'ADD' => array(), 'CHANGE' => array());
|
|
}
|
|
$aChanges['tablesToAlter'][$sTableName]['CHANGE'][$sColumName]['Field'] = $newField['Field'];
|
|
$aChanges['tablesToAlter'][$sTableName]['CHANGE'][$sColumName]['Type'] = $newField['Type'];
|
|
$aChanges['tablesToAlter'][$sTableName]['CHANGE'][$sColumName]['Null'] = $newField['Null'];
|
|
if ( isset($newField['Default']) )
|
|
$aChanges['tablesToAlter'][$sTableName]['CHANGE'][$sColumName]['Default'] = $newField['Default'];
|
|
else
|
|
$aChanges['tablesToAlter'][$sTableName]['CHANGE'][$sColumName]['Default'] = null;
|
|
|
|
}
|
|
}
|
|
} //only columns, no the indexes column
|
|
}//foreach $aColumns
|
|
|
|
//now check the indexes of table
|
|
if ( isset($aNewSchema[$sTableName]['INDEXES']) ) {
|
|
foreach ( $aNewSchema[$sTableName]['INDEXES'] as $indexName => $indexFields ) {
|
|
if (!isset( $aOldSchema[$sTableName]['INDEXES'][$indexName]) ) {
|
|
if (!isset($aChanges['tablesWithNewIndex'][$sTableName])) {
|
|
$aChanges['tablesWithNewIndex'][$sTableName] = array();
|
|
}
|
|
$aChanges['tablesWithNewIndex'][$sTableName][$indexName] = $indexFields;
|
|
}
|
|
else {
|
|
if ( $aOldSchema[$sTableName]['INDEXES'][$indexName] != $indexFields ) {
|
|
if (!isset($aChanges['tablesToAlterIndex'][$sTableName])) {
|
|
$aChanges['tablesToAlterIndex'][$sTableName] = array();
|
|
}
|
|
$aChanges['tablesToAlterIndex'][$sTableName][$indexName] = $indexFields;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} //for-else table exists
|
|
} //for new schema
|
|
return $aChanges;
|
|
}
|
|
|
|
function repairSchema($checkOnly = false, $progress = true) {
|
|
$dbInfo = $this->getDBInfo();
|
|
$DB_ADAPTER = $dbInfo["DB_ADAPTER"];
|
|
$DB_HOST = $dbInfo["DB_HOST"];
|
|
$DB_USER = $dbInfo["DB_USER"];
|
|
$DB_PASS = $dbInfo["DB_PASS"];
|
|
$DB_NAME = $dbInfo["DB_NAME"];
|
|
|
|
if (strcmp($DB_ADAPTER, "mysql") != 0) {
|
|
throw new Exception("Only MySQL is supported\n");
|
|
}
|
|
|
|
$currentSchema = $this->getSchemaFromFile(PATH_TRUNK . "workflow/engine/config/schema.xml", "mysql");
|
|
$workspaceSchema = $this->getSchema();
|
|
$changes = $this->getSchemaChanges($workspaceSchema, $currentSchema);
|
|
$changed = (count($changes['tablesToAdd']) > 0 ||
|
|
count($changes['tablesToAlter']) > 0 ||
|
|
count($changes['tablesWithNewIndex']) > 0 ||
|
|
count($changes['tablesToAlterIndex']) > 0);
|
|
if ($checkOnly || (!$changed)) {
|
|
if ($changed)
|
|
return $changes;
|
|
else
|
|
return $changed;
|
|
}
|
|
|
|
$oDataBase = new database($DB_ADAPTER, $DB_HOST, $DB_USER, $DB_PASS, $DB_NAME);
|
|
if ( !$oDataBase->isConnected() ) {
|
|
throw new Exception("Could not connect to the database");
|
|
}
|
|
$oDataBase->iFetchType = MYSQL_NUM;
|
|
|
|
$oDataBase->logQuery ( count ($changes ) );
|
|
|
|
if ($progress)
|
|
echo "Adding " . count($changes['tablesToAdd']) . " tables\n";
|
|
foreach ($changes['tablesToAdd'] as $sTable => $aColumns) {
|
|
$oDataBase->executeQuery($oDataBase->generateCreateTableSQL($sTable, $aColumns));
|
|
if (isset($changes['tablesToAdd'][$sTable]['INDEXES'])) {
|
|
foreach ($changes['tablesToAdd'][$sTable]['INDEXES'] as $indexName => $aIndex) {
|
|
$oDataBase->executeQuery($oDataBase->generateAddKeysSQL($sTable, $indexName, $aIndex ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if ($progress)
|
|
echo "Altering " . count($changes['tablesToAlter']) . " tables\n";
|
|
foreach ($changes['tablesToAlter'] as $sTable => $aActions) {
|
|
foreach ($aActions as $sAction => $aAction) {
|
|
foreach ($aAction as $sColumn => $vData) {
|
|
switch ($sAction) {
|
|
case 'DROP':
|
|
$oDataBase->executeQuery($oDataBase->generateDropColumnSQL($sTable, $vData));
|
|
break;
|
|
case 'ADD':
|
|
$oDataBase->executeQuery($oDataBase->generateAddColumnSQL($sTable, $sColumn, $vData));
|
|
break;
|
|
case 'CHANGE':
|
|
$oDataBase->executeQuery($oDataBase->generateChangeColumnSQL($sTable, $sColumn, $vData));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if ($progress)
|
|
echo "Adding indexes to " . count($changes['tablesWithNewIndex']) . " tables\n";
|
|
foreach ($changes['tablesWithNewIndex'] as $sTable => $aIndexes) {
|
|
foreach ($aIndexes as $sIndexName => $aIndexFields ) {
|
|
$oDataBase->executeQuery($oDataBase->generateAddKeysSQL($sTable, $sIndexName, $aIndexFields ));
|
|
}
|
|
}
|
|
|
|
|
|
if ($progress)
|
|
echo "Altering indexes to " . count($changes['tablesWithNewIndex']) . " tables\n";
|
|
foreach ($changes['tablesToAlterIndex'] as $sTable => $aIndexes) {
|
|
foreach ($aIndexes as $sIndexName => $aIndexFields ) {
|
|
$oDataBase->executeQuery($oDataBase->generateDropKeySQL($sTable, $sIndexName ));
|
|
$oDataBase->executeQuery($oDataBase->generateAddKeysSQL($sTable, $sIndexName, $aIndexFields ));
|
|
}
|
|
}
|
|
$oDataBase->close();
|
|
return true;
|
|
}
|
|
|
|
}
|
|
?>
|