Commit f0c7bb83 authored by catch's avatar catch
Browse files

Issue #3218978 by effulgentsia, daffie, mcdruid, Wim Leers: MySQL driver...

Issue #3218978 by effulgentsia, daffie, mcdruid, Wim Leers: MySQL driver allows settings.php to remove ANSI_QUOTES from sql_mode, but doesn't work when it is

(cherry picked from commit cff1307c)
parent 2bf41216
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -89,6 +89,35 @@ class Connection extends DatabaseConnection {
   */
  protected $identifierQuotes = ['"', '"'];

  /**
   * {@inheritdoc}
   */
  public function __construct(\PDO $connection, array $connection_options) {
    // If the SQL mode doesn't include 'ANSI_QUOTES' (explicitly or via a
    // combination mode), then MySQL doesn't interpret a double quote as an
    // identifier quote, in which case use the non-ANSI-standard backtick.
    //
    // Because we still support MySQL 5.7, check for the deprecated combination
    // modes as well.
    //
    // @see https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html#sqlmode_ansi_quotes
    $ansi_quotes_modes = ['ANSI_QUOTES', 'ANSI', 'DB2', 'MAXDB', 'MSSQL', 'ORACLE', 'POSTGRESQL'];
    $is_ansi_quotes_mode = FALSE;
    foreach ($ansi_quotes_modes as $mode) {
      // None of the modes in $ansi_quotes_modes are substrings of other modes
      // that are not in $ansi_quotes_modes, so a simple stripos() does not
      // return false positives.
      if (stripos($connection_options['init_commands']['sql_mode'], $mode) !== FALSE) {
        $is_ansi_quotes_mode = TRUE;
        break;
      }
    }
    if ($this->identifierQuotes === ['"', '"'] && !$is_ansi_quotes_mode) {
      $this->identifierQuotes = ['`', '`'];
    }
    parent::__construct($connection, $connection_options);
  }

  /**
   * {@inheritdoc}
   */
+50 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\KernelTests\Core\Database;

/**
 * Tests compatibility of the MySQL driver with various sql_mode options.
 *
 * @group Database
 */
class SqlModeTest extends DatabaseTestBase {

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    if ($this->connection->databaseType() !== 'mysql') {
      $this->markTestSkipped("Skipping test since sql_mode is a MySQL-only feature.");
    }
  }

  /**
   * Tests quoting identifiers in queries.
   */
  public function testQuotingIdentifiers() {
    // Use SQL-reserved words for both the table and column names.
    $query = $this->connection->query('SELECT [update] FROM {select}');
    $this->assertEquals('Update value 1', $query->fetchObject()->update);
    $this->assertStringContainsString('SELECT `update` FROM `', $query->getQueryString());
  }

  /**
   * {@inheritdoc}
   */
  protected function getDatabaseConnectionInfo() {
    $info = parent::getDatabaseConnectionInfo();

    // This runs during setUp(), so is not yet skipped for non MySQL databases.
    // We defer skipping the test to later in setUp(), so that that can be
    // based on databaseType() rather than 'driver', but here all we have to go
    // on is 'driver'.
    if ($info['default']['driver'] === 'mysql') {
      $info['default']['init_commands']['sql_mode'] = "SET sql_mode = ''";
    }

    return $info;
  }

}