%PDF- %PDF-
Direktori : /home/bitrix/www/bitrix/modules/perfmon/lib/sql/ |
Current File : /home/bitrix/www/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; } }