Your IP : 3.142.132.253


Current Path : /home/bitrix/ext_www/ballu.in.ua/bitrix/modules/perfmon/lib/sql/
Upload File :
Current File : /home/bitrix/ext_www/ballu.in.ua/bitrix/modules/perfmon/lib/sql/updater.php

<?php
namespace Bitrix\Perfmon\Sql;

use Bitrix\Main\NotSupportedException;
use Bitrix\Perfmon\Php;

class Updater
{
	protected $dbType = '';
	protected $delimiter = '';
	/** @var \Bitrix\Perfmon\Sql\Table  */
	protected $tableCheck = null;
	protected $conditions = array();

	/** @var \Bitrix\Perfmon\Php\Statement[]*/
	protected  $statements = array();

	/**
	 * Sets database type. Currently supported:
	 * - MYSQL
	 * - ORACLE
	 * - MSSQL
	 *
	 * @param string $dbType Database type.
	 *
	 * @return Updater
	 */
	public function setDbType($dbType = '')
	{
		$this->dbType = (string)$dbType;
		return $this;
	}

	/**
	 * Sets DDL delimiter for parsing.
	 *
	 * @param string $delimiter DDL statements delimiter.
	 *
	 * @return Updater
	 */
	public function setDelimiter($delimiter = '')
	{
		$this->delimiter = (string)$delimiter;
		return $this;
	}

	/**
	 * Returns array of generated statements.
	 *
	 * @return \Bitrix\Perfmon\Php\Statement[]
	 */
	public function getStatements()
	{
		return $this->statements;
	}

	/**
	 * Produces updater code.
	 *
	 * @param string $sourceSql Source DDL statements.
	 * @param string $targetSql Target DDL statements.
	 *
	 * @return string
	 * @throws NotSupportedException
	 */
	public function generate($sourceSql, $targetSql)
	{
		$source = new Schema;
		$source->createFromString($sourceSql, $this->delimiter);

		$target = new Schema;
		$target->createFromString($targetSql, $this->delimiter);

		$diff = Compare::diff($source ,$target);
		if ($diff)
		{
			$sourceTables = $source->tables->getList();
			if ($sourceTables)
			{
				$this->tableCheck = array_shift($sourceTables);
			}
			else
			{
				$targetTables = $target->tables->getList();
				if ($targetTables)
				{
					$this->tableCheck = array_shift($targetTables);
				}
				else
				{
					$this->tableCheck = null;
				}
			}

			if (!$this->tableCheck)
				throw new NotSupportedException("no CHECK TABLE found.");

			$php = $this->handle($diff);

			return
				"if (\$updater->CanUpdateDatabase() && \$updater->TableExists('".EscapePHPString($this->tableCheck->name)."'))\n".
				"{\n".
				"\tif (\$DB->type == \"".EscapePHPString($this->dbType)."\")\n".
				"\t{\n".
				$php.
				"\t}\n".
				"}\n";
		}
		else
		{
			return "";
		}
	}

	/**
	 * @param array $diff Difference pairs.
	 *
	 * @return string
	 */
	protected function handle(array $diff)
	{
		$this->conditions = array();
		foreach ($diff as $pair)
		{
			if (!isset($pair[0]))
			{
				$this->handleCreate($pair[1]);
			}
			elseif (!isset($pair[1]))
			{
				$this->handleDrop($pair[0]);
			}
			else
			{
				$this->handleChange($pair[0], $pair[1]);
			}
		}

		$result = "";
		foreach ($this->conditions as $condition => $statements)
		{
			$result .= $condition;
			if ($condition)
				$result .= "\t\t{\n";
			$result .= implode("", $statements);
			if ($condition)
				$result .= "\t\t}\n";
		}

		return $result;
	}

	/**
	 * @param BaseObject $object Database schema object.
	 *
	 * @return void
	 */
	protected function handleCreate(BaseObject $object)
	{
		if ($object instanceof Sequence || $object instanceof Procedure)
		{
			$ddl = $object->getCreateDdl($this->dbType);

			$this->conditions[""][] = $this->multiLinePhp("\t\t\$DB->Query(\"", $ddl, "\", true);\n");

			$stmt = $this->createStatement("\$DB->Query(\"", $ddl, "\", true);");
			$stmt->dependOn = $this->tableCheck->getLowercasedName();
			$stmt->addCondition("\$updater->CanUpdateDatabase()");
			$stmt->addCondition("\$DB->type == \"".EscapePHPString($this->dbType)."\"");
			$stmt->addCondition("\$updater->TableExists(\"".EscapePHPString($this->tableCheck->getLowercasedName())."\")");
		}
		elseif ($object instanceof Table)
		{
			$ddl = $object->getCreateDdl($this->dbType);
			$predicate = "!\$updater->TableExists(\"".EscapePHPString($object->name)."\")";
			$cond = "\t\tif ($predicate)\n";

			$this->conditions[$cond][] = $this->multiLinePhp("\t\t\t\$DB->Query(\"\n\t\t\t\t", str_replace("\n", "\n\t\t\t\t", $ddl), "\n\t\t\t\");\n");

			$stmt = $this->createStatement("\$DB->Query(\"", $ddl, "\", true);");
			$stmt->tableName = $object->getLowercasedName();
			$stmt->addCondition("\$updater->CanUpdateDatabase()");
			$stmt->addCondition("\$DB->type == \"".EscapePHPString($this->dbType)."\"");
			$stmt->addCondition("\$updater->TableExists(\"".EscapePHPString($this->tableCheck->getLowercasedName())."\")");
			$stmt->addCondition("!\$updater->TableExists(\"".EscapePHPString($object->getLowercasedName())."\")");
		}
		elseif ($object instanceof Column)
		{
			$ddl = $object->getCreateDdl($this->dbType);
			$predicate = "\$updater->TableExists(\"".EscapePHPString($object->parent->getLowercasedName())."\")";
			$cond = "\t\tif ($predicate)\n";
			$predicate2 = "!\$DB->Query(\"SELECT ".EscapePHPString($object->name)." FROM ".EscapePHPString($object->parent->getLowercasedName())." WHERE 1=0\", true)";

			$this->conditions[$cond][] =
				"\t\t\tif ($predicate2)\n".
				"\t\t\t{\n".
				$this->multiLinePhp("\t\t\t\t\$DB->Query(\"", $ddl, "\");\n").
				"\t\t\t}\n";

			$stmt = $this->createStatement("\$DB->Query(\"", $ddl, "\");");
			$stmt->dependOn = $object->parent->getLowercasedName();
			$stmt->addCondition("\$updater->CanUpdateDatabase()");
			$stmt->addCondition("\$DB->type == \"".EscapePHPString($this->dbType)."\"");
			$stmt->addCondition($predicate);
			$stmt->addCondition($predicate2);
		}
		elseif ($object instanceof Index)
		{
			$ddl = $object->getCreateDdl($this->dbType);
			$predicate = "\$updater->TableExists(\"".EscapePHPString($object->parent->getLowercasedName())."\")";
			$cond = "\t\tif ($predicate)\n";
			$predicate2 = "!\$DB->IndexExists(\"".EscapePHPString($object->parent->getUnquotedName())."\", array(".$this->multiLinePhp("\"", $object->getUnquotedName($object->columns), "\", ")."))";

			$this->conditions[$cond][] =
				"\t\t\tif ($predicate2)\n".
				"\t\t\t{\n".
				$this->multiLinePhp("\t\t\t\t\$DB->Query(\"", $ddl, "\");\n").
				"\t\t\t}\n";

			$stmt = $this->createStatement("\$DB->Query(\"", $ddl, "\");");
			$stmt->dependOn = $object->parent->getLowercasedName();
			$stmt->addCondition("\$updater->CanUpdateDatabase()");
			$stmt->addCondition("\$DB->type == \"".EscapePHPString($this->dbType)."\"");
			$stmt->addCondition($predicate);
			$stmt->addCondition($predicate2);
		}
		elseif ($object instanceof Trigger || $object instanceof Constraint)
		{
			$ddl = $object->getCreateDdl($this->dbType);
			$predicate = "\$updater->TableExists(\"".EscapePHPString($object->parent->getLowercasedName())."\")";
			$cond = "\t\tif ($predicate)\n";

			$this->conditions[$cond][] = $this->multiLinePhp("\t\t\t\$DB->Query(\"", $ddl, "\", true);\n");

			$stmt = $this->createStatement("\$DB->Query(\"", $ddl, "\", true);");
			$stmt->dependOn = $object->parent->getLowercasedName();
			$stmt->addCondition("\$updater->CanUpdateDatabase()");
			$stmt->addCondition("\$DB->type == \"".EscapePHPString($this->dbType)."\"");
			$stmt->addCondition($predicate);
		}
		else
		{
			$this->conditions[""][] = "\t\t//create for ".get_class($object)." not supported yet\n";
			$stmt = $this->createStatement("", "//create for ".get_class($object)." not supported yet", "");
		}
		
		if ($stmt)
		{
			$this->statements[] = $stmt;
		}
	}

	/**
	 * @param BaseObject $object Database schema object.
	 *
	 * @return void
	 */
	protected function handleDrop(BaseObject $object)
	{
		if ($object instanceof Sequence || $object instanceof Procedure)
		{
			$ddl = $object->getDropDdl($this->dbType);

			$this->conditions[""][] = "\t\t\$DB->Query(\"".EscapePHPString($ddl)."\", true);\n";

			$stmt = $this->createStatement("\$DB->Query(\"", $ddl, "\", true);");
			$stmt->addCondition("\$updater->CanUpdateDatabase()");
			$stmt->addCondition("\$DB->type == \"".EscapePHPString($this->dbType)."\"");
			$stmt->addCondition("\$updater->TableExists(\"".EscapePHPString($this->tableCheck->getLowercasedName())."\")");
		}
		elseif ($object instanceof Table)
		{
			$ddl = $object->getDropDdl($this->dbType);
			$predicate = "\$updater->TableExists(\"".EscapePHPString($object->getLowercasedName())."\")";
			$cond = "\t\tif ($predicate)\n";

			$this->conditions[$cond][] = $this->multiLinePhp("\t\t\t\$DB->Query(\"", $ddl, "\");\n");

			$stmt = $this->createStatement("\$DB->Query(\"", $ddl, "\");");
			$stmt->addCondition("\$updater->CanUpdateDatabase()");
			$stmt->addCondition("\$DB->type == \"".EscapePHPString($this->dbType)."\"");
			$stmt->addCondition($predicate);
		}
		elseif ($object instanceof Column)
		{
			$ddl = $object->getDropDdl($this->dbType);
			$predicate = "\$updater->TableExists(\"".EscapePHPString($object->parent->name)."\")";
			$cond = "\t\tif ($predicate)\n";
			$predicate2 = "\$DB->Query(\"SELECT ".EscapePHPString($object->name)." FROM ".EscapePHPString($object->parent->getLowercasedName())." WHERE 1=0\", true)";

			$this->conditions[$cond][] =
				"\t\t\tif ($predicate2)\n".
				"\t\t\t{\n".
				$this->multiLinePhp("\t\t\t\t\$DB->Query(\"", $ddl, "\");\n").
				"\t\t\t}\n";

			$stmt = $this->createStatement("\$DB->Query(\"", $ddl, "\");");
			$stmt->addCondition("\$updater->CanUpdateDatabase()");
			$stmt->addCondition("\$DB->type == \"".EscapePHPString($this->dbType)."\"");
			$stmt->addCondition($predicate);
			$stmt->addCondition($predicate2);
		}
		elseif ($object instanceof Index)
		{
			$ddl = $object->getDropDdl($this->dbType);
			$predicate = "\$updater->TableExists(\"".EscapePHPString($object->parent->getLowercasedName())."\")";
			$cond = "\t\tif ($predicate)\n";
			$predicate2 = "\$DB->IndexExists(\"".EscapePHPString($object->parent->getUnquotedName())."\", array(".$this->multiLinePhp("\"", $object->getUnquotedName($object->columns), "\", ")."))";

			$this->conditions[$cond][] =
				"\t\t\tif ($predicate2)\n".
				"\t\t\t{\n".
				$this->multiLinePhp("\t\t\t\t\$DB->Query(\"", $ddl, "\");\n").
				"\t\t\t}\n";

			$stmt = $this->createStatement("\$DB->Query(\"", $ddl, "\");");
			$stmt->addCondition("\$updater->CanUpdateDatabase()");
			$stmt->addCondition("\$DB->type == \"".EscapePHPString($this->dbType)."\"");
			$stmt->addCondition($predicate);
			$stmt->addCondition($predicate2);
		}
		elseif ($object instanceof Trigger || $object instanceof Constraint)
		{
			$ddl = $object->getDropDdl($this->dbType);
			$predicate = "\$updater->TableExists(\"".EscapePHPString($object->parent->getLowercasedName())."\")";
			$cond = "\t\tif ($predicate)\n";

			$this->conditions[$cond][] = $this->multiLinePhp("\t\t\t\$DB->Query(\"", $ddl, "\", true);\n");

			$stmt = $this->createStatement("\$DB->Query(\"", $ddl, "\", true);");
			$stmt->addCondition("\$updater->CanUpdateDatabase()");
			$stmt->addCondition("\$DB->type == \"".EscapePHPString($this->dbType)."\"");
			$stmt->addCondition($predicate);
		}
		else
		{
			$this->conditions[""][] = "\t\t//drop for ".get_class($object)." not supported yet\n";
			$stmt = $this->createStatement("", "//drop for ".get_class($object)." not supported yet", "");
		}

		if ($stmt)
		{
			$this->statements[] = $stmt;
		}
	}

	/**
	 * @param BaseObject $source Source object.
	 * @param BaseObject $target Target object.
	 *
	 * @return void
	 */
	protected function handleChange(BaseObject $source, BaseObject $target)
	{
		if ($source instanceof Sequence || $source instanceof Procedure)
		{
			$this->conditions[""][] =
				$this->multiLinePhp("\t\t\$DB->Query(\"", $source->getDropDdl($this->dbType), "\", true);\n").
				$this->multiLinePhp("\t\t\$DB->Query(\"", $target->getCreateDdl($this->dbType), "\", true);\n");

			$dropStmt = $this->createStatement("\$DB->Query(\"", $source->getDropDdl($this->dbType), "\", true);");
			$createStmt = $this->createStatement("\$DB->Query(\"", $target->getCreateDdl($this->dbType), "\", true);");
			$stmt = new Php\Statement;
			$stmt->dependOn = $this->tableCheck->getLowercasedName();
			$stmt->merge($dropStmt);
			$stmt->merge($createStmt);
			$stmt->addCondition("\$updater->CanUpdateDatabase()");
			$stmt->addCondition("\$DB->type == \"".EscapePHPString($this->dbType)."\"");
			$stmt->addCondition("\$updater->TableExists(\"".EscapePHPString($this->tableCheck->getLowercasedName())."\")");
		}
		elseif ($target instanceof Column)
		{
			$ddl = $source->getModifyDdl($target, $this->dbType);
			$predicate = "\$updater->TableExists(\"".EscapePHPString($source->parent->getLowercasedName())."\")";
			$cond = "\t\tif ($predicate)\n";
			$predicate2 = "\$DB->Query(\"SELECT ".EscapePHPString($source->name)." FROM ".EscapePHPString($source->parent->getLowercasedName())." WHERE 1=0\", true)";

			$this->conditions[$cond][] =
				"\t\t\tif ($predicate2)\n".
				"\t\t\t{\n".
				$this->multiLinePhp("\t\t\t\t\$DB->Query(\"", $ddl, "\");\n").
				"\t\t\t}\n";

			$stmt = $this->createStatement("\$DB->Query(\"", $ddl, "\");");
			$stmt->dependOn = $source->parent->getLowercasedName();
			$stmt->addCondition("\$updater->CanUpdateDatabase()");
			$stmt->addCondition("\$DB->type == \"".EscapePHPString($this->dbType)."\"");
			$stmt->addCondition($predicate);
			$stmt->addCondition($predicate2);
		}
		elseif ($source instanceof Index)
		{
			$predicate = "\$updater->TableExists(\"".EscapePHPString($source->parent->getLowercasedName())."\")";
			$cond = "\t\tif ($predicate)\n";
			$predicate2 = "\$DB->IndexExists(\"".EscapePHPString($source->parent->getUnquotedName())."\", array(".$this->multiLinePhp("\"", $source->getUnquotedName($source->columns), "\", ")."))";
			$this->conditions[$cond][] =
				"\t\t\tif ($predicate2)\n".
				"\t\t\t{\n".
				$this->multiLinePhp("\t\t\t\t\$DB->Query(\"", $source->getDropDdl($this->dbType), "\");\n").
				$this->multiLinePhp("\t\t\t\t\$DB->Query(\"", $target->getCreateDdl($this->dbType), "\");\n").
				"\t\t\t}\n";

			$dropStmt = $this->createStatement("\$DB->Query(\"", $source->getDropDdl($this->dbType), "\", true);");
			$createStmt = $this->createStatement("\$DB->Query(\"", $target->getCreateDdl($this->dbType), "\", true);");
			$stmt = new Php\Statement;
			$stmt->dependOn = $source->parent->getLowercasedName();
			$stmt->merge($dropStmt);
			$stmt->merge($createStmt);
			$stmt->addCondition("\$updater->CanUpdateDatabase()");
			$stmt->addCondition("\$DB->type == \"".EscapePHPString($this->dbType)."\"");
			$stmt->addCondition($predicate);
			$stmt->addCondition($predicate2);
			$stmt->addCondition("!\$DB->IndexExists(\"".EscapePHPString($target->parent->getUnquotedName())."\", array(".$this->multiLinePhp("\"", $source->getUnquotedName($source->columns), "\", ")."))");
		}
		elseif ($source instanceof Trigger || $source instanceof Constraint)
		{
			$ddl = $source->getModifyDdl($target, $this->dbType);
			$predicate = "\$updater->TableExists(\"".EscapePHPString($source->parent->getLowercasedName())."\")";
			$cond = "\t\tif ($predicate)\n";

			$this->conditions[$cond][] = $this->multiLinePhp("\t\t\t\$DB->Query(\"", $ddl, "\", true);\n");
			$stmt = $this->createStatement("\$DB->Query(\"", $ddl, "\", true);");
			$stmt->dependOn = $source->parent->getLowercasedName();
			$stmt->addCondition("\$updater->CanUpdateDatabase()");
			$stmt->addCondition("\$DB->type == \"".EscapePHPString($this->dbType)."\"");
			$stmt->addCondition($predicate);
		}
		else
		{
			$this->conditions[""][] = "\t\t//change for ".get_class($source)." not supported yet\n";
			$stmt = $this->createStatement("", "//change for ".get_class($source)." not supported yet", "");
		}

		if ($stmt)
		{
			$this->statements[] = $stmt;
		}
	}

	/**
	 * Returns escaped php code repeated for body? prefixed with $prefix and suffixed with $suffix.
	 *
	 * @param string $prefix Prefix string for each from body.
	 * @param array|string $body Strings to be escaped.
	 * @param string $suffix Suffix string for each from body.
	 *
	 * @return string
	 */
	protected function multiLinePhp($prefix, $body, $suffix)
	{
		$result  = array();
		if (is_array($body))
		{
			foreach ($body as $line)
			{
				$result[] = $prefix.EscapePHPString($line).$suffix;
			}
		}
		else
		{
			$result[] = $prefix.EscapePHPString($body).$suffix;
		}
		return implode("", $result);
	}

	/**
	 * Returns Php\Statement object with escaped php code repeated for body? prefixed with $prefix and suffixed with $suffix.
	 *
	 * @param string $prefix Prefix string for each from body.
	 * @param array|string $body Strings to be escaped.
	 * @param string $suffix Suffix string for each from body.
	 *
	 * @return \Bitrix\Perfmon\Php\Statement
	 */
	protected function createStatement($prefix, $body, $suffix)
	{
		$result  = new Php\Statement;
		if (is_array($body))
		{
			foreach ($body as $line)
			{
				$result->addLine($prefix.EscapePHPString($line).$suffix);
			}
		}
		else
		{
			$result->addLine($prefix.EscapePHPString($body).$suffix);
		}
		return $result;
	}
}