Commit f67095e4 authored by catch's avatar catch
Browse files

Issue #3174662 by mondrake, andypost, alexpott, hussainweb, anmolgoyal74:...

Issue #3174662 by mondrake, andypost, alexpott, hussainweb, anmolgoyal74: Encapsulate \PDOStatement instead of extending from it
parent 2b3e72f4
Loading
Loading
Loading
Loading
+38 −4
Original line number Diff line number Diff line
@@ -73,9 +73,22 @@ abstract class Connection {
   * The name of the Statement class for this connection.
   *
   * @var string
   *
   * @deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Database
   *   drivers should use or extend StatementWrapper instead, and encapsulate
   *   client-level statement objects.
   *
   * @see https://www.drupal.org/node/3177488
   */
  protected $statementClass = 'Drupal\Core\Database\Statement';

  /**
   * The name of the StatementWrapper class for this connection.
   *
   * @var string
   */
  protected $statementWrapperClass = NULL;

  /**
   * Whether this database connection supports transactional DDL.
   *
@@ -239,7 +252,9 @@ public function __construct(\PDO $connection, array $connection_options) {
    $this->setPrefix(isset($connection_options['prefix']) ? $connection_options['prefix'] : '');

    // Set a Statement class, unless the driver opted out.
    // @todo remove this in Drupal 10 https://www.drupal.org/node/3177490
    if (!empty($this->statementClass)) {
      @trigger_error('\Drupal\Core\Database\Connection::$statementClass is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Database drivers should use or extend StatementWrapper instead, and encapsulate client-level statement objects. See https://www.drupal.org/node/3177488', E_USER_DEPRECATED);
      $connection->setAttribute(\PDO::ATTR_STATEMENT_CLASS, [$this->statementClass, [$this]]);
    }

@@ -278,6 +293,7 @@ public function destroy() {
      // Destroy all references to this connection by setting them to NULL.
      // The Statement class attribute only accepts a new value that presents a
      // proper callable, so we reset it to PDOStatement.
      // @todo remove this in Drupal 10 https://www.drupal.org/node/3177490
      if (!empty($this->statementClass)) {
        $this->connection->setAttribute(\PDO::ATTR_STATEMENT_CLASS, ['PDOStatement', []]);
      }
@@ -535,7 +551,11 @@ public function prepareStatement(string $query, array $options): StatementInterf
    if (!($options['allow_square_brackets'] ?? FALSE)) {
      $query = $this->quoteIdentifiers($query);
    }
    return $this->connection->prepare($query, $options['pdo'] ?? []);
    // @todo in Drupal 10, only return the StatementWrapper.
    // @see https://www.drupal.org/node/3177490
    return $this->statementWrapperClass ?
      new $this->statementWrapperClass($this, $this->connection, $query, $options['pdo'] ?? []) :
      $this->connection->prepare($query, $options['pdo'] ?? []);
  }

  /**
@@ -728,7 +748,7 @@ protected function filterComment($comment = '') {
   * query. All queries executed by Drupal are executed as PDO prepared
   * statements.
   *
   * @param string|\Drupal\Core\Database\StatementInterface $query
   * @param string|\Drupal\Core\Database\StatementInterface|\PDOStatement $query
   *   The query to execute. In most cases this will be a string containing
   *   an SQL query with placeholders. An already-prepared instance of
   *   StatementInterface may also be passed in order to allow calling
@@ -779,6 +799,10 @@ public function query($query, array $args = [], $options = []) {
        $stmt = $query;
        $stmt->execute(NULL, $options);
      }
      elseif ($query instanceof \PDOStatement) {
        $stmt = $query;
        $stmt->execute();
      }
      else {
        $this->expandArguments($query, $args);
        // To protect against SQL injection, Drupal only supports executing one
@@ -854,7 +878,15 @@ protected function handleQueryException(\PDOException $e, $query, array $args =
      // Wrap the exception in another exception, because PHP does not allow
      // overriding Exception::getMessage(). Its message is the extra database
      // debug information.
      $query_string = ($query instanceof StatementInterface) ? $query->getQueryString() : $query;
      if ($query instanceof StatementInterface) {
        $query_string = $query->getQueryString();
      }
      elseif ($query instanceof \PDOStatement) {
        $query_string = $query->queryString;
      }
      else {
        $query_string = $query;
      }
      $message = $e->getMessage() . ": " . $query_string . "; " . print_r($args, TRUE);
      // Match all SQLSTATE 23xxx errors.
      if (substr($e->getCode(), -6, -3) == '23') {
@@ -1743,7 +1775,9 @@ abstract public function nextId($existing_id = 0);
   */
  public function prepare($statement, array $driver_options = []) {
    @trigger_error('Connection::prepare() is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Database drivers should instantiate \PDOStatement objects by calling \PDO::prepare in their Connection::prepareStatement method instead. \PDO::prepare should not be called outside of driver code. See https://www.drupal.org/node/3137786', E_USER_DEPRECATED);
    return $this->connection->prepare($statement, $driver_options);
    return $this->statementWrapperClass ?
      (new $this->statementWrapperClass($this, $this->connection, $statement, $driver_options))->getClientStatement() :
      $this->connection->prepare($statement, $driver_options);
  }

  /**
+11 −1
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@
use Drupal\Core\Database\DatabaseAccessDeniedException;
use Drupal\Core\Database\IntegrityConstraintViolationException;
use Drupal\Core\Database\DatabaseExceptionWrapper;

use Drupal\Core\Database\StatementWrapper;
use Drupal\Core\Database\Database;
use Drupal\Core\Database\DatabaseNotFoundException;
use Drupal\Core\Database\DatabaseException;
@@ -47,6 +47,16 @@ class Connection extends DatabaseConnection {
   */
  const SQLSTATE_SYNTAX_ERROR = 42000;

  /**
   * {@inheritdoc}
   */
  protected $statementClass = NULL;

  /**
   * {@inheritdoc}
   */
  protected $statementWrapperClass = StatementWrapper::class;

  /**
   * Flag to indicate if the cleanup function in __destruct() should run.
   *
+11 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
use Drupal\Core\Database\DatabaseAccessDeniedException;
use Drupal\Core\Database\DatabaseNotFoundException;
use Drupal\Core\Database\StatementInterface;
use Drupal\Core\Database\StatementWrapper;

// cSpell:ignore ilike nextval

@@ -38,6 +39,16 @@ class Connection extends DatabaseConnection {
   */
  const CONNECTION_FAILURE = '08006';

  /**
   * {@inheritdoc}
   */
  protected $statementClass = NULL;

  /**
   * {@inheritdoc}
   */
  protected $statementWrapperClass = StatementWrapper::class;

  /**
   * A map of condition operators to PostgreSQL operators.
   *
+3 −3
Original line number Diff line number Diff line
@@ -37,13 +37,13 @@ public function execute() {
          fwrite($blobs[$blob_count], $insert_values[$idx]);
          rewind($blobs[$blob_count]);

          $stmt->bindParam(':db_insert_placeholder_' . $max_placeholder++, $blobs[$blob_count], \PDO::PARAM_LOB);
          $stmt->getClientStatement()->bindParam(':db_insert_placeholder_' . $max_placeholder++, $blobs[$blob_count], \PDO::PARAM_LOB);

          // Pre-increment is faster in PHP than increment.
          ++$blob_count;
        }
        else {
          $stmt->bindParam(':db_insert_placeholder_' . $max_placeholder++, $insert_values[$idx]);
          $stmt->getClientStatement()->bindParam(':db_insert_placeholder_' . $max_placeholder++, $insert_values[$idx]);
        }
      }
      // Check if values for a serial field has been passed.
@@ -80,7 +80,7 @@ public function execute() {
      // the foreach statement assigns the element to the existing reference.
      $arguments = $this->fromQuery->getArguments();
      foreach ($arguments as $key => $value) {
        $stmt->bindParam($key, $arguments[$key]);
        $stmt->getClientStatement()->bindParam($key, $arguments[$key]);
      }
    }

+5 −5
Original line number Diff line number Diff line
@@ -32,14 +32,14 @@ public function execute() {
          // We assume that an expression will never happen on a BLOB field,
          // which is a fairly safe assumption to make since in most cases
          // it would be an invalid query anyway.
          $stmt->bindParam($placeholder, $data['arguments'][$placeholder]);
          $stmt->getClientStatement()->bindParam($placeholder, $data['arguments'][$placeholder]);
        }
      }
      if ($data['expression'] instanceof SelectInterface) {
        $data['expression']->compile($this->connection, $this);
        $select_query_arguments = $data['expression']->arguments();
        foreach ($select_query_arguments as $placeholder => $argument) {
          $stmt->bindParam($placeholder, $select_query_arguments[$placeholder]);
          $stmt->getClientStatement()->bindParam($placeholder, $select_query_arguments[$placeholder]);
        }
      }
      unset($fields[$field]);
@@ -52,11 +52,11 @@ public function execute() {
        $blobs[$blob_count] = fopen('php://memory', 'a');
        fwrite($blobs[$blob_count], $value);
        rewind($blobs[$blob_count]);
        $stmt->bindParam($placeholder, $blobs[$blob_count], \PDO::PARAM_LOB);
        $stmt->getClientStatement()->bindParam($placeholder, $blobs[$blob_count], \PDO::PARAM_LOB);
        ++$blob_count;
      }
      else {
        $stmt->bindParam($placeholder, $fields[$field]);
        $stmt->getClientStatement()->bindParam($placeholder, $fields[$field]);
      }
    }

@@ -65,7 +65,7 @@ public function execute() {

      $arguments = $this->condition->arguments();
      foreach ($arguments as $placeholder => $value) {
        $stmt->bindParam($placeholder, $arguments[$placeholder]);
        $stmt->getClientStatement()->bindParam($placeholder, $arguments[$placeholder]);
      }
    }

Loading