373 lines
12 KiB
PHP
Raw Permalink Normal View History

2013-01-30 21:51:28 -05:00
<?php
/*+*******************************************************************************
* The contents of this file are subject to the vtiger CRM Public License Version 1.0
* ("License"); You may not use this file except in compliance with the License
* The Original Code is: vtiger CRM Open Source
* The Initial Developer of the Original Code is vtiger.
* Portions created by vtiger are Copyright (C) vtiger.
* All Rights Reserved.
*
*********************************************************************************/
require_once 'include/db_backup/Source/BackupSource.php';
require_once 'adodb/adodb-lib.inc.php';
/**
* Description of MysqlSource
*
* @author MAK
*/
class MysqlSource extends BackupSource {
private $currentStage = -1;
private $connection = null;
private $dbConfig = null;
private $tableNameList = null;
private $result = null;
private $start = 0;
private $currentTable;
private $valueListSize = 60000;
public function __construct($dbConfig) {
$this->dbConfig = $dbConfig;
$this->currentStage = -1;
$this->tableNameList = null;
$this->result = null;
$this->start = 0;
$this->setup();
}
private function setup() {
$this->connection = &NewADOConnection($this->dbConfig->getDBType());
$ok = $this->connection->NConnect($this->dbConfig->getHostName(),$this->dbConfig->getUserName(),
$this->dbConfig->getPassword(),$this->dbConfig->getDatabaseName());
if(!$ok){
throw new DatabaseBackupException(DatabaseBackupErrorCode::$DB_CONNECT_ERROR,
DatabaseBackup::$langString['SourceConnectFailed']);
}
$this->connection->_Execute("SET NAMES 'utf8'",false);
$result = $this->connection->_Execute("SET interactive_timeout=28800",false);
$result = $this->connection->_Execute("SET wait_timeout=28800",false);
$result = $this->connection->_Execute("SET net_write_timeout=900",false);
$result = $this->connection->_Execute("SET net_read_timeout=900",false);
}
public function next() {
$this->currentStage = $this->getNextStage($this->currentStage);
$data = $this->getStageData($this->currentStage);
return array('stage'=>$this->currentStage,'data'=>$data);
}
public function valid() {
$nextStage = $this->getNextStage($this->currentStage);
return ($nextStage !== false);
}
public function getNextStage($stage) {
switch($stage) {
case -1: return $this->startBackupStage;
case $this->startBackupStage: return $this->tableCreateStage;
case $this->tableCreateStage: return $this->startTableBackupStage;
case $this->startTableBackupStage: return $this->processStatementStage;
break;
case $this->processStatementStage: $rowCount = $this->result->RecordCount();
if($this->start < $rowCount) {
return $this->processStatementStage;
}else{
return $this->finishTableBackupStage;
}
case $this->finishTableBackupStage: $index = array_search($this->currentTable,
$this->tableNameList);
if($index+1 == count($this->tableNameList)) {
return $this->finishBackupStage;
}else{
return $this->tableCreateStage;
}
case $this->finishBackupStage: return false;
}
}
function getStageData($stage) {
switch($stage) {
case $this->startBackupStage: $this->initTableList();
return array(null);
case $this->tableCreateStage: if(empty($this->currentTable)) {
$index = -1;
} else {
$index = array_search($this->currentTable,
$this->tableNameList);
}
$this->setCurrentTable($this->tableNameList[$index+1]);
$stmt = $this->getTableCreateStatement($this->currentTable);
return array($this->currentTable,$stmt);
case $this->startTableBackupStage: return array($this->currentTable);
break;
case $this->processStatementStage: return array(
$this->getInsertStatement($this->currentTable)
);
break;
case $this->finishTableBackupStage: return array($this->currentTable);
break;
case $this->finishBackupStage: return array(null);
}
}
function initTableList() {
$this->tableNameList = $this->connection->MetaTables('TABLES');
if($this->tableNameList === false){
throw new DatabaseBackupException(DatabaseBackupErrorCode::$TABLE_NAME_ERROR,
DatabaseBackup::$langString['TableListFetchError']);
}
}
function setCurrentTable($tableName) {
$this->result = null;
$this->start = 0;
$this->currentTable = $tableName;
}
function getInsertStatement($tableName) {
if(empty($this->result)) {
$sql = "select * from $tableName";
$this->result = $this->connection->_Execute($sql,false);
$this->checkError($result,$sql.' '.$this->connection->ErrorMsg().' '.
$this->connection->ErrorNo());
$this->start = 0;
}
$rowCount = $this->result->RecordCount();
$i =0;
$sql = 'INSERT INTO '.$tableName;//
while($i < $this->valueListSize && $this->start < $rowCount) {
$row = $this->result->GetRowAssoc(2);
if($i ==0 ) {
$sql .= ' ( '.$this->getInsertNames($this->connection, $this->result, $row).' )';
$sql .= ' VALUES ';
}
$values = $this->getInsertValues($this->connection, $this->result, $row);
if($i > 0) {
$sql .= ', ';
}
$sql .= '( '.$values.' )';
$i++;
$this->start++;
$this->result->MoveNext();
}
if($i ==0) {
return '';
}
$sql .= ';';
return $sql;
}
function getTableCreateStatement($tableName) {
$sql = "show create table $tableName";
$result = $this->connection->_Execute($sql,false);
$this->checkError($result,$sql.' '.$this->connection->ErrorMsg().' '.
$this->connection->ErrorNo());
$data = $result->FetchRow();
return $data[1];
}
static $cacheRS = false;
static $cacheSig = 0;
static $cacheCols;
function getInsertNames(&$zthis,&$rs,$arrFields,$magicq=false,$force=2) {
$tableName = '';
$values = '';
$fields = '';
$recordSet = null;
$arrFields = _array_change_key_case($arrFields);
$fieldInsertedCount = 0;
if (is_string($rs)) {
//ok we have a table name
//try and get the column info ourself.
$tableName = $rs;
//we need an object for the recordSet
//because we have to call MetaType.
//php can't do a $rsclass::MetaType()
$rsclass = $zthis->rsPrefix.$zthis->databaseType;
$recordSet = new $rsclass(-1,$zthis->fetchMode);
$recordSet->connection = &$zthis;
if (is_string(self::$cacheRS) && self::$cacheRS == $rs) {
$columns =& self::$cacheCols;
} else {
$columns = $zthis->MetaColumns( $tableName );
self::$cacheRS = $tableName;
self::$cacheCols = $columns;
}
} else if (is_subclass_of($rs, 'adorecordset')) {
if (isset($rs->insertSig) && is_integer(self::$cacheRS) &&
self::$cacheRS == $rs->insertSig) {
$columns =& self::$cacheCols;
} else {
for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++)
$columns[] = $rs->FetchField($i);
self::$cacheRS = self::$cacheSig;
self::$cacheCols = $columns;
$rs->insertSig = self::$cacheSig++;
}
$recordSet =& $rs;
} else {
printf(ADODB_BAD_RS,'GetInsertSQL');
return false;
}
// Loop through all of the fields in the recordset
foreach( $columns as $field ) {
$upperfname = strtoupper($field->name);
if (adodb_key_exists($upperfname,$arrFields,$force)) {
$bad = false;
if (strpos($upperfname,' ') !== false)
$fnameq = $zthis->nameQuote.$upperfname.$zthis->nameQuote;
else
$fnameq = $upperfname;
// Get the name of the fields to insert
$fields[] = $fnameq;
}
}
$fields = implode(', ',$fields);
return $fields;
}
function getInsertValues(&$zthis,&$rs,$arrFields,$magicq=false,$force=2) {
$tableName = '';
$values = '';
$fields = '';
$recordSet = null;
$arrFields = _array_change_key_case($arrFields);
$fieldInsertedCount = 0;
if (is_string($rs)) {
//ok we have a table name
//try and get the column info ourself.
$tableName = $rs;
//we need an object for the recordSet
//because we have to call MetaType.
//php can't do a $rsclass::MetaType()
$rsclass = $zthis->rsPrefix.$zthis->databaseType;
$recordSet = new $rsclass(-1,$zthis->fetchMode);
$recordSet->connection = &$zthis;
if (is_string(self::$cacheRS) && self::$cacheRS == $rs) {
$columns =& self::$cacheCols;
} else {
$columns = $zthis->MetaColumns( $tableName );
self::$cacheRS = $tableName;
self::$cacheCols = $columns;
}
} else if (is_subclass_of($rs, 'adorecordset')) {
if (isset($rs->insertSig) && is_integer(self::$cacheRS) &&
self::$cacheRS == $rs->insertSig) {
$columns =& self::$cacheCols;
} else {
for ($i=0, $max=$rs->FieldCount(); $i < $max; $i++)
$columns[] = $rs->FetchField($i);
self::$cacheRS = self::$cacheSig;
self::$cacheCols = $columns;
$rs->insertSig = self::$cacheSig++;
}
$recordSet =& $rs;
} else {
printf(ADODB_BAD_RS,'GetInsertSQL');
return false;
}
// Loop through all of the fields in the recordset
foreach( $columns as $field ) {
$upperfname = strtoupper($field->name);
if (adodb_key_exists($upperfname,$arrFields,$force)) {
$bad = false;
if (strpos($upperfname,' ') !== false)
$fnameq = $zthis->nameQuote.$upperfname.$zthis->nameQuote;
else
$fnameq = $upperfname;
$type = $recordSet->MetaType($field->type);
if (is_null($arrFields[$upperfname])
|| (empty($arrFields[$upperfname]) && strlen($arrFields[$upperfname]) == 0)
|| $arrFields[$upperfname] === 'null') {
switch ($force) {
case 0: // we must always set null if missing
$bad = true;
break;
case 1:
$values .= "null, ";
break;
case 2:
//Set empty
$arrFields[$upperfname] = "";
$values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq,
$arrFields, $magicq);
break;
default:
case 3:
//Set the value that was given in array, so you can give both null and
//empty values
if (is_null($arrFields[$upperfname]) || $arrFields[$upperfname] ===
'null') {
$values .= "null, ";
} else {
$values .= _adodb_column_sql($zthis, 'I', $type, $upperfname,
$fnameq, $arrFields, $magicq);
}
break;
} // switch
} else {
//we do this so each driver can customize the sql for
//DB specific column types.
//Oracle needs BLOB types to be handled with a returning clause
//postgres has special needs as well
$values .= _adodb_column_sql($zthis, 'I', $type, $upperfname, $fnameq,
$arrFields, $magicq);
}
if ($bad) continue;
// Set the counter for the number of fields that will be inserted.
$fieldInsertedCount++;
// Get the name of the fields to insert
$fields .= $fnameq . ", ";
}
}
// If there were any inserted fields then build the rest of the insert query.
if ($fieldInsertedCount <= 0) return false;
// Get the table name from the existing query.
if (!$tableName) {
if (!empty($rs->tableName)) $tableName = $rs->tableName;
else if (preg_match("/FROM\s+".ADODB_TABLE_REGEX."/is", $rs->sql, $tableName))
$tableName = $tableName[1];
else
return false;
}
// Strip off the comma and space on the end of both the fields
// and their values.
$fields = substr($fields, 0, -2);
$values = substr($values, 0, -2);
return $values;
}
function checkError($result,$sql){
if($result === false){
throw new DatabaseBackupException(DatabaseBackupErrorCode::$SQL_EXECUTION_ERROR,
DatabaseBackup::$langString['SqlExecutionError'].' '.$sql);
}
}
}
?>