diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Condition.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Condition.php
new file mode 100644
index 0000000000..d7a77ec2c0
--- /dev/null
+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Condition.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\Core\Database\Driver\sqlite;
+
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Database\Query\PlaceholderInterface;
+use Drupal\Core\Database\Query\Condition as QueryCondition;
+
+/**
+ * SQLite implementation of \Drupal\Core\Database\Query\Condition.
+ */
+class Condition extends QueryCondition {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function compileValueList(array $values, Connection $connection, PlaceholderInterface $queryPlaceholder) {
+    // SQLite has a default limit of 999 placeholders. Therefore, pass large
+    // value lists as a single json-encoded placeholder. Because a query might
+    // contain multiple conditions, treat any individual condition's value list
+    // as large when it's over 50.
+    if (count($values) > 50) {
+      $value_fragment = [];
+      $arguments = [];
+      $json_value = json_encode($values, 0 ,1);
+      $placeholder = ':db_condition_placeholder_' . $queryPlaceholder->nextPlaceholder();
+      $value_fragment[] = "select value from json_each($placeholder)";
+      $arguments[$placeholder] = $json_value;
+      return [$value_fragment, $arguments];
+    }
+    else {
+      return parent::compileValueList($values, $connection, $queryPlaceholder);
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Delete.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Delete.php
index c009fb8105..c7d884619b 100644
--- a/core/lib/Drupal/Core/Database/Driver/sqlite/Delete.php
+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Delete.php
@@ -7,4 +7,6 @@
 /**
  * SQLite implementation of \Drupal\Core\Database\Query\Delete.
  */
-class Delete extends QueryDelete {}
+class Delete extends QueryDelete {
+  use QueryConditionTrait;
+}
diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Merge.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Merge.php
index fddee0053b..8b841895e6 100644
--- a/core/lib/Drupal/Core/Database/Driver/sqlite/Merge.php
+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Merge.php
@@ -7,4 +7,6 @@
 /**
  * SQLite implementation of \Drupal\Core\Database\Query\Merge.
  */
-class Merge extends QueryMerge {}
+class Merge extends QueryMerge {
+  use QueryConditionTrait;
+}
diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/QueryConditionTrait.php b/core/lib/Drupal/Core/Database/Driver/sqlite/QueryConditionTrait.php
new file mode 100644
index 0000000000..bc661cfdff
--- /dev/null
+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/QueryConditionTrait.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Drupal\Core\Database\Driver\sqlite;
+
+/**
+ * Overrides the condition factory to use the driver-specific Condition class.
+ */
+trait QueryConditionTrait {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function conditionGroupFactory($conjunction = 'AND') {
+    return new Condition($conjunction);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Select.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Select.php
index 5eaa5157d8..095d731851 100644
--- a/core/lib/Drupal/Core/Database/Driver/sqlite/Select.php
+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Select.php
@@ -9,6 +9,8 @@
  */
 class Select extends QuerySelect {
 
+  use QueryConditionTrait;
+
   public function forUpdate($set = TRUE) {
     // SQLite does not support FOR UPDATE so nothing to do.
     return $this;
diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Update.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Update.php
index 66a7294d49..afc7418095 100644
--- a/core/lib/Drupal/Core/Database/Driver/sqlite/Update.php
+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Update.php
@@ -7,4 +7,6 @@
 /**
  * SQLite implementation of \Drupal\Core\Database\Query\Update.
  */
-class Update extends QueryUpdate {}
+class Update extends QueryUpdate {
+  use QueryConditionTrait;
+}
diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Upsert.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Upsert.php
index fc59afde25..3da69f02d6 100644
--- a/core/lib/Drupal/Core/Database/Driver/sqlite/Upsert.php
+++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Upsert.php
@@ -27,4 +27,32 @@ public function __toString() {
     return $query;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function execute() {
+    // SQLite has a default limit of 999 placeholders, so upsert in batches
+    // less than that.
+    $remaining_insert_values = $all_insert_values = $this->insertValues;
+    while (count($remaining_insert_values)) {
+      $this->insertValues = [];
+      $num_placeholders = 0;
+      while (count($remaining_insert_values)) {
+        $next_insert_value = array_shift($remaining_insert_values);
+        if ($num_placeholders + count($next_insert_value) > 999) {
+          array_unshift($remaining_insert_values, $next_insert_value);
+          break;
+        }
+        $this->insertValues[] = $next_insert_value;
+        $num_placeholders += count($next_insert_value);
+      }
+      if (count($this->insertValues)) {
+        parent::execute();
+      }
+    }
+
+    $this->insertValues = $all_insert_values;
+    return TRUE;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Database/Query/Condition.php b/core/lib/Drupal/Core/Database/Query/Condition.php
index e7038e23c9..ea2d3e3cd1 100644
--- a/core/lib/Drupal/Core/Database/Query/Condition.php
+++ b/core/lib/Drupal/Core/Database/Query/Condition.php
@@ -291,24 +291,9 @@ public function compile(Connection $connection, PlaceholderInterface $queryPlace
             $condition['value'] = [$condition['value']];
           }
           // Process all individual values.
-          $value_fragment = [];
-          foreach ($condition['value'] as $value) {
-            if ($value instanceof SelectInterface) {
-              // Right hand part is a subquery. Compile, put brackets around it
-              // and collect any arguments.
-              $value->compile($connection, $queryPlaceholder);
-              $value_fragment[] = '(' . (string) $value . ')';
-              $arguments += $value->arguments();
-            }
-            else {
-              // Right hand part is a normal value. Replace the value with a
-              // placeholder and add the value as an argument.
-              $placeholder = ':db_condition_placeholder_' . $queryPlaceholder->nextPlaceholder();
-              $value_fragment[] = $placeholder;
-              $arguments[$placeholder] = $value;
-            }
-          }
+          list ($value_fragment, $fragment_arguments) = $this->compileValueList($condition['value'], $connection, $queryPlaceholder);
           $value_fragment = $operator['prefix'] . implode($operator['delimiter'], $value_fragment) . $operator['postfix'];
+          $arguments += $fragment_arguments;
         }
 
         // Concatenate the left hand part, operator and right hand part.
@@ -323,6 +308,46 @@ public function compile(Connection $connection, PlaceholderInterface $queryPlace
     }
   }
 
+  /**
+   * Compiles the values (right side of operator) of a conditional.
+   *
+   * @param $values
+   *   The values to compile.
+   * @param $connection
+   *   The database connection for which to compile the conditionals.
+   * @param $queryPlaceholder
+   *   The query this condition belongs to. If not given, the current query is
+   *   used.
+   *
+   * @return
+   *   A two item array:
+   *   - The first item is an array of SQL fragments for the list, containing
+   *     placeholders for values as needed.
+   *   - The second item is an associative array mapping each placeholder name
+   *     to a value.
+   */
+  protected function compileValueList(array $values, Connection $connection, PlaceholderInterface $queryPlaceholder) {
+    $value_fragment = [];
+    $arguments = [];
+    foreach ($values as $value) {
+      if ($value instanceof SelectInterface) {
+        // Right hand part is a subquery. Compile, put brackets around it
+        // and collect any arguments.
+        $value->compile($connection, $queryPlaceholder);
+        $value_fragment[] = '(' . (string) $value . ')';
+        $arguments += $value->arguments();
+      }
+      else {
+        // Right hand part is a normal value. Replace the value with a
+        // placeholder and add the value as an argument.
+        $placeholder = ':db_condition_placeholder_' . $queryPlaceholder->nextPlaceholder();
+        $value_fragment[] = $placeholder;
+        $arguments[$placeholder] = $value;
+      }
+    }
+    return [$value_fragment, $arguments];
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/lib/Drupal/Core/Database/Query/Delete.php b/core/lib/Drupal/Core/Database/Query/Delete.php
index def88385e0..03674670b1 100644
--- a/core/lib/Drupal/Core/Database/Query/Delete.php
+++ b/core/lib/Drupal/Core/Database/Query/Delete.php
@@ -36,7 +36,7 @@ public function __construct(Connection $connection, $table, array $options = [])
     parent::__construct($connection, $options);
     $this->table = $table;
 
-    $this->condition = new Condition('AND');
+    $this->condition = $this->conditionGroupFactory();
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Database/Query/Merge.php b/core/lib/Drupal/Core/Database/Query/Merge.php
index 6d89532472..3141e38c66 100644
--- a/core/lib/Drupal/Core/Database/Query/Merge.php
+++ b/core/lib/Drupal/Core/Database/Query/Merge.php
@@ -138,7 +138,7 @@ public function __construct(Connection $connection, $table, array $options = [])
     parent::__construct($connection, $options);
     $this->table = $table;
     $this->conditionTable = $table;
-    $this->condition = new Condition('AND');
+    $this->condition = $this->conditionGroupFactory();
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Database/Query/Select.php b/core/lib/Drupal/Core/Database/Query/Select.php
index 27892ce5cb..3027bbe3d9 100644
--- a/core/lib/Drupal/Core/Database/Query/Select.php
+++ b/core/lib/Drupal/Core/Database/Query/Select.php
@@ -134,8 +134,8 @@ public function __construct(Connection $connection, $table, $alias = NULL, $opti
     $options['return'] = Database::RETURN_STATEMENT;
     parent::__construct($connection, $options);
     $conjunction = isset($options['conjunction']) ? $options['conjunction'] : 'AND';
-    $this->condition = new Condition($conjunction);
-    $this->having = new Condition($conjunction);
+    $this->condition = $this->conditionGroupFactory($conjunction);
+    $this->having = $this->conditionGroupFactory($conjunction);
     $this->addJoin(NULL, $table, $alias);
   }
 
diff --git a/core/lib/Drupal/Core/Database/Query/Update.php b/core/lib/Drupal/Core/Database/Query/Update.php
index 5de5d682ec..b4bc8a30e5 100644
--- a/core/lib/Drupal/Core/Database/Query/Update.php
+++ b/core/lib/Drupal/Core/Database/Query/Update.php
@@ -65,7 +65,7 @@ public function __construct(Connection $connection, $table, array $options = [])
     parent::__construct($connection, $options);
     $this->table = $table;
 
-    $this->condition = new Condition('AND');
+    $this->condition = $this->conditionGroupFactory();
   }
 
   /**
diff --git a/core/tests/Drupal/KernelTests/Core/Database/InsertTest.php b/core/tests/Drupal/KernelTests/Core/Database/InsertTest.php
index 9f52a15266..b2cc301c36 100644
--- a/core/tests/Drupal/KernelTests/Core/Database/InsertTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Database/InsertTest.php
@@ -70,6 +70,42 @@ public function testMultiInsert() {
     $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.');
   }
 
+
+  /**
+   * Tests that we can insert 1000 records in one query object.
+   *
+   * This test is motivated by SQLite's default limit of 999 placeholders, but
+   * it's good to ensure that all database drivers can handle large inserts.
+   *
+   * @link https://www.sqlite.org/limits.html#max_variable_number
+   */
+  public function testLargeMultiInsert() {
+    $num_records_before = (int) $this->connection->query('SELECT COUNT(*) FROM {test}')->fetchField();
+
+    $query = $this->connection->insert('test');
+    $query->fields(['name', 'age']);
+
+    for ($i=0; $i<1000; $i++) {
+      $values = [
+        'name' => "Name $i",
+        'age' => $i,
+      ];
+      $query->values($values);
+    }
+
+    // Check how many records are queued for insertion.
+    $this->assertIdentical($query->count(), 1000, '1000 records are queued for insertion.');
+
+    $query->execute();
+
+    $num_records_after = (int) $this->connection->query('SELECT COUNT(*) FROM {test}')->fetchField();
+    $this->assertSame($num_records_before + 1000, $num_records_after, 'Record inserts correctly.');
+    $saved_age = $this->connection->query('SELECT age FROM {test} WHERE name = :name', [':name' => 'Name 0'])->fetchField();
+    $this->assertIdentical($saved_age, '0', 'Can retrieve after inserting.');
+    $saved_age = $this->connection->query('SELECT age FROM {test} WHERE name = :name', [':name' => 'Name 999'])->fetchField();
+    $this->assertIdentical($saved_age, '999', 'Can retrieve after inserting.');
+  }
+
   /**
    * Tests that an insert object can be reused with new data after it executes.
    */
diff --git a/core/tests/Drupal/KernelTests/Core/Database/SelectTest.php b/core/tests/Drupal/KernelTests/Core/Database/SelectTest.php
index 8407aee9d2..8763f09357 100644
--- a/core/tests/Drupal/KernelTests/Core/Database/SelectTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Database/SelectTest.php
@@ -613,4 +613,41 @@ public function testEmptyInCondition() {
     }
   }
 
+  /**
+   * Tests queries with >1000 items in an IN list.
+   *
+   * This test is motivated by SQLite's default limit of 999 placeholders, but
+   * it's good to ensure that all database drivers can handle large IN lists.
+   *
+   * @link https://www.sqlite.org/limits.html#max_variable_number
+   */
+  public function testLargeInCondition() {
+    $names = [];
+    $names[] = 'John';
+    for ($i=1; $i<500; $i++) {
+      $names[] = "Name $i";
+    }
+    $names[] = 'George';
+    for ($i=501; $i<1000; $i++) {
+      $names[] = "Name $i";
+    }
+    $names[] = 'Ringo';
+
+    // Find the above 3 Beatles.
+    $num_records = $this->connection->select('test')
+      ->condition('name', $names, 'IN')
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+    $this->assertEqual($num_records, 3);
+
+    // Find the other Beatle.
+    $num_records = $this->connection->select('test')
+      ->condition('name', $names, 'NOT IN')
+      ->countQuery()
+      ->execute()
+      ->fetchField();
+    $this->assertEqual($num_records, 1);
+  }
+
 }
diff --git a/core/tests/Drupal/KernelTests/Core/Database/UpsertTest.php b/core/tests/Drupal/KernelTests/Core/Database/UpsertTest.php
index 461117797f..2631de026e 100644
--- a/core/tests/Drupal/KernelTests/Core/Database/UpsertTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Database/UpsertTest.php
@@ -89,4 +89,44 @@ public function testSpecialColumnUpsert() {
     $this->assertEquals($record->function, 'Function 2');
   }
 
+  /**
+   * Confirms that we can upsert 1000 records successfully.
+   *
+   * This test is motivated by SQLite's default limit of 999 placeholders, but
+   * it's good to ensure that all database drivers can handle large upserts.
+   *
+   * @link https://www.sqlite.org/limits.html#max_variable_number
+   */
+  public function testLargeUpsert() {
+    $connection = Database::getConnection();
+    $num_records_before = $connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+
+    $upsert = $connection->upsert('test_people')
+      ->key('job')
+      ->fields(['job', 'age', 'name']);
+
+    for ($i=0; $i<1000; $i++) {
+      $values = [
+        'job' => "Job $i",
+        'age' => $i,
+        'name' => "Name $i",
+      ];
+      $upsert->values($values);
+    }
+
+    $upsert->execute();
+
+    $num_records_after = $connection->query('SELECT COUNT(*) FROM {test_people}')->fetchField();
+    $this->assertEqual($num_records_before + 1000, $num_records_after, 'Rows were inserted properly.');
+
+    $person = $connection->query('SELECT * FROM {test_people} WHERE job = :job', [':job' => 'Job 0'])->fetch();
+    $this->assertEqual($person->job, 'Job 0', 'First job set correctly.');
+    $this->assertEqual($person->age, 0, 'First age set correctly.');
+    $this->assertEqual($person->name, 'Name 0', 'First name set correctly.');
+
+    $person = $connection->query('SELECT * FROM {test_people} WHERE job = :job', [':job' => 'Job 999'])->fetch();
+    $this->assertEqual($person->job, 'Job 999', 'Last job set correctly.');
+    $this->assertEqual($person->age, 999, 'Last age set correctly.');
+    $this->assertEqual($person->name, 'Name 999', 'Last name set correctly.');
+  }
 }
