diff --git a/core/lib/Drupal/Core/Database/Query/Select.php b/core/lib/Drupal/Core/Database/Query/Select.php
index 3be5a2f..4966744 100644
--- a/core/lib/Drupal/Core/Database/Query/Select.php
+++ b/core/lib/Drupal/Core/Database/Query/Select.php
@@ -44,7 +44,7 @@ class Select extends Query implements SelectInterface {
    *   'type' => $join_type (one of INNER, LEFT OUTER, RIGHT OUTER),
    *   'table' => $table,
    *   'alias' => $alias_of_the_table,
-   *   'condition' => $condition_clause_on_which_to_join,
+   *   'condition' => $join_condition (string or Condition object),
    *   'arguments' => $array_of_arguments_for_placeholders_in_the condition.
    *   'all_fields' => TRUE to SELECT $alias.*, FALSE or NULL otherwise.
    * )
@@ -52,6 +52,10 @@ class Select extends Query implements SelectInterface {
    * If $table is a string, it is taken as the name of a table. If it is
    * a Select query object, it is taken as a subquery.
    *
+   * If $join_condition is a Condition object, any arguments should be
+   * incorporated into the object; a separate array of arguments does not
+   * need to be provided.
+   *
    * @var array
    */
   protected $tables = array();
@@ -201,6 +205,10 @@ public function arguments() {
       if ($table['table'] instanceof SelectInterface) {
         $args += $table['table']->arguments();
       }
+      // If the join condition is an object, grab its arguments recursively.
+      if (!empty($table['condition']) && $table['condition'] instanceof ConditionInterface) {
+        $args += $table['condition']->arguments();
+      }
     }
 
     foreach ($this->expressions as $expression) {
@@ -230,6 +238,10 @@ public function compile(Connection $connection, PlaceholderInterface $queryPlace
       if ($table['table'] instanceof SelectInterface) {
         $table['table']->compile($connection, $queryPlaceholder);
       }
+      // Make sure join conditions are also compiled.
+      if (!empty($table['condition']) && $table['condition'] instanceof ConditionInterface) {
+        $table['condition']->compile($connection, $queryPlaceholder);
+      }
     }
 
     // If there are any dependent queries to UNION, compile it recursively.
@@ -253,6 +265,11 @@ public function compiled() {
           return FALSE;
         }
       }
+      if (!empty($table['condition']) && $table['condition'] instanceof ConditionInterface) {
+        if (!$table['condition']->compiled()) {
+          return FALSE;
+        }
+      }
     }
 
     foreach ($this->union as $union) {
@@ -827,7 +844,7 @@ public function __toString() {
       $query .=  $table_string . ' ' . $this->connection->escapeTable($table['alias']);
 
       if (!empty($table['condition'])) {
-        $query .= ' ON ' . $table['condition'];
+        $query .= ' ON ' . (string) $table['condition'];
       }
     }
 
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 91d27e8..4bd7862 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -1044,7 +1044,7 @@ function node_query_node_access_alter(AlterableInterface $query) {
     return;
   }
 
-  $tables = $query->getTables();
+  $tables = &$query->getTables();
   $base_table = $query->getMetaData('base_table');
   // If the base table is not given, default to node if present.
   if (!$base_table) {
diff --git a/core/modules/node/src/NodeGrantDatabaseStorage.php b/core/modules/node/src/NodeGrantDatabaseStorage.php
index 6b6f280..a7b2ee7 100644
--- a/core/modules/node/src/NodeGrantDatabaseStorage.php
+++ b/core/modules/node/src/NodeGrantDatabaseStorage.php
@@ -190,7 +190,24 @@ public function alterQuery($query, array $tables, $op, AccountInterface $account
         // Now handle entities.
         $subquery->where("$nalias.$field = na.nid");
 
-        $query->exists($subquery);
+        if (empty($tableinfo['join type'])) {
+          $query->exists($subquery);
+        }
+        else {
+          // If it's a join, add the node access check to the join condition.
+          $join_cond = $query
+            ->andConditionGroup()
+            ->exists($subquery);
+          // Add the existing join conditions into the Condition object.
+          if ($tables[$nalias]['condition'] instanceof ConditionInterface) {
+            $join_cond->condition(tables[$nalias]['condition']);
+          }
+          else {
+            $join_cond->where($tables[$nalias]['condition'], $tables[$nalias]['arguments']);
+            $tables[$nalias]['arguments'] = array();
+          }
+          $tables[$nalias]['condition'] = $join_cond;
+        }
       }
     }
   }
diff --git a/core/modules/node/src/NodeGrantDatabaseStorageInterface.php b/core/modules/node/src/NodeGrantDatabaseStorageInterface.php
index c59f941..3cf8b7c 100644
--- a/core/modules/node/src/NodeGrantDatabaseStorageInterface.php
+++ b/core/modules/node/src/NodeGrantDatabaseStorageInterface.php
@@ -50,7 +50,7 @@ public function checkAll(AccountInterface $account);
    * @return int
    *   Status of the access check.
    */
-  public function alterQuery($query, array $tables, $op, AccountInterface $account, $base_table);
+  public function alterQuery($query, array &$tables, $op, AccountInterface $account, $base_table);
 
   /**
    * Writes a list of grants to the database, deleting previously saved ones.
diff --git a/core/modules/node/tests/modules/node_access_test/node_access_test.module b/core/modules/node/tests/modules/node_access_test/node_access_test.module
index e195ea8..1338eac 100644
--- a/core/modules/node/tests/modules/node_access_test/node_access_test.module
+++ b/core/modules/node/tests/modules/node_access_test/node_access_test.module
@@ -78,7 +78,7 @@ function node_access_test_node_grants($account, $op) {
 function node_access_test_node_access_records(NodeInterface $node) {
   $grants = array();
   // For NodeAccessBaseTableTestCase, only set records for private nodes.
-  if (!\Drupal::state()->get('node_access_test.private') || $node->private->value) {
+  if (!\Drupal::state()->get('node_access_test.private') || (isset($node->private) && $node->private->value)) {
     // Groups 8888 and 8889 for the node_access_test realm both receive a view
     // grant for all controlled nodes. See node_access_test_node_grants().
     $grants[] = array(
