Same filename in this branch
  1. 10 core/modules/pgsql/tests/src/Unit/SchemaTest.php
  2. 10 core/modules/mysql/tests/src/Kernel/mysql/SchemaTest.php
  3. 10 core/modules/pgsql/tests/src/Kernel/pgsql/SchemaTest.php
  4. 10 core/modules/sqlite/tests/src/Kernel/sqlite/SchemaTest.php
Same filename and directory in other branches
  1. 9 core/modules/mysql/tests/src/Kernel/mysql/SchemaTest.php

Namespace

Drupal\Tests\mysql\Kernel\mysql

File

core/modules/mysql/tests/src/Kernel/mysql/SchemaTest.php
View source
<?php

declare (strict_types=1);
namespace Drupal\Tests\mysql\Kernel\mysql;

use Drupal\Component\Utility\Unicode;
use Drupal\Core\Database\DatabaseExceptionWrapper;
use Drupal\Core\Database\Exception\SchemaTableColumnSizeTooLargeException;
use Drupal\Core\Database\Exception\SchemaTableKeyTooLargeException;
use Drupal\Core\Database\SchemaException;
use Drupal\Core\Database\SchemaObjectDoesNotExistException;
use Drupal\Core\Database\SchemaObjectExistsException;
use Drupal\KernelTests\Core\Database\DriverSpecificSchemaTestBase;

/**
 * Tests schema API for the MySQL driver.
 *
 * @group Database
 */
class SchemaTest extends DriverSpecificSchemaTestBase {

  /**
   * {@inheritdoc}
   */
  public function checkSchemaComment(string $description, string $table, string $column = NULL) : void {
    $comment = $this->schema
      ->getComment($table, $column);
    $max_length = $column ? 255 : 60;
    $description = Unicode::truncate($description, $max_length, TRUE, TRUE);
    $this
      ->assertSame($description, $comment, 'The comment matches the schema description.');
  }

  /**
   * {@inheritdoc}
   */
  protected function assertCollation() : void {

    // Make sure that varchar fields have the correct collations.
    $columns = $this->connection
      ->query('SHOW FULL COLUMNS FROM {test_table}');
    foreach ($columns as $column) {
      if ($column->Field == 'test_field_string') {
        $string_check = $column->Collation;
      }
      if ($column->Field == 'test_field_string_ascii') {
        $string_ascii_check = $column->Collation;
      }
    }
    $this
      ->assertMatchesRegularExpression('#^(utf8mb4_general_ci|utf8mb4_0900_ai_ci)$#', $string_check, 'test_field_string should have a utf8mb4_general_ci or a utf8mb4_0900_ai_ci collation, but it has not.');
    $this
      ->assertSame('ascii_general_ci', $string_ascii_check, 'test_field_string_ascii should have a ascii_general_ci collation, but it has not.');
  }

  /**
   * {@inheritdoc}
   */
  public function testTableWithSpecificDataType() : void {
    $table_specification = [
      'description' => 'Schema table description.',
      'fields' => [
        'timestamp' => [
          'mysql_type' => 'timestamp',
          'not null' => FALSE,
          'default' => NULL,
        ],
      ],
    ];
    $this->schema
      ->createTable('test_timestamp', $table_specification);
    $this
      ->assertTrue($this->schema
      ->tableExists('test_timestamp'));
  }

  /**
   * Tests that indexes on string fields are limited to 191 characters on MySQL.
   *
   * @see \Drupal\mysql\Driver\Database\mysql\Schema::getNormalizedIndexes()
   */
  public function testIndexLength() : void {
    $table_specification = [
      'fields' => [
        'id' => [
          'type' => 'int',
          'default' => NULL,
        ],
        'test_field_text' => [
          'type' => 'text',
          'not null' => TRUE,
        ],
        'test_field_string_long' => [
          'type' => 'varchar',
          'length' => 255,
          'not null' => TRUE,
        ],
        'test_field_string_ascii_long' => [
          'type' => 'varchar_ascii',
          'length' => 255,
        ],
        'test_field_string_short' => [
          'type' => 'varchar',
          'length' => 128,
          'not null' => TRUE,
        ],
      ],
      'indexes' => [
        'test_regular' => [
          'test_field_text',
          'test_field_string_long',
          'test_field_string_ascii_long',
          'test_field_string_short',
        ],
        'test_length' => [
          [
            'test_field_text',
            128,
          ],
          [
            'test_field_string_long',
            128,
          ],
          [
            'test_field_string_ascii_long',
            128,
          ],
          [
            'test_field_string_short',
            128,
          ],
        ],
        'test_mixed' => [
          [
            'test_field_text',
            200,
          ],
          'test_field_string_long',
          [
            'test_field_string_ascii_long',
            200,
          ],
          'test_field_string_short',
        ],
      ],
    ];
    $this->schema
      ->createTable('test_table_index_length', $table_specification);

    // Ensure expected exception thrown when adding index with missing info.
    $expected_exception_message = "MySQL needs the 'test_field_text' field specification in order to normalize the 'test_regular' index";
    $missing_field_spec = $table_specification;
    unset($missing_field_spec['fields']['test_field_text']);
    try {
      $this->schema
        ->addIndex('test_table_index_length', 'test_separate', [
        [
          'test_field_text',
          200,
        ],
      ], $missing_field_spec);
      $this
        ->fail('SchemaException not thrown when adding index with missing information.');
    } catch (SchemaException $e) {
      $this
        ->assertEquals($expected_exception_message, $e
        ->getMessage());
    }

    // Add a separate index.
    $this->schema
      ->addIndex('test_table_index_length', 'test_separate', [
      [
        'test_field_text',
        200,
      ],
    ], $table_specification);
    $table_specification_with_new_index = $table_specification;
    $table_specification_with_new_index['indexes']['test_separate'] = [
      [
        'test_field_text',
        200,
      ],
    ];

    // Ensure that the exceptions of addIndex are thrown as expected.
    try {
      $this->schema
        ->addIndex('test_table_index_length', 'test_separate', [
        [
          'test_field_text',
          200,
        ],
      ], $table_specification);
      $this
        ->fail('\\Drupal\\Core\\Database\\SchemaObjectExistsException exception missed.');
    } catch (SchemaObjectExistsException $e) {

      // Expected exception; just continue testing.
    }
    try {
      $this->schema
        ->addIndex('test_table_non_existing', 'test_separate', [
        [
          'test_field_text',
          200,
        ],
      ], $table_specification);
      $this
        ->fail('\\Drupal\\Core\\Database\\SchemaObjectDoesNotExistException exception missed.');
    } catch (SchemaObjectDoesNotExistException $e) {

      // Expected exception; just continue testing.
    }

    // Get index information.
    $results = $this->connection
      ->query('SHOW INDEX FROM {test_table_index_length}');
    $expected_lengths = [
      'test_regular' => [
        'test_field_text' => 191,
        'test_field_string_long' => 191,
        'test_field_string_ascii_long' => NULL,
        'test_field_string_short' => NULL,
      ],
      'test_length' => [
        'test_field_text' => 128,
        'test_field_string_long' => 128,
        'test_field_string_ascii_long' => 128,
        'test_field_string_short' => NULL,
      ],
      'test_mixed' => [
        'test_field_text' => 191,
        'test_field_string_long' => 191,
        'test_field_string_ascii_long' => 200,
        'test_field_string_short' => NULL,
      ],
      'test_separate' => [
        'test_field_text' => 191,
      ],
    ];

    // Count the number of columns defined in the indexes.
    $column_count = 0;
    foreach ($table_specification_with_new_index['indexes'] as $index) {
      foreach ($index as $field) {
        $column_count++;
      }
    }
    $test_count = 0;
    foreach ($results as $result) {
      $this
        ->assertEquals($expected_lengths[$result->Key_name][$result->Column_name], $result->Sub_part, 'Index length matches expected value.');
      $test_count++;
    }
    $this
      ->assertEquals($column_count, $test_count, 'Number of tests matches expected value.');
  }

  /**
   * @covers \Drupal\mysql\Driver\Database\mysql\Schema::introspectIndexSchema
   */
  public function testIntrospectIndexSchema() : void {
    $table_specification = [
      'fields' => [
        'id' => [
          'type' => 'int',
          'not null' => TRUE,
          'default' => 0,
        ],
        'test_field_1' => [
          'type' => 'int',
          'not null' => TRUE,
          'default' => 0,
        ],
        'test_field_2' => [
          'type' => 'int',
          'default' => 0,
        ],
        'test_field_3' => [
          'type' => 'int',
          'default' => 0,
        ],
        'test_field_4' => [
          'type' => 'int',
          'default' => 0,
        ],
        'test_field_5' => [
          'type' => 'int',
          'default' => 0,
        ],
      ],
      'primary key' => [
        'id',
        'test_field_1',
      ],
      'unique keys' => [
        'test_field_2' => [
          'test_field_2',
        ],
        'test_field_3_test_field_4' => [
          'test_field_3',
          'test_field_4',
        ],
      ],
      'indexes' => [
        'test_field_4' => [
          'test_field_4',
        ],
        'test_field_4_test_field_5' => [
          'test_field_4',
          'test_field_5',
        ],
      ],
    ];
    $table_name = strtolower($this
      ->getRandomGenerator()
      ->name());
    $this->schema
      ->createTable($table_name, $table_specification);
    unset($table_specification['fields']);
    $introspect_index_schema = new \ReflectionMethod(get_class($this->schema), 'introspectIndexSchema');
    $index_schema = $introspect_index_schema
      ->invoke($this->schema, $table_name);
    $this
      ->assertEquals($table_specification, $index_schema);
  }

  /**
   * Tests SchemaTableKeyTooLargeException.
   */
  public function testSchemaTableKeyTooLargeException() : void {
    $this
      ->expectException(SchemaTableKeyTooLargeException::class);
    $this->schema
      ->createTable('test_schema', [
      'description' => 'Tests SchemaTableKeyTooLargeException.',
      'fields' => [
        'id' => [
          'type' => 'varchar',
          'length' => 64,
          'not null' => TRUE,
        ],
        'id1' => [
          'type' => 'varchar',
          'length' => 255,
          'not null' => TRUE,
        ],
        'id2' => [
          'type' => 'varchar',
          'length' => 255,
          'not null' => TRUE,
        ],
        'id3' => [
          'type' => 'varchar',
          'length' => 255,
          'not null' => TRUE,
        ],
        'id4' => [
          'type' => 'varchar',
          'length' => 255,
          'not null' => TRUE,
        ],
        'id5' => [
          'type' => 'varchar',
          'length' => 255,
          'not null' => TRUE,
        ],
      ],
      'primary key' => [
        'id',
      ],
      'indexes' => [
        'key1' => [
          'id1',
          'id2',
          'id3',
          'id4',
          'id5',
        ],
      ],
    ]);
  }

  /**
   * Tests SchemaTableColumnSizeTooLargeException.
   */
  public function testSchemaTableColumnSizeTooLargeException() : void {
    $this
      ->expectException(SchemaTableColumnSizeTooLargeException::class);
    $this
      ->expectExceptionMessage("Column length too big for column 'too_large' (max = 16383); use BLOB or TEXT instead");
    $this->schema
      ->createTable('test_schema', [
      'description' => 'Tests SchemaTableColumnSizeTooLargeException.',
      'fields' => [
        'too_large' => [
          'type' => 'varchar',
          'length' => 65536,
          'not null' => TRUE,
        ],
      ],
    ]);
  }

  /**
   * Tests adding a primary key when sql_generate_invisible_primary_key is on.
   */
  public function testGeneratedInvisiblePrimaryKey() : void {
    $is_maria = method_exists($this->connection, 'isMariaDb') && $this->connection
      ->isMariaDb();
    if ($this->connection
      ->databaseType() !== 'mysql' || $is_maria || version_compare($this->connection
      ->version(), '8.0.30', '<')) {
      $this
        ->markTestSkipped('This test only runs on MySQL 8.0.30 and above');
    }
    try {
      $this->connection
        ->query("SET sql_generate_invisible_primary_key = 1;")
        ->execute();
    } catch (DatabaseExceptionWrapper $e) {
      $this
        ->markTestSkipped('This test requires the SESSION_VARIABLES_ADMIN privilege.');
    }
    $this->schema
      ->createTable('test_primary_key', [
      'fields' => [
        'foo' => [
          'type' => 'varchar',
          'length' => 1,
        ],
      ],
    ]);
    $this->schema
      ->addField('test_primary_key', 'id', [
      'type' => 'serial',
      'not null' => TRUE,
    ], [
      'primary key' => [
        'id',
      ],
    ]);
  }

}

Classes

Namesort descending Description
SchemaTest Tests schema API for the MySQL driver.