Skip to content

Commit

Permalink
Issue #3155563 by mcdruid, mrinalini9, Hardik_Patel_12, mondrake, daf…
Browse files Browse the repository at this point in the history
…fie: select query should quote aliases which are reserved words in MySQL
  • Loading branch information
alexpott committed Jul 16, 2020
1 parent 92f426f commit 786b5b3
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 3 deletions.
9 changes: 6 additions & 3 deletions lib/Drupal/Core/Database/Query/Select.php
Original file line number Diff line number Diff line change
Expand Up @@ -814,13 +814,16 @@ public function __toString() {
$fields = [];
foreach ($this->tables as $alias => $table) {
if (!empty($table['all_fields'])) {
$fields[] = $this->connection->escapeTable($alias) . '.*';
$fields[] = $this->connection->escapeAlias($alias) . '.*';
}
}
foreach ($this->fields as $field) {
// Note that $field['table'] holds the table alias.
// @see \Drupal\Core\Database\Query\Select::addField
$table = isset($field['table']) ? $this->connection->escapeAlias($field['table']) . '.' : '';
// Always use the AS keyword for field aliases, as some
// databases require it (e.g., PostgreSQL).
$fields[] = (isset($field['table']) ? $this->connection->escapeTable($field['table']) . '.' : '') . $this->connection->escapeField($field['field']) . ' AS ' . $this->connection->escapeAlias($field['alias']);
$fields[] = $table . $this->connection->escapeField($field['field']) . ' AS ' . $this->connection->escapeAlias($field['alias']);
}
foreach ($this->expressions as $expression) {
$fields[] = $expression['expression'] . ' AS ' . $this->connection->escapeAlias($expression['alias']);
Expand Down Expand Up @@ -852,7 +855,7 @@ public function __toString() {

// Don't use the AS keyword for table aliases, as some
// databases don't support it (e.g., Oracle).
$query .= $table_string . ' ' . $this->connection->escapeTable($table['alias']);
$query .= $table_string . ' ' . $this->connection->escapeAlias($table['alias']);

if (!empty($table['condition'])) {
$query .= ' ON ' . (string) $table['condition'];
Expand Down
16 changes: 16 additions & 0 deletions modules/system/tests/modules/database_test/database_test.install
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,22 @@ function database_test_schema() {
'primary key' => ['id'],
];

$schema['virtual'] = [
'description' => 'A simple table with reserved name in MySQL 8.',
'fields' => [
'id' => [
'description' => 'Simple unique ID.',
'type' => 'int',
'not null' => TRUE,
],
'function' => [
'description' => 'A column with reserved name in MySQL 8.',
'type' => 'text',
],
],
'primary key' => ['id'],
];

$schema['TEST_UPPERCASE'] = $schema['test'];

return $schema;
Expand Down
8 changes: 8 additions & 0 deletions tests/Drupal/KernelTests/Core/Database/DatabaseTestBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ protected function setUp() {
'test_serialized',
'test_special_columns',
'TEST_UPPERCASE',
'virtual',
]);
self::addSampleData();
}
Expand Down Expand Up @@ -164,6 +165,13 @@ public static function addSampleData() {
'function' => 'Function value 1',
])
->execute();

$connection->insert('virtual')
->fields([
'id' => 1,
'function' => 'Function value 1',
])
->execute();
}

}
83 changes: 83 additions & 0 deletions tests/Drupal/KernelTests/Core/Database/ReservedWordTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

namespace Drupal\KernelTests\Core\Database;

/**
* Tests queries that include reserved words.
*
* @group Database
*/
class ReservedWordTest extends DatabaseTestBase {

/**
* Tests SELECT count query from a table with a reserved name.
*/
public function testSelectReservedWordTableCount() {
$query = $this->connection->select('virtual');
$num_records = $query->countQuery()->execute()->fetchField();

$this->assertSame('1', $num_records);
}

/**
* Tests SELECT query with a specific field from a table with a reserved name.
*/
public function testSelectReservedWordTableSpecificField() {
$query = $this->connection->select('virtual');
$query->addField('virtual', 'function');
$rows = $query->execute()->fetchCol();

$this->assertSame('Function value 1', $rows[0]);
}

/**
* Tests SELECT query with all fields from a table with a reserved name.
*/
public function testSelectReservedWordTableAllFields() {
$query = $this->connection->select('virtual');
$query->fields('virtual');
$result = $query->execute()->fetchObject();

$this->assertSame('Function value 1', $result->function);
}

/**
* Tests SELECT count query from a table with a reserved alias.
*/
public function testSelectReservedWordAliasCount() {
$query = $this->connection->select('test', 'character');
$num_records = $query->countQuery()->execute()->fetchField();

$this->assertSame('4', $num_records);
}

/**
* Tests SELECT query with specific fields from a table with a reserved alias.
*/
public function testSelectReservedWordAliasSpecificFields() {
$query = $this->connection->select('test', 'high_priority');
$query->addField('high_priority', 'name');
$query->addField('high_priority', 'age', 'age');
$query->condition('age', 27);
$record = $query->execute()->fetchObject();

// Ensure that we got the right record.
$this->assertSame('George', $record->name);
$this->assertSame('27', $record->age);
}

/**
* Tests SELECT query with all fields from a table with a reserved alias.
*/
public function testSelectReservedWordAliasAllFields() {
$record = $this->connection->select('test', 'signal')
->fields('signal')
->condition('age', 27)
->execute()->fetchObject();

// Ensure that we got the right record.
$this->assertSame('George', $record->name);
$this->assertSame('27', $record->age);
}

}

0 comments on commit 786b5b3

Please sign in to comment.