77namespace Magento \Framework \Setup \Declaration \Schema \Db \MySQL ;
88
99use Magento \Framework \App \ResourceConnection ;
10+ use Magento \Framework \DB \Adapter \ConnectionException ;
11+ use Magento \Framework \DB \Adapter \SqlVersionProvider ;
12+ use Magento \Framework \DB \Adapter \AdapterInterface ;
1013use Magento \Framework \Setup \Declaration \Schema \Db \DbSchemaWriterInterface ;
1114use Magento \Framework \Setup \Declaration \Schema \Db \Statement ;
1215use Magento \Framework \Setup \Declaration \Schema \Db \StatementAggregator ;
@@ -59,22 +62,30 @@ class DbSchemaWriter implements DbSchemaWriterInterface
5962 */
6063 private $ dryRunLogger ;
6164
65+ /**
66+ * @var SqlVersionProvider
67+ */
68+ private $ sqlVersionProvider ;
69+
6270 /**
6371 * @param ResourceConnection $resourceConnection
64- * @param StatementFactory $statementFactory
65- * @param DryRunLogger $dryRunLogger
66- * @param array $tableOptions
72+ * @param StatementFactory $statementFactory
73+ * @param DryRunLogger $dryRunLogger
74+ * @param SqlVersionProvider $sqlVersionProvider
75+ * @param array $tableOptions
6776 */
6877 public function __construct (
6978 ResourceConnection $ resourceConnection ,
7079 StatementFactory $ statementFactory ,
7180 DryRunLogger $ dryRunLogger ,
81+ SqlVersionProvider $ sqlVersionProvider ,
7282 array $ tableOptions = []
7383 ) {
7484 $ this ->resourceConnection = $ resourceConnection ;
7585 $ this ->statementFactory = $ statementFactory ;
7686 $ this ->dryRunLogger = $ dryRunLogger ;
7787 $ this ->tableOptions = array_replace ($ this ->tableOptions , $ tableOptions );
88+ $ this ->sqlVersionProvider = $ sqlVersionProvider ;
7889 }
7990
8091 /**
@@ -271,15 +282,14 @@ public function compile(StatementAggregator $statementAggregator, $dryRun)
271282 $ statementsSql = [];
272283 $ statement = null ;
273284
274- /**
275- * @var Statement $statement
276- */
277- foreach ($ statementBank as $ statement ) {
278- $ statementsSql [] = $ statement ->getStatement ();
279- }
280- $ adapter = $ this ->resourceConnection ->getConnection ($ statement ->getResource ());
281-
282285 if ($ dryRun ) {
286+ /**
287+ * @var Statement $statement
288+ */
289+ foreach ($ statementBank as $ statement ) {
290+ $ statementsSql [] = $ statement ->getStatement ();
291+ }
292+ $ adapter = $ this ->resourceConnection ->getConnection ($ statement ->getResource ());
283293 $ this ->dryRunLogger ->log (
284294 sprintf (
285295 $ this ->statementDirectives [$ statement ->getType ()],
@@ -288,18 +298,81 @@ public function compile(StatementAggregator $statementAggregator, $dryRun)
288298 )
289299 );
290300 } else {
301+ $ this ->doQuery ($ statementBank );
302+ $ statement = end ($ statementBank );
303+ //Do post update, like SQL DML operations or etc...
304+ foreach ($ statement ->getTriggers () as $ trigger ) {
305+ call_user_func ($ trigger );
306+ }
307+ }
308+ }
309+ }
310+
311+ /**
312+ * Check if we can concatenate sql into one statement
313+ *
314+ * Due to issues with some versions of MariaBD such statements
315+ * may produce errors, e.g. with foreign key definition with column modification
316+ *
317+ * @return bool
318+ * @throws ConnectionException
319+ */
320+ private function isNeedToSplitSql () : bool
321+ {
322+ return str_contains ($ this ->sqlVersionProvider ->getSqlVersion (), SqlVersionProvider::MARIA_DB_10_4_VERSION ) ||
323+ str_contains ($ this ->sqlVersionProvider ->getSqlVersion (), SqlVersionProvider::MARIA_DB_10_6_VERSION );
324+ }
325+
326+ /**
327+ * Perform queries based on statements
328+ *
329+ * @param Statement[] $statementBank
330+ * @return void
331+ * @throws ConnectionException
332+ */
333+ private function doQuery (
334+ array $ statementBank
335+ ) : void {
336+ if (empty ($ statementBank )) {
337+ return ;
338+ }
339+
340+ $ statement = null ;
341+ $ statementsSql = [];
342+ foreach ($ statementBank as $ statement ) {
343+ $ statementsSql [] = $ statement ->getStatement ();
344+ }
345+ $ adapter = $ this ->resourceConnection ->getConnection ($ statement ->getResource ());
346+
347+ if ($ this ->isNeedToSplitSql ()) {
348+ $ preparedStatements = $ this ->getPreparedStatements ($ statementBank );
349+
350+ if (!empty ($ preparedStatements ['canBeCombinedStatements ' ])) {
291351 $ adapter ->query (
292352 sprintf (
293353 $ this ->statementDirectives [$ statement ->getType ()],
294354 $ adapter ->quoteIdentifier ($ statement ->getTableName ()),
295- implode (", " , $ statementsSql )
355+ implode (", " , $ preparedStatements ['canBeCombinedStatements ' ])
356+ )
357+ );
358+ }
359+ foreach ($ preparedStatements ['separatedStatements ' ] as $ separatedStatement ) {
360+ $ adapter ->query (
361+ sprintf (
362+ $ this ->statementDirectives [$ statement ->getType ()],
363+ $ adapter ->quoteIdentifier ($ statement ->getTableName ()),
364+ $ separatedStatement
296365 )
297366 );
298- //Do post update, like SQL DML operations or etc...
299- foreach ($ statement ->getTriggers () as $ trigger ) {
300- call_user_func ($ trigger );
301- }
302367 }
368+ } else {
369+ $ adapter ->query (
370+ sprintf (
371+ $ this ->statementDirectives [$ statement ->getType ()],
372+ $ adapter ->quoteIdentifier ($ statement ->getTableName ()),
373+ implode (", " , $ statementsSql )
374+ )
375+ );
303376 }
304377 }
305378
@@ -309,6 +382,7 @@ public function compile(StatementAggregator $statementAggregator, $dryRun)
309382 * @param string $tableName
310383 * @param string $resource
311384 * @return int
385+ * @throws \Zend_Db_Statement_Exception
312386 */
313387 private function getNextAutoIncrementValue (string $ tableName , string $ resource ): int
314388 {
@@ -323,6 +397,59 @@ private function getNextAutoIncrementValue(string $tableName, string $resource):
323397 } else {
324398 return 1 ;
325399 }
400+ }
326401
402+ /**
403+ * Prepare list of modified columns from statement
404+ *
405+ * @param array $statementBank
406+ * @return array
407+ */
408+ private function getModifiedColumns (array $ statementBank ) : array
409+ {
410+ $ columns = [];
411+ foreach ($ statementBank as $ statement ) {
412+ if ($ statement ->getType () === 'alter '
413+ && str_contains ($ statement ->getStatement (), 'MODIFY COLUMN ' )) {
414+ $ columns [] = $ statement ->getName ();
415+ }
416+ }
417+ return $ columns ;
418+ }
419+
420+ /**
421+ * Separate statements that can't be executed as one statement
422+ *
423+ * @param array $statementBank
424+ * @return array
425+ */
426+ private function getPreparedStatements (array $ statementBank ) : array
427+ {
428+ $ statementsSql = [];
429+ foreach ($ statementBank as $ statement ) {
430+ $ statementsSql [] = $ statement ->getStatement ();
431+ }
432+ $ result = ['separatedStatements ' => [], 'canBeCombinedStatements ' => []];
433+ $ modifiedColumns = $ this ->getModifiedColumns ($ statementBank );
434+
435+ foreach ($ statementsSql as $ statementSql ) {
436+ if (str_contains ($ statementSql , 'FOREIGN KEY ' )) {
437+ $ isThisColumnModified = false ;
438+ foreach ($ modifiedColumns as $ modifiedColumn ) {
439+ if (str_contains ($ statementSql , '` ' . $ modifiedColumn . '` ' )) {
440+ $ isThisColumnModified = true ;
441+ break ;
442+ }
443+ }
444+ if ($ isThisColumnModified ) {
445+ $ result ['separatedStatements ' ][] = $ statementSql ;
446+ } else {
447+ $ result ['canBeCombinedStatements ' ][] = $ statementSql ;
448+ }
449+ } else {
450+ $ result ['canBeCombinedStatements ' ][] = $ statementSql ;
451+ }
452+ }
453+ return $ result ;
327454 }
328455}
0 commit comments