Commit 43a384ba authored by catch's avatar catch
Browse files

Issue #1120020 by effulgentsia, andypost, Garrett Albright, pfrenssen,...

Issue #1120020 by effulgentsia, andypost, Garrett Albright, pfrenssen, Taran2L, kim.pepper, andregp, phenaproxima, daffie, geek-merlin, rfay, ranjith_kumar_k_u, tf198, Damien Tournoud, alexpott, xjm, chx, bradjones1, hestenet: SQLite database locking errors cause fatal errors
parent cf4437e6
Loading
Loading
Loading
Loading
+10 −8
Original line number Diff line number Diff line
@@ -79,22 +79,24 @@ public function execute() {
    }

    $last_insert_id = 0;

    // Each insert happens in its own query in the degenerate case. However,
    // we wrap it in a transaction so that it is atomic where possible. On many
    // databases, such as SQLite, this is also a notable performance boost.
    $transaction = $this->connection->startTransaction();
    $stmt = $this->connection->prepareStatement((string) $this, $this->queryOptions);

    try {
      // Per https://en.wikipedia.org/wiki/Insert_%28SQL%29#Multirow_inserts,
      // not all databases implement SQL-92's standard syntax for multi-row
      // inserts. Therefore, in the degenerate case, execute a separate query
      // for each row, all within a single transaction for atomicity and
      // performance.
      $transaction = $this->connection->startTransaction();
      foreach ($this->insertValues as $insert_values) {
        $stmt->execute($insert_values, $this->queryOptions);
        $last_insert_id = $this->connection->lastInsertId();
      }
    }
    catch (\Exception $e) {
      if (isset($transaction)) {
        // One of the INSERTs failed, rollback the whole batch.
        $transaction->rollBack();
      }
      // Rethrow the exception for the calling code.
      throw $e;
    }
+12 −5
Original line number Diff line number Diff line
@@ -176,10 +176,11 @@
 * @code
 * function my_transaction_function() {
 *   $connection = \Drupal::database();
 *
 *   try {
 *     // The transaction opens here.
 *     $transaction = $connection->startTransaction();
 *
 *   try {
 *     $id = $connection->insert('example')
 *       ->fields(array(
 *         'field1' => 'string',
@@ -192,8 +193,14 @@
 *     return $id;
 *   }
 *   catch (Exception $e) {
 *     // Something went wrong somewhere, so roll back now.
 *     // Something went wrong somewhere. If the exception was thrown during
 *     // startTransaction(), then $transaction is NULL and there's nothing to
 *     // roll back. If the exception was thrown after a transaction was
 *     // successfully started, then it must be rolled back.
 *     if (isset($transaction)) {
 *       $transaction->rollBack();
 *     }
 *
 *     // Log the exception to watchdog.
 *     watchdog_exception('type', $e);
 *   }
+12 −6
Original line number Diff line number Diff line
@@ -746,15 +746,17 @@ public function delete(array $entities) {
      return;
    }

    $transaction = $this->database->startTransaction();
    try {
      $transaction = $this->database->startTransaction();
      parent::delete($entities);

      // Ignore replica server temporarily.
      \Drupal::service('database.replica_kill_switch')->trigger();
    }
    catch (\Exception $e) {
      if (isset($transaction)) {
        $transaction->rollBack();
      }
      watchdog_exception($this->entityTypeId, $e);
      throw new EntityStorageException($e->getMessage(), $e->getCode(), $e);
    }
@@ -797,8 +799,8 @@ protected function doDeleteFieldItems($entities) {
   * {@inheritdoc}
   */
  public function save(EntityInterface $entity) {
    $transaction = $this->database->startTransaction();
    try {
      $transaction = $this->database->startTransaction();
      $return = parent::save($entity);

      // Ignore replica server temporarily.
@@ -806,7 +808,9 @@ public function save(EntityInterface $entity) {
      return $return;
    }
    catch (\Exception $e) {
      if (isset($transaction)) {
        $transaction->rollBack();
      }
      watchdog_exception($this->entityTypeId, $e);
      throw new EntityStorageException($e->getMessage(), $e->getCode(), $e);
    }
@@ -816,8 +820,8 @@ public function save(EntityInterface $entity) {
   * {@inheritdoc}
   */
  public function restore(EntityInterface $entity) {
    $transaction = $this->database->startTransaction();
    try {
      $transaction = $this->database->startTransaction();
      // Insert the entity data in the base and data tables only for default
      // revisions.
      /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
@@ -853,7 +857,9 @@ public function restore(EntityInterface $entity) {
      \Drupal::service('database.replica_kill_switch')->trigger();
    }
    catch (\Exception $e) {
      if (isset($transaction)) {
        $transaction->rollBack();
      }
      watchdog_exception($this->entityTypeId, $e);
      throw new EntityStorageException($e->getMessage(), $e->getCode(), $e);
    }
+26 −19
Original line number Diff line number Diff line
@@ -763,12 +763,13 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $
        }
      }

      try {
        if ($this->database->supportsTransactionalDDL()) {
          // If the database supports transactional DDL, we can go ahead and rely
          // on it. If not, we will have to rollback manually if something fails.
          $transaction = $this->database->startTransaction();
        }
      try {

        // Copy the data from the base table.
        $this->database->insert($dedicated_table_name)
          ->from($this->getSelectQueryForFieldStorageDeletion($field_table_name, $shared_table_field_columns, $dedicated_table_field_columns))
@@ -788,9 +789,11 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $
        }
      }
      catch (\Exception $e) {
        if ($this->database->supportsTransactionalDDL()) {
          if (isset($transaction)) {
            $transaction->rollBack();
          }
        }
        else {
          // Delete the dedicated tables.
          foreach ($dedicated_table_field_schema as $name => $table) {
@@ -1724,12 +1727,12 @@ protected function deleteSharedTableSchema(FieldStorageDefinitionInterface $stor
  protected function updateDedicatedTableSchema(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) {
    if (!$this->storage->countFieldData($original, TRUE)) {
      // There is no data. Re-create the tables completely.
      try {
        if ($this->database->supportsTransactionalDDL()) {
          // If the database supports transactional DDL, we can go ahead and rely
          // on it. If not, we will have to rollback manually if something fails.
          $transaction = $this->database->startTransaction();
        }
      try {
        // Since there is no data we may be switching from a shared table schema
        // to a dedicated table schema, hence we should use the proper API.
        $this->performFieldSchemaOperation('delete', $original);
@@ -1737,8 +1740,10 @@ protected function updateDedicatedTableSchema(FieldStorageDefinitionInterface $s
      }
      catch (\Exception $e) {
        if ($this->database->supportsTransactionalDDL()) {
          if (isset($transaction)) {
            $transaction->rollBack();
          }
        }
        else {
          // Recreate tables.
          $this->performFieldSchemaOperation('create', $original);
@@ -1815,12 +1820,12 @@ protected function updateDedicatedTableSchema(FieldStorageDefinitionInterface $s
   */
  protected function updateSharedTableSchema(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) {
    if (!$this->storage->countFieldData($original, TRUE)) {
      try {
        if ($this->database->supportsTransactionalDDL()) {
          // If the database supports transactional DDL, we can go ahead and rely
          // on it. If not, we will have to rollback manually if something fails.
          $transaction = $this->database->startTransaction();
        }
      try {
        // Since there is no data we may be switching from a dedicated table
        // to a schema table schema, hence we should use the proper API.
        $this->performFieldSchemaOperation('delete', $original);
@@ -1828,8 +1833,10 @@ protected function updateSharedTableSchema(FieldStorageDefinitionInterface $stor
      }
      catch (\Exception $e) {
        if ($this->database->supportsTransactionalDDL()) {
          if (isset($transaction)) {
            $transaction->rollBack();
          }
        }
        else {
          // Recreate original schema.
          $this->createSharedTableSchema($original);
+4 −2
Original line number Diff line number Diff line
@@ -76,15 +76,17 @@ public function onRouterRebuild($event) {
   */
  protected function menuLinksRebuild() {
    if ($this->lock->acquire(__FUNCTION__)) {
      $transaction = $this->connection->startTransaction();
      try {
        $transaction = $this->connection->startTransaction();
        // Ensure the menu links are up to date.
        $this->menuLinkManager->rebuild();
        // Ignore any database replicas temporarily.
        $this->replicaKillSwitch->trigger();
      }
      catch (\Exception $e) {
        if (isset($transaction)) {
          $transaction->rollBack();
        }
        watchdog_exception('menu', $e);
      }

Loading