diff --git a/includes/database/database.inc b/includes/database/database.inc
index 6879f69916..7deee78e7d 100644
--- a/includes/database/database.inc
+++ b/includes/database/database.inc
@@ -311,8 +311,6 @@ abstract class DatabaseConnection extends PDO {
   protected $escapedAliases = array();
 
   function __construct($dsn, $username, $password, $driver_options = array()) {
-    // Initialize and prepare the connection prefix.
-    $this->setPrefix(isset($this->connectionOptions['prefix']) ? $this->connectionOptions['prefix'] : '');
 
     // Because the other methods don't seem to work right.
     $driver_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
@@ -320,6 +318,9 @@ abstract class DatabaseConnection extends PDO {
     // Call PDO::__construct and PDO::setAttribute.
     parent::__construct($dsn, $username, $password, $driver_options);
 
+    // Initialize and prepare the connection prefix.
+    $this->setPrefix(isset($this->connectionOptions['prefix']) ? $this->connectionOptions['prefix'] : '');
+
     // Set a Statement class, unless the driver opted out.
     if (!empty($this->statementClass)) {
       $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array($this->statementClass, array($this)));
diff --git a/includes/database/mysql/database.inc b/includes/database/mysql/database.inc
index 356e039f73..208e17d770 100644
--- a/includes/database/mysql/database.inc
+++ b/includes/database/mysql/database.inc
@@ -12,6 +12,14 @@
 
 class DatabaseConnection_mysql extends DatabaseConnection {
 
+  /**
+   * Flag to indicate whether aliases, prefixes, and other names must be
+   * escaped for MYSQL keywords, which fail otherwise on MySQL 8+.
+   *
+   * @var boolean
+   */
+  protected $needsReservedKeywordEscape;
+
   /**
    * Flag to indicate if the cleanup function in __destruct() should run.
    *
@@ -19,6 +27,277 @@ class DatabaseConnection_mysql extends DatabaseConnection {
    */
   protected $needsCleanup = FALSE;
 
+  /**
+   * The list of MySQL reserved key words.
+   *
+   * @link https://dev.mysql.com/doc/refman/8.0/en/keywords.html
+   */
+  private $reservedKeyWords = array(
+    'accessible',
+    'add',
+    'admin',
+    'all',
+    'alter',
+    'analyze',
+    'and',
+    'as',
+    'asc',
+    'asensitive',
+    'before',
+    'between',
+    'bigint',
+    'binary',
+    'blob',
+    'both',
+    'by',
+    'call',
+    'cascade',
+    'case',
+    'change',
+    'char',
+    'character',
+    'check',
+    'collate',
+    'column',
+    'condition',
+    'constraint',
+    'continue',
+    'convert',
+    'create',
+    'cross',
+    'cube',
+    'cume_dist',
+    'current_date',
+    'current_time',
+    'current_timestamp',
+    'current_user',
+    'cursor',
+    'database',
+    'databases',
+    'day_hour',
+    'day_microsecond',
+    'day_minute',
+    'day_second',
+    'dec',
+    'decimal',
+    'declare',
+    'default',
+    'delayed',
+    'delete',
+    'dense_rank',
+    'desc',
+    'describe',
+    'deterministic',
+    'distinct',
+    'distinctrow',
+    'div',
+    'double',
+    'drop',
+    'dual',
+    'each',
+    'else',
+    'elseif',
+    'empty',
+    'enclosed',
+    'escaped',
+    'except',
+    'exists',
+    'exit',
+    'explain',
+    'false',
+    'fetch',
+    'first_value',
+    'float',
+    'float4',
+    'float8',
+    'for',
+    'force',
+    'foreign',
+    'from',
+    'fulltext',
+    'function',
+    'generated',
+    'get',
+    'grant',
+    'group',
+    'grouping',
+    'groups',
+    'having',
+    'high_priority',
+    'hour_microsecond',
+    'hour_minute',
+    'hour_second',
+    'if',
+    'ignore',
+    'in',
+    'index',
+    'infile',
+    'inner',
+    'inout',
+    'insensitive',
+    'insert',
+    'int',
+    'int1',
+    'int2',
+    'int3',
+    'int4',
+    'int8',
+    'integer',
+    'interval',
+    'into',
+    'io_after_gtids',
+    'io_before_gtids',
+    'is',
+    'iterate',
+    'join',
+    'json_table',
+    'key',
+    'keys',
+    'kill',
+    'lag',
+    'last_value',
+    'lead',
+    'leading',
+    'leave',
+    'left',
+    'like',
+    'limit',
+    'linear',
+    'lines',
+    'load',
+    'localtime',
+    'localtimestamp',
+    'lock',
+    'long',
+    'longblob',
+    'longtext',
+    'loop',
+    'low_priority',
+    'master_bind',
+    'master_ssl_verify_server_cert',
+    'match',
+    'maxvalue',
+    'mediumblob',
+    'mediumint',
+    'mediumtext',
+    'middleint',
+    'minute_microsecond',
+    'minute_second',
+    'mod',
+    'modifies',
+    'natural',
+    'not',
+    'no_write_to_binlog',
+    'nth_value',
+    'ntile',
+    'null',
+    'numeric',
+    'of',
+    'on',
+    'optimize',
+    'optimizer_costs',
+    'option',
+    'optionally',
+    'or',
+    'order',
+    'out',
+    'outer',
+    'outfile',
+    'over',
+    'partition',
+    'percent_rank',
+    'persist',
+    'persist_only',
+    'precision',
+    'primary',
+    'procedure',
+    'purge',
+    'range',
+    'rank',
+    'read',
+    'reads',
+    'read_write',
+    'real',
+    'recursive',
+    'references',
+    'regexp',
+    'release',
+    'rename',
+    'repeat',
+    'replace',
+    'require',
+    'resignal',
+    'restrict',
+    'return',
+    'revoke',
+    'right',
+    'rlike',
+    'row',
+    'rows',
+    'row_number',
+    'schema',
+    'schemas',
+    'second_microsecond',
+    'select',
+    'sensitive',
+    'separator',
+    'set',
+    'show',
+    'signal',
+    'smallint',
+    'spatial',
+    'specific',
+    'sql',
+    'sqlexception',
+    'sqlstate',
+    'sqlwarning',
+    'sql_big_result',
+    'sql_calc_found_rows',
+    'sql_small_result',
+    'ssl',
+    'starting',
+    'stored',
+    'straight_join',
+    'system',
+    'table',
+    'terminated',
+    'then',
+    'tinyblob',
+    'tinyint',
+    'tinytext',
+    'to',
+    'trailing',
+    'trigger',
+    'true',
+    'undo',
+    'union',
+    'unique',
+    'unlock',
+    'unsigned',
+    'update',
+    'usage',
+    'use',
+    'using',
+    'utc_date',
+    'utc_time',
+    'utc_timestamp',
+    'values',
+    'varbinary',
+    'varchar',
+    'varcharacter',
+    'varying',
+    'virtual',
+    'when',
+    'where',
+    'while',
+    'window',
+    'with',
+    'write',
+    'xor',
+    'year_month',
+    'zerofill',
+  );
+
   public function __construct(array $connection_options = array()) {
     // This driver defaults to transaction support, except if explicitly passed FALSE.
     $this->transactionSupport = !isset($connection_options['transactions']) || ($connection_options['transactions'] !== FALSE);
@@ -64,6 +343,7 @@ class DatabaseConnection_mysql extends DatabaseConnection {
     }
 
     parent::__construct($dsn, $connection_options['username'], $connection_options['password'], $connection_options['pdo']);
+    $mysql_server_version = $this->getAttribute(PDO::ATTR_SERVER_VERSION);
 
     // Force MySQL to use the UTF-8 character set. Also set the collation, if a
     // certain one has been set; otherwise, MySQL defaults to 'utf8_general_ci'
@@ -86,15 +366,102 @@ class DatabaseConnection_mysql extends DatabaseConnection {
     $connection_options += array(
       'init_commands' => array(),
     );
+
+    $sql_mode = 'REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO';
+    // NO_AUTO_CREATE_USER is removed in MySQL 8.0.11
+    // https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-11.html#mysqld-8-0-11-deprecation-removal
+    if (version_compare($mysql_server_version, '8.0.11', '<')) {
+      $sql_mode .= ',NO_AUTO_CREATE_USER';
+    }
     $connection_options['init_commands'] += array(
-      'sql_mode' => "SET sql_mode = 'REAL_AS_FLOAT,PIPES_AS_CONCAT,ANSI_QUOTES,IGNORE_SPACE,STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER'",
+      'sql_mode' => "SET sql_mode = '$sql_mode'",
     );
+
     // Execute initial commands.
     foreach ($connection_options['init_commands'] as $sql) {
       $this->exec($sql);
     }
   }
 
+  /**
+   * {@inheritdoc}}
+   */
+  protected function setPrefix($prefix) {
+    parent::setPrefix($prefix);
+    $this->needsReservedKeywordEscape = version_compare($this->getAttribute(PDO::ATTR_SERVER_VERSION), '8', '>=');
+    if ($this->needsReservedKeywordEscape) {
+      // MySQL 8 is more strict about the use of reserved keywords as table
+      // names. Therefore we surround all table names with back ticks (`).
+      foreach ($this->prefixSearch as $i => $prefixSearch) {
+        if (substr($prefixSearch, 0, 1) === '{') {
+          // If the prefix ends with a backtick, remove it.
+          if (substr($this->prefixReplace[$i], -1) === '`') {
+            $this->prefixReplace[$i] = substr($this->prefixReplace[$i], 0, -1);
+          }
+          else {
+            $this->prefixReplace[$i] = '`' . $this->prefixReplace[$i];
+          }
+        }
+        if (substr($prefixSearch, -1) === '}') {
+          $this->prefixReplace[$i] .= '`';
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function escapeField($field) {
+    $field = parent::escapeField($field);
+    return $this->quoteIdentifier($field);
+  }
+
+  public function escapeFields(array $fields) {
+    foreach ($fields as &$field) {
+      $field = $this->escapeField($field);
+    }
+    return $fields;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function escapeAlias($field) {
+    // Quote fields so that MySQL reserved words like 'function' can be used
+    // as aliases.
+    $field = parent::escapeAlias($field);
+    return $this->quoteIdentifier($field);
+  }
+
+  /**
+   * Quotes an identifier if it matches a MySQL reserved keyword.
+   *
+   * @param string $identifier
+   *   The field to check.
+   *
+   * @return string
+   *   The identifier, quoted if it matches a MySQL reserved keyword.
+   */
+  private function quoteIdentifier($identifier) {
+    if (!$this->needsReservedKeywordEscape) {
+      return $identifier;
+    }
+    // Quote identifiers so that MySQL reserved words like 'function' can be
+    // used as column names. Sometimes the 'table.column_name' format is passed
+    // in. For example,
+    // \Drupal\Core\Entity\Sql\SqlContentEntityStorage::buildQuery() adds a
+    // condition on "base.uid" while loading user entities.
+    if (strpos($identifier, '.') !== FALSE) {
+      list($table, $identifier) = explode('.', $identifier, 2);
+    }
+    if (in_array(strtolower($identifier), $this->reservedKeyWords, TRUE)) {
+      // Quote the string for MySQL reserved keywords.
+      $identifier = '"' . $identifier . '"';
+    }
+    return isset($table) ? $table . '.' . $identifier : $identifier;
+  }
+
   public function __destruct() {
     if ($this->needsCleanup) {
       $this->nextIdDelete();
@@ -119,6 +486,10 @@ class DatabaseConnection_mysql extends DatabaseConnection {
     return 'mysql';
   }
 
+  public function needsReservedKeywordEscape() {
+    return $this->needsReservedKeywordEscape;
+  }
+
   public function mapConditionOperator($operator) {
     // We don't want to override any of the defaults.
     return NULL;
diff --git a/includes/database/mysql/query.inc b/includes/database/mysql/query.inc
index d3d2d9eecf..b9221c7276 100644
--- a/includes/database/mysql/query.inc
+++ b/includes/database/mysql/query.inc
@@ -48,6 +48,10 @@ class InsertQuery_mysql extends InsertQuery {
     // Default fields are always placed first for consistency.
     $insert_fields = array_merge($this->defaultFields, $this->insertFields);
 
+    if (method_exists($this->connection, 'escapeFields')) {
+      $insert_fields = $this->connection->escapeFields($insert_fields);
+    }
+
     // If we're selecting from a SelectQuery, finish building the query and
     // pass it back, as any remaining options are irrelevant.
     if (!empty($this->fromQuery)) {
diff --git a/includes/database/mysql/schema.inc b/includes/database/mysql/schema.inc
index 9ba1c73397..049d865f16 100644
--- a/includes/database/mysql/schema.inc
+++ b/includes/database/mysql/schema.inc
@@ -57,6 +57,11 @@ class DatabaseSchema_mysql extends DatabaseSchema {
   protected function buildTableNameCondition($table_name, $operator = '=', $add_prefix = TRUE) {
     $info = $this->connection->getConnectionOptions();
 
+    if ($this->connection->needsReservedKeywordEscape()) {
+      // Ensure the table name has not been wrapped in backticks as that is not
+      // necessary or desirable for schema queries.
+      $table_name = str_replace('`', '', $table_name);
+    }
     $table_info = $this->getPrefixInfo($table_name, $add_prefix);
 
     $condition = new DatabaseCondition('AND');
@@ -494,11 +499,11 @@ class DatabaseSchema_mysql extends DatabaseSchema {
       $condition->condition('column_name', $column);
       $condition->compile($this->connection, $this);
       // Don't use {} around information_schema.columns table.
-      return $this->connection->query("SELECT column_comment FROM information_schema.columns WHERE " . (string) $condition, $condition->arguments())->fetchField();
+      return $this->connection->query("SELECT column_comment as column_comment FROM information_schema.columns WHERE " . (string) $condition, $condition->arguments())->fetchField();
     }
     $condition->compile($this->connection, $this);
     // Don't use {} around information_schema.tables table.
-    $comment = $this->connection->query("SELECT table_comment FROM information_schema.tables WHERE " . (string) $condition, $condition->arguments())->fetchField();
+    $comment = $this->connection->query("SELECT table_comment as table_comment FROM information_schema.tables WHERE " . (string) $condition, $condition->arguments())->fetchField();
     // Work-around for MySQL 5.0 bug http://bugs.mysql.com/bug.php?id=11379
     return preg_replace('/; InnoDB free:.*$/', '', $comment);
   }
diff --git a/includes/database/schema.inc b/includes/database/schema.inc
index 31862db394..1ce4bb2565 100644
--- a/includes/database/schema.inc
+++ b/includes/database/schema.inc
@@ -343,7 +343,7 @@ abstract class DatabaseSchema implements QueryPlaceholderInterface {
     // couldn't use db_select() here because it would prefix
     // information_schema.tables and the query would fail.
     // Don't use {} around information_schema.tables table.
-    return $this->connection->query("SELECT table_name FROM information_schema.tables WHERE " . (string) $condition, $condition->arguments())->fetchAllKeyed(0, 0);
+    return $this->connection->query("SELECT table_name as table_name FROM information_schema.tables WHERE " . (string) $condition, $condition->arguments())->fetchAllKeyed(0, 0);
   }
 
   /**
diff --git a/includes/database/select.inc b/includes/database/select.inc
index 8d84460e83..95639dab7a 100644
--- a/includes/database/select.inc
+++ b/includes/database/select.inc
@@ -1520,7 +1520,7 @@ class SelectQuery extends Query implements SelectQueryInterface {
     $fields = array();
     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 $alias => $field) {
@@ -1555,7 +1555,7 @@ class SelectQuery extends Query implements SelectQueryInterface {
 
       // 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 ' . $table['condition'];
diff --git a/modules/simpletest/tests/database_test.test b/modules/simpletest/tests/database_test.test
index 59d2e5d620..09bd11774b 100644
--- a/modules/simpletest/tests/database_test.test
+++ b/modules/simpletest/tests/database_test.test
@@ -3458,6 +3458,27 @@ class DatabaseQueryTestCase extends DatabaseTestCase {
     $this->assertFalse($result, 'SQL injection attempt did not result in a row being inserted in the database table.');
   }
 
+  /**
+   * Test if specifying the table along with the database like in "db.{table}"
+   * correctly applies table prefixes.
+   */
+  public function testTableWithDatabaseQuery() {
+    try {
+      $connection = Database::getConnection()->getConnectionOptions();
+      $database = $connection['database'];
+
+      // Test for {table} pattern
+      db_query('SELECT * FROM {test} LIMIT 1')->fetchObject();
+
+      // Test for database.{table} pattern
+      db_query('SELECT * FROM ' . $database . '.{test} LIMIT 1')->fetchObject();
+
+      $this->pass('Correctly apply prefixes if the table is specified along with the database.');
+    }
+    catch (PDOException $e) {
+      $this->fail('Correctly apply prefixes if the table is specified along with the database.');
+    }
+  }
 }
 
 /**
