Unverified Commit 39170302 authored by Alex Pott's avatar Alex Pott
Browse files

Issue #2345451 by mondrake, daffie, david_garcia, shobhit_juyal, sokru,...

Issue #2345451 by mondrake, daffie, david_garcia, shobhit_juyal, sokru, markdorison, alexpott, Beakerboy: Introduce a Connection::prepareStatement method allowing to pass options to the Statement, deprecate Connection::prepareQuery() and Connection::prepare()
parent aece6314
Loading
Loading
Loading
Loading
+49 −11
Original line number Diff line number Diff line
@@ -315,6 +315,11 @@ public function destroy() {
   *   database type. In rare cases, such as creating an SQL function, []
   *   characters might be needed and can be allowed by changing this option to
   *   TRUE.
   * - pdo: By default, queries will execute with the PDO options set on the
   *   connection. In particular cases, it could be necessary to override the
   *   PDO driver options on the statement level. In such case, pass the
   *   required setting as an array here, and they will be passed to the
   *   prepared statement. See https://www.php.net/manual/en/pdo.prepare.php.
   *
   * @return array
   *   An array of default query options.
@@ -326,6 +331,7 @@ protected function defaultOptions() {
      'throw_exception' => TRUE,
      'allow_delimiter_in_query' => FALSE,
      'allow_square_brackets' => FALSE,
      'pdo' => [],
    ];
  }

@@ -476,6 +482,31 @@ public function getFullQualifiedTableName($table) {
    return $options['database'] . '.' . $prefix . $table;
  }

  /**
   * Returns a prepared statement given a SQL string.
   *
   * This method caches prepared statements, reusing them when possible. It also
   * prefixes tables names enclosed in curly braces and, optionally, quotes
   * identifiers enclosed in square brackets.
   *
   * @param string $query
   *   The query string as SQL, with curly braces surrounding the table names.
   * @param array $options
   *   An associative array of options to control how the query is run. See
   *   the documentation for self::defaultOptions() for details. The content of
   *   the 'pdo' key will be passed to the prepared statement.
   *
   * @return \Drupal\Core\Database\StatementInterface
   *   A PDO prepared statement ready for its execute() method.
   */
  public function prepareStatement(string $query, array $options): StatementInterface {
    $query = $this->prefixTables($query);
    if (!($options['allow_square_brackets'] ?? FALSE)) {
      $query = $this->quoteIdentifiers($query);
    }
    return $this->connection->prepare($query, $options['pdo'] ?? []);
  }

  /**
   * Prepares a query string and returns the prepared statement.
   *
@@ -492,14 +523,15 @@ public function getFullQualifiedTableName($table) {
   *
   * @return \Drupal\Core\Database\StatementInterface
   *   A PDO prepared statement ready for its execute() method.
   *
   * @deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use
   *   ::prepareStatement instead.
   *
   * @see https://www.drupal.org/node/3137786
   */
  public function prepareQuery($query, $quote_identifiers = TRUE) {
    $query = $this->prefixTables($query);
    if ($quote_identifiers) {
      $query = $this->quoteIdentifiers($query);
    }

    return $this->connection->prepare($query);
    @trigger_error('Connection::prepareQuery() is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use ::prepareStatement() instead. See https://www.drupal.org/node/3137786', E_USER_DEPRECATED);
    return $this->prepareStatement($query, ['allow_square_brackets' => !$quote_identifiers]);
  }

  /**
@@ -675,9 +707,7 @@ protected function filterComment($comment = '') {
   *   object to this method. It is used primarily for database drivers for
   *   databases that require special LOB field handling.
   * @param array $args
   *   An array of arguments for the prepared statement. If the prepared
   *   statement uses ? placeholders, this array must be an indexed array.
   *   If it contains named placeholders, it must be an associative array.
   *   The associative array of arguments for the prepared statement.
   * @param array $options
   *   An associative array of options to control how the query is run. The
   *   given options will be merged with self::defaultOptions(). See the
@@ -734,7 +764,7 @@ public function query($query, array $args = [], $options = []) {
        if (strpos($query, ';') !== FALSE && empty($options['allow_delimiter_in_query'])) {
          throw new \InvalidArgumentException('; is not supported in SQL strings. Use only one statement at a time.');
        }
        $stmt = $this->prepareQuery($query, !$options['allow_square_brackets']);
        $stmt = $this->prepareStatement($query, $options);
        $stmt->execute($args, $options);
      }

@@ -1668,9 +1698,17 @@ abstract public function nextId($existing_id = 0);
   *
   * @throws \PDOException
   *
   * @see \PDO::prepare()
   * @see https://www.php.net/manual/en/pdo.prepare.php
   *
   * @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 Collection::prepareStatement method instead.
   *   \PDO::prepare should not be called outside of driver code.
   *
   * @see https://www.drupal.org/node/3137786
   */
  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 Collection::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);
  }

+7 −2
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
use Drupal\Core\Database\Connection as DatabaseConnection;
use Drupal\Core\Database\DatabaseAccessDeniedException;
use Drupal\Core\Database\DatabaseNotFoundException;
use Drupal\Core\Database\StatementInterface;

/**
 * @addtogroup database
@@ -184,12 +185,16 @@ public function query($query, array $args = [], $options = []) {
    return $return;
  }

  public function prepareQuery($query, $quote_identifiers = TRUE) {
  /**
   * {@inheritdoc}
   */
  public function prepareStatement(string $query, array $options): StatementInterface {
    // mapConditionOperator converts some operations (LIKE, REGEXP, etc.) to
    // PostgreSQL equivalents (ILIKE, ~*, etc.). However PostgreSQL doesn't
    // automatically cast the fields to the right type for these operators,
    // so we need to alter the query and add the type-cast.
    return parent::prepareQuery(preg_replace('/ ([^ ]+) +(I*LIKE|NOT +I*LIKE|~\*|!~\*) /i', ' ${1}::text ${2} ', $query), $quote_identifiers);
    $query = preg_replace('/ ([^ ]+) +(I*LIKE|NOT +I*LIKE|~\*|!~\*) /i', ' ${1}::text ${2} ', $query);
    return parent::prepareStatement($query, $options);
  }

  public function queryRange($query, $from, $count, array $args = [], array $options = []) {
+1 −1
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ public function execute() {
      return NULL;
    }

    $stmt = $this->connection->prepareQuery((string) $this);
    $stmt = $this->connection->prepareStatement((string) $this, $this->queryOptions);

    // Fetch the list of blobs and sequences used on that table.
    $table_information = $this->connection->schema()->queryTableInformation($this->table);
+1 −1
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@ public function execute() {
      return NULL;
    }

    $stmt = $this->connection->prepareQuery((string) $this);
    $stmt = $this->connection->prepareStatement((string) $this, $this->queryOptions);

    // Fetch the list of blobs and sequences used on that table.
    $table_information = $this->connection->schema()->queryTableInformation($this->table);
+1 −1
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ public function execute() {

    // Because we filter $fields the same way here and in __toString(), the
    // placeholders will all match up properly.
    $stmt = $this->connection->prepareQuery((string) $this);
    $stmt = $this->connection->prepareStatement((string) $this, $this->queryOptions);

    // Fetch the list of blobs and sequences used on that table.
    $table_information = $this->connection->schema()->queryTableInformation($this->table);
Loading