Your IP : 3.147.140.13


Current Path : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/perfmon/tools/
Upload File :
Current File : /var/www/www-root/data/www/monolith-realty.ru/bitrix/modules/perfmon/tools/mysql_to_pgsql.php

#!/usr/bin/php
<?php
namespace Bitrix\Main;

//utf8: 21844 * 3 = 65532
//utf8mb4: 21844 * 4 = 87376 # too much
//utf8mb4: 16383 * 4 = 65532
define('MAX_VARCHAR_LEN', 16383);
define('CHAR_WIDTH', 4);
define('CHARSET', 'utf8mb4');
define('COLLATION', 'utf8mb4_0900_ai_ci');

error_reporting(E_ALL & ~E_NOTICE & ~E_DEPRECATED & ~E_STRICT & ~E_WARNING & ~E_USER_WARNING & ~E_COMPILE_WARNING);

class NotSupportedException extends \Exception {}

spl_autoload_register(
	function ($class_name)
	{
		if (strpos(strtolower($class_name), 'bitrix\\perfmon\\') === 0)
		{
			$file_name = substr($class_name, strlen('bitrix\\perfmon\\'));
			$file_name = strtolower(preg_replace('/(?<=[a-z])([A-Z])/', '_$1', $file_name));
			$file_name = str_replace('\\', '/', $file_name);
			require_once __DIR__ . '/../../perfmon/lib/' . $file_name . '.php';
		}
	}
);

$files = [];
$mysqldump = '';
$host = '';
$user = '';
$password = '';
$databases = [];
$table = '';

for ($i = 1, $c = count($argv); $i < $c; $i++)
{
	if (preg_match("/^--file=(.+)\$/", $argv[$i], $match))
	{
		$files[] = $match[1];
	}
	elseif (preg_match("/^--mysqldump=(.+)\$/", $argv[$i], $match))
	{
		$mysqldump = $match[1];
	}
	elseif (preg_match("/^--host=(.+)\$/", $argv[$i], $match))
	{
		$host = $match[1];
	}
	elseif (preg_match("/^--user=(.+)\$/", $argv[$i], $match))
	{
		$user = $match[1];
	}
	elseif (preg_match("/^--password=(.+)\$/", $argv[$i], $match))
	{
		$password = $match[1];
	}
	elseif (preg_match("/^--database=(.+)\$/", $argv[$i], $match))
	{
		$databases[] = $match[1];
	}
	elseif (preg_match("/^--table=(.+)\$/", $argv[$i], $match))
	{
		$table = $match[1];
	}
	else
	{
		$files[] = $argv[$i];
	}
}

if ($mysqldump)
{
	$f = fopen($mysqldump, 'r');
	if ($f)
	{
		echo "SET client_min_messages TO WARNING;\n";

		$match = [];
		$tableName = '';
		$ddl = '';
		while ($line = fgets($f))
		{
			if (preg_match('/^CREATE TABLE `(.*?)` /', $line, $match))
			{
				$tableName = $match[1];
				$ddl = $line;
			}
			elseif ($tableName)
			{
				$ddl .= $line;
				if (preg_match('/^\)/', $line))
				{
					//echo $ddl;
					echo generate_schema_ddl($ddl, $table, true, $mysqldump);
					$tableName = '';
					$ddl = '';
				}
			}
			elseif (preg_match("/^(?:INSERT|REPLACE) INTO `(.*?)` VALUES (.+);\s*\$/", $line, $match))
			{
				$rows = parse_values($match[2]);
				foreach ($rows as $row)
				{
					echo 'INSERT INTO "' . $match[1] . '" VALUES ' . $row . ";\n";
				}
			}
		}
	}
}

if ($files)
{
	foreach ($files as $file_name)
	{
		$sql = file_get_contents($file_name, "r");
		echo generate_schema_ddl($sql, $table, false, $file_name);
	}
}

if ($host || $user || $password)
{
	echo "SET client_min_messages TO WARNING;\n";

	$dbh = new \PDO("mysql:host=".$host.";port=3306", $user, $password);
	$dbh->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
	if (!$databases)
	{
		$r = $dbh->query("show databases");
		while ($a = $r->fetch(\PDO::FETCH_ASSOC))
		{
			if (
				$a['Database'] !== 'information_schema'
				&& $a['Database'] !== 'mysql'
				&& $a['Database'] !== 'performance_schema'
				&& $a['Database'] !== 'sys'
			)
			{
				$databases[] = $a['Database'];
			}
		}
	}

	foreach ($databases as $db_name)
	{
		$dbh->exec("use ".$db_name);
		$sql = '';
		$r = $dbh->query("show tables");
		while ($a = $r->fetch(\PDO::FETCH_ASSOC))
		{
			$r2 = $dbh->query("show create table `".$a['Tables_in_' . $db_name]."`");
			$a2 = $r2->fetch(\PDO::FETCH_ASSOC);
			if (isset($a2['Table']))
			{
				$sql .= $a2['Create Table'] . ";\n";
			}
		}
		echo generate_schema_ddl($sql, $table, true, $db_name);
	}
}

function unquote($identifier)
{
	return trim($identifier, "`");
}

function convertColumnType($columnType, $length, $unsigned)
{
	switch ($columnType)
	{
		case 'TINYINT':
		case 'SMALLINT':
			return $unsigned ? 'int' : 'smallint';
		case 'BOOL':
		case 'BOOLEAN':
			return 'smallint';
		case 'MEDIUMINT':
		case 'INT':
		case 'INTEGER':
			return $unsigned ? 'int8' : 'int';
		case 'BIGINT':
			return 'int8';
		case 'DECIMAL':
		case 'NUMERIC':
			return strtolower($columnType);
		case 'FLOAT':
			return 'real';
		case 'DOUBLE':
			return 'double precision';
		case 'CHAR':
			return 'char(' . $length . ')';
		case 'VARCHAR':
			return 'varchar(' . $length . ')';
		case 'VARBINARY':
		case 'MEDIUMBLOB':
		case 'LONGBLOB':
		case 'BLOB':
			return 'bytea';
		case 'TEXT':
		case 'TINYTEXT':
		case 'MEDIUMTEXT':
		case 'LONGTEXT':
			return 'text';
		case 'DATE':
			return 'date';
		case 'DATETIME':
		case 'TIMESTAMP':
			return 'timestamp';
		case 'ENUM':
			return 'enum';
		default:
			return '//unknown type ' . $columnType;
	}
}

function generate_schema_ddl($sql, $tableFilter, $isDump, $source)
{
	$ddl = "";
	$match = [];

	//todo: Unsupported statement by Perfmon\Sql\Schema
	$sql = str_replace('USING BTREE', ' ', $sql);

	$s = new \Bitrix\Perfmon\Sql\Schema;
	$s->createFromString($sql, ';');
	/** @var \Bitrix\Perfmon\Sql\Table $table */
	foreach ($s->tables->getList() as $table)
	{
		if ($tableFilter && unquote($table->name) !== $tableFilter)
		{
			continue;
		}

		if ($isDump)
		{
			$ddl .= "DROP TABLE IF EXISTS " . unquote($table->name) . ";\n";
		}

		$autoIncrementValue = null;
		$autoIncrementColumn = '';
		$inset = [];
		/** @var \Bitrix\Perfmon\Sql\Column $column */
		foreach ($table->columns->getList() as $column)
		{
			$columnDefinition = unquote($column->name);
			if ($columnDefinition === 'OFFSET' || $columnDefinition === 'KEY')
			{
				$columnDefinition = '"' . strtolower($columnDefinition) . '"';
			}

			$hasAutoIncrement = preg_match('/AUTO_INCREMENT/i', $column->body) > 0;
			if ($hasAutoIncrement && preg_match('/AUTO_INCREMENT=(\d+)/', $table->body, $match))
			{
				$autoIncrementValue = $match[1];
				$autoIncrementColumn = unquote($column->name);
			}
			$type = convertColumnType($column->type, $column->length, $column->unsigned);

			if ($type === 'enum' && $column->enum)
			{
				$enumType = 't_' . preg_replace('/^b_/i', '', unquote($table->name)) . '_' . unquote($column->name);
				$ddl .= 'DROP TYPE IF EXISTS ' . $enumType . ";\n";
				$ddl .= 'CREATE TYPE ' . $enumType . " AS ENUM ('" . implode("', '", $column->enum) . "');\n";
				$columnDefinition .= ' ' . $enumType;
				fwrite(STDERR, "Warning: " . $source . ": " . $table->name . '.' . $column->name ." is enum. Convert to char.\n");
			}
			else
			{
				$columnDefinition .= ' ' . $type . ($hasAutoIncrement ? ' GENERATED BY DEFAULT AS IDENTITY' : '');
			}

			if (!$column->nullable || $hasAutoIncrement)
			{
				$columnDefinition .= ' NOT NULL';
			}

			if ($column->unsigned)
			{
				fwrite(STDERR, "Notice: " . $source . ": " . $table->name . '.' . $column->name ." is unsigned. Consider to convert to wider type and remove unsigned defunition.\n");
			}

			if ($column->type === 'TIMESTAMP')
			{
				$columnDefinition .= " DEFAULT CURRENT_TIMESTAMP";
				fwrite(STDERR, "Warning: " . $source . ": " . $table->name . '.' . $column->name ." is timestamp. Convert to datetime.\n");
			}
			elseif (!is_null($column->default) && strlen($column->default) > 0)
			{
				$default = str_replace('"', "'", $column->default);
				$default = str_replace("'0000-00-00 00:00:00'", "", $default);
				$default = str_replace('NOW', "CURRENT_TIMESTAMP", $default);
				$default = str_replace('now', "CURRENT_TIMESTAMP", $default);
				$default = str_replace('false', "0", $default);
				$default = trim($default, " \t\n\r");
				if ($default !== '')
				{
					$columnDefinition .= " DEFAULT " . $default;
				}
			}
			$inset[] = $columnDefinition;
		}

		/** @var \Bitrix\Perfmon\Sql\Constraint $constraint */
		foreach ($table->constraints->getList() as $constraint)
		{
			if (preg_match('/^PRIMARY/i', $constraint->body) > 0)
			{
				$inset[] = "PRIMARY KEY (" . implode(", ", array_map(
					function($x)
					{
						return trim(unquote(preg_replace('/\s+(desc|asc)/i', '', preg_replace('/\(\d+\)/', '', $x))), " \t\n\r");
					}, $constraint->columns)) . ")";
			}
			elseif (preg_match('/^UNIQUE/i', $constraint->body) > 0)
			{
				$inset[] = "UNIQUE (" . implode(", ", array_map(
					function($x)
					{
						return unquote($x);
					}, $constraint->columns)) . ")";
			}
		}

		if ($inset)
		{
			$ddl .= "\nCREATE TABLE " . unquote($table->name) . " (\n";

			$c = count($inset) - 1;
			foreach ($inset as $i => $line)
			{
				$ddl .= "  " . $line . ($i < $c ? "," : "") . "\n";
			}

			$ddl .= ");\n";
		}

		if ($autoIncrementValue && $isDump)
		{
			$ddl .= "ALTER TABLE " . unquote($table->name) . " ALTER COLUMN " . $autoIncrementColumn . " RESTART WITH " . $autoIncrementValue . ";\n";
		}

		$indexes = [];
		/** @var \Bitrix\Perfmon\Sql\Index $index */
		foreach ($table->indexes->getList() as $index)
		{
			$indexName = substr(
				($index->unique ? "ux_" : ($index->fulltext ? "tx_" : "ix_"))
				. unquote($table->name)
				. "_"
				. implode("_", array_map(
					function($x)
					{
						return strtolower(unquote(preg_replace('/\s*(\(\d+\)|asc|desc)(?![a-z0-9_])\s*/i', '', $x)));
					}, $index->columns))
				, 0, 63);
			if (array_key_exists($indexName, $indexes))
			{
				$i = ++$indexes[$indexName];
				$suffix = '_' . $i;
				$indexName = substr($indexName, 0, -strlen($suffix)) . $suffix;
			}
			else
			{
				$indexes[$indexName] = 0;
			}

			if ($isDump)
			{
				$ddl .= "DROP INDEX IF EXISTS " . $indexName . ";\n";
			}

			if ($index->fulltext)
			{
				$ddl .= "CREATE INDEX " . $indexName . " ON " . unquote($table->name) . " USING GIN (to_tsvector('english', " . implode(" || ", array_map(
					function($x)
					{
						return strtolower(unquote(preg_replace('/\s*(\(\d+\)|asc|desc)(?![a-z0-9_])\s*/i', '', $x)));
					}, $index->columns)) . "));\n";
			}
			else
			{
				$ddl .= "CREATE" . ($index->unique ? " UNIQUE " : " ") . "INDEX " . $indexName . " ON " . unquote($table->name) . " (" . implode(", ", array_map(
					function($x)
					{
						return strtolower(unquote(preg_replace('/\s*(\(\d+\)|asc|desc)(?![a-z0-9_])\s*/i', '', $x)));
					}, $index->columns)) . ");\n";
			}
		}
	}
	return $ddl;
}

function parse_values($values_str)
{
	static $search  = ['\\\'', '\\"'];
	static $replace = ['\'\'', '"'  ];
	$result = [];
	$tokens = token_get_all('<?php '.$values_str);
	$row = [];
	$c = count($tokens);
	for ($i = 1; $i < $c; $i++)
	{
		$token = $tokens[$i];
		if (
			($token == ',' || $token == ';')
			&& ($tokens[$i-1] == ')')
		)
		{
			if ($row)
			{
				$result[] = '('.implode('', $row).')';
			}
			$row = [];
		}
		elseif ($token == '(' || $token == ')')
		{
			//skip
		}
		elseif (is_array($token))
		{
			if ($token[0] === T_CONSTANT_ENCAPSED_STRING)
			{
				$escaped = str_replace($search, $replace, $token[1]);
				if ($escaped === "'0000-00-00 00:00:00'")
				{
					$escaped = "NULL";
				}
				elseif (
					preg_match('/\\\\[bfnrt\']/', $escaped)
					|| strpos($escaped, '\\\\') !== false
				)
				{
					$escaped = 'E' . $escaped;
				}
				$row[] = $escaped;
			}
			elseif (preg_match('/^0x[0-9A-F]+$/', $token[1]))
			{
				$row[] = 'decode(\'' . substr($token[1], 2) . '\', \'hex\')';
			}
			else
			{
				$row[] = $token[1];
			}
		}
		else
		{
			$row[] = $token;
		}
	}
	if ($row)
	{
		$result[] = '('.implode('', $row).')';
	}
	return $result;
}