OperationsExecutor.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Setup\Declaration\Schema;
  7. use Magento\Framework\App\ResourceConnection;
  8. use Magento\Framework\Setup\Declaration\Schema\DataSavior\DataSaviorInterface;
  9. use Magento\Framework\Setup\Declaration\Schema\Db\DbSchemaWriterInterface;
  10. use Magento\Framework\Setup\Declaration\Schema\Db\StatementAggregatorFactory;
  11. use Magento\Framework\Setup\Declaration\Schema\Db\StatementFactory;
  12. use Magento\Framework\Setup\Declaration\Schema\Diff\DiffInterface;
  13. use Magento\Framework\Setup\Declaration\Schema\Dto\ElementInterface;
  14. use Magento\Framework\Setup\Declaration\Schema\Operations\AddColumn;
  15. use Magento\Framework\Setup\Declaration\Schema\Operations\CreateTable;
  16. use Magento\Framework\Setup\Declaration\Schema\Operations\ReCreateTable;
  17. /**
  18. * Schema operations executor.
  19. *
  20. * Go through all available SQL operations and execute each one with data from change registry.
  21. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  22. */
  23. class OperationsExecutor
  24. {
  25. /**
  26. * Request keys.
  27. */
  28. const KEY_SAFE_MODE = 'safe-mode';
  29. const KEY_DATA_RESTORE = 'data-restore';
  30. /**
  31. * @var OperationInterface[]
  32. */
  33. private $operations;
  34. /**
  35. * @var Sharding
  36. */
  37. private $sharding;
  38. /**
  39. * @var ResourceConnection
  40. */
  41. private $resourceConnection;
  42. /**
  43. * @var StatementFactory
  44. */
  45. private $statementFactory;
  46. /**
  47. * @var DbSchemaWriterInterface
  48. */
  49. private $dbSchemaWriter;
  50. /**
  51. * @var StatementAggregatorFactory
  52. */
  53. private $statementAggregatorFactory;
  54. /**
  55. * @var DataSaviorInterface[]
  56. */
  57. private $dataSaviorsCollection;
  58. /**
  59. * @var DryRunLogger
  60. */
  61. private $dryRunLogger;
  62. /**
  63. * Constructor.
  64. *
  65. * @param array $operations
  66. * @param array $dataSaviorsCollection
  67. * @param Sharding $sharding
  68. * @param ResourceConnection $resourceConnection
  69. * @param StatementFactory $statementFactory
  70. * @param DbSchemaWriterInterface $dbSchemaWriter
  71. * @param StatementAggregatorFactory $statementAggregatorFactory
  72. * @param DryRunLogger $dryRunLogger
  73. */
  74. public function __construct(
  75. array $operations,
  76. array $dataSaviorsCollection,
  77. Sharding $sharding,
  78. ResourceConnection $resourceConnection,
  79. StatementFactory $statementFactory,
  80. DbSchemaWriterInterface $dbSchemaWriter,
  81. StatementAggregatorFactory $statementAggregatorFactory,
  82. DryRunLogger $dryRunLogger
  83. ) {
  84. $this->operations = $operations;
  85. $this->sharding = $sharding;
  86. $this->resourceConnection = $resourceConnection;
  87. $this->statementFactory = $statementFactory;
  88. $this->dbSchemaWriter = $dbSchemaWriter;
  89. $this->statementAggregatorFactory = $statementAggregatorFactory;
  90. $this->dataSaviorsCollection = $dataSaviorsCollection;
  91. $this->dryRunLogger = $dryRunLogger;
  92. }
  93. /**
  94. * Retrieve only destructive operation names.
  95. *
  96. * For example, drop_table, recreate_table, etc.
  97. *
  98. * @return array
  99. */
  100. public function getDestructiveOperations()
  101. {
  102. $operations = [];
  103. foreach ($this->operations as $operation) {
  104. if ($operation->isOperationDestructive()) {
  105. $operations[$operation->getOperationName()] = $operation->getOperationName();
  106. }
  107. }
  108. return $operations;
  109. }
  110. /**
  111. * In order to successfully run all operations we need to start setup for all
  112. * connections first.
  113. *
  114. * @return void
  115. */
  116. private function startSetupForAllConnections()
  117. {
  118. foreach ($this->sharding->getResources() as $resource) {
  119. $this->resourceConnection->getConnection($resource)
  120. ->startSetup();
  121. $this->resourceConnection->getConnection($resource)
  122. ->query('SET UNIQUE_CHECKS=0');
  123. }
  124. }
  125. /**
  126. * In order to revert previous state we need to end setup for all connections
  127. * connections first.
  128. *
  129. * @return void
  130. */
  131. private function endSetupForAllConnections()
  132. {
  133. foreach ($this->sharding->getResources() as $resource) {
  134. $this->resourceConnection->getConnection($resource)
  135. ->endSetup();
  136. }
  137. }
  138. /**
  139. * Check if during this operation we need to restore data
  140. *
  141. * @param OperationInterface $operation
  142. * @return bool
  143. */
  144. private function operationIsOppositeToDestructive(OperationInterface $operation)
  145. {
  146. return $operation instanceof AddColumn ||
  147. $operation instanceof CreateTable ||
  148. $operation instanceof ReCreateTable;
  149. }
  150. /**
  151. * Loop through all operations that are configured in di.xml
  152. * and execute them with elements from Diff.
  153. *
  154. * @see OperationInterface
  155. * @param DiffInterface $diff
  156. * @param array $requestData
  157. * @return void
  158. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  159. * @SuppressWarnings(PHPMD.NPathComplexity)
  160. */
  161. public function execute(DiffInterface $diff, array $requestData)
  162. {
  163. $this->startSetupForAllConnections();
  164. $tableHistories = $diff->getAll();
  165. $dryRun = isset($requestData[DryRunLogger::INPUT_KEY_DRY_RUN_MODE]) &&
  166. $requestData[DryRunLogger::INPUT_KEY_DRY_RUN_MODE];
  167. if ($dryRun) {
  168. $this->dryRunLogger->prepareToDryRun();
  169. }
  170. if (is_array($tableHistories)) {
  171. foreach ($tableHistories as $tableHistory) {
  172. $destructiveElements = [];
  173. $oppositeToDestructiveElements = [];
  174. $statementAggregator = $this->statementAggregatorFactory->create();
  175. foreach ($this->operations as $operation) {
  176. if (isset($tableHistory[$operation->getOperationName()])) {
  177. /** @var ElementHistory $elementHistory */
  178. foreach ($tableHistory[$operation->getOperationName()] as $elementHistory) {
  179. $statementAggregator->addStatements($operation->doOperation($elementHistory));
  180. if ($operation->isOperationDestructive()) {
  181. $destructiveElements[] = $elementHistory->getOld();
  182. } elseif ($this->operationIsOppositeToDestructive($operation)) {
  183. $oppositeToDestructiveElements[] = $elementHistory->getNew();
  184. }
  185. }
  186. }
  187. }
  188. $this->doDump($destructiveElements, $requestData);
  189. $this->dbSchemaWriter->compile($statementAggregator, $dryRun);
  190. $this->doRestore($oppositeToDestructiveElements, $requestData);
  191. }
  192. }
  193. $this->endSetupForAllConnections();
  194. }
  195. /**
  196. * Do restore of destructive operations
  197. *
  198. * @param array $elements
  199. * @param array $requestData
  200. */
  201. private function doRestore(array $elements, array $requestData)
  202. {
  203. $restoreMode = isset($requestData[self::KEY_DATA_RESTORE]) && $requestData[self::KEY_DATA_RESTORE];
  204. if ($restoreMode) {
  205. /**
  206. * @var ElementInterface $element
  207. */
  208. foreach ($elements as $element) {
  209. foreach ($this->dataSaviorsCollection as $dataSavior) {
  210. if ($dataSavior->isAcceptable($element)) {
  211. $dataSavior->restore($element);
  212. break;
  213. }
  214. }
  215. }
  216. }
  217. }
  218. /**
  219. * Do dump of destructive operations
  220. *
  221. * @param array $elements
  222. * @param array $requestData
  223. */
  224. private function doDump(array $elements, array $requestData)
  225. {
  226. $safeMode = isset($requestData[self::KEY_SAFE_MODE]) && $requestData[self::KEY_SAFE_MODE];
  227. if ($safeMode) {
  228. /**
  229. * @var ElementInterface $element
  230. */
  231. foreach ($elements as $element) {
  232. foreach ($this->dataSaviorsCollection as $dataSavior) {
  233. if ($dataSavior->isAcceptable($element)) {
  234. $dataSavior->dump($element);
  235. break;
  236. }
  237. }
  238. }
  239. }
  240. }
  241. }