[TASK] Use prepared statements in generic persistence 26/27426/19
authorFelix Oertel <felix@oer.tel>
Tue, 4 Feb 2014 15:42:29 +0000 (16:42 +0100)
committerErnesto Baschny <ernst@cron-it.de>
Wed, 12 Mar 2014 13:08:46 +0000 (14:08 +0100)
While Typo3DbBackend claims to be the glue between Extbase
and TYPO3, it hardly uses the TYPO3 DB API.

This patch enhances getObjectData- and getObjectCountByQuery
- the main methods used when dealing with domain models
through a repository - to use the TYPO3 DB API. In conjunction
this enables Extbase to use the preparedStatement introduced
in TYPO3 4.5.

Because prepared queries might not be desirable in all circum-
stances, they are disabled by default. They can be enable on
a per-query-basis via
  $query->getQuerySettings()->setUsePreparedStatement(TRUE);

Prepared statements will *only* be used for SELECT queries.

* consistent use of the TYPO3 DB API
  use exec_SELECTgetRows and exec_SELECTcountRows() even for
  non-prepared queries. This should help a lot along the way
  to support DBAL.

* preparedStatement in custom statements
  When using custom statements, it's now possible to use a pre-
  pared statement in $query->statement(), instead of a query-
  string. The prepared statement has to be initialized already
  though, because Extbase cannot take care of splitting your
  custom query string. The parameters are given as array in the
  second argument as usual.

* deprecated using parameters in non-prepared custom statement
  If a custom query string is used with $query->statement(),
  giving the parameters in the second argument is deprecated.

* get count working
  Counting - especially with limit and offset - has been broken
  for quite some time now. By refactoring the limit and offset
  parameters and adapting exec_SELECTcountRows() this should
  now work again.

The next step is to separate the query parsing from the backend
and introduce an intelligent query cache. This will work as a
drop-in-replacement. The @todo-annotations are intended to stay
there for the next patch.

Resolves: #55360
Releases: 6.2
Change-Id: I91a3fd69470df178a8a5465511e50766b50d7f52
Reviewed-on: https://review.typo3.org/27426
Reviewed-by: Steffen Ritter
Tested-by: Steffen Ritter
Reviewed-by: Marc Bastian Heinrichs
Reviewed-by: Ernesto Baschny
Tested-by: Ernesto Baschny
typo3/sysext/extbase/Classes/Persistence/Generic/Qom/Statement.php
typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbBackend.php
typo3/sysext/extbase/Classes/Persistence/Generic/Typo3QuerySettings.php

index 5f45db9..08c0846 100644 (file)
@@ -33,7 +33,7 @@ namespace TYPO3\CMS\Extbase\Persistence\Generic\Qom;
 class Statement {
 
        /**
-        * @var string
+        * @var mixed
         */
        protected $statement;
 
@@ -45,10 +45,19 @@ class Statement {
        /**
         * Constructs the Statement instance
         *
-        * @param string $statement The statement
-        * @param array $boundVariables An array of variables to bind to the statement
+        * @param mixed $statement The statement as sql string or TYPO3\CMS\Core\Database\PreparedStatement
+        * @param array $boundVariables An array of variables to bind to the statement, only to be used with preparedStatement
         */
        public function __construct($statement, array $boundVariables = array()) {
+               // @deprecated since 6.2, using $boundVariables without preparedStatement will be removed in two versions
+               if (
+                       !empty($boundVariables)
+                       && !($statement instanceof \TYPO3\CMS\Core\Database\PreparedStatement)
+               ) {
+                       \TYPO3\CMS\Core\Utility\GeneralUtility::deprecationLog('Using boundVariables'
+                               . ' in Extbase\'s custom statement without using preparedStatement is'
+                               . ' deprecated since TYPO3 6.2 and will be removed in two versions.');
+               }
                $this->statement = $statement;
                $this->boundVariables = $boundVariables;
        }
index 4e98538..803e0ca 100644 (file)
@@ -29,6 +29,7 @@ namespace TYPO3\CMS\Extbase\Persistence\Generic\Storage;
  ***************************************************************/
 
 use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Extbase\Persistence\QueryInterface;
 
 /**
  * A Storage backend
@@ -277,29 +278,150 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B
         * @return array
         */
        public function getObjectDataByQuery(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query) {
-               $statement = $query->getStatement();
-               if ($statement instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\Statement) {
-                       $sql = $statement->getStatement();
-                       $parameters = $statement->getBoundVariables();
+               if ($query->getStatement() instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\Statement) {
+                       $rows = $this->getObjectDataByRawQuery($query);
                } else {
+
+                       // @todo with queryParser we don't need this parameters anymore
                        $parameters = array();
                        $statementParts = $this->parseQuery($query, $parameters);
-                       $sql = $this->buildQuery($statementParts, $parameters);
-               }
-               $tableName = 'foo';
-               if (is_array($statementParts) && !empty($statementParts['tables'][0])) {
-                       $tableName = $statementParts['tables'][0];
+                       $rows = $this->getRowsByStatementParts($query, $statementParts, $parameters);
                }
-               $this->replacePlaceholders($sql, $parameters, $tableName);
-               // debug($sql,-2);
-               $result = $this->databaseHandle->sql_query($sql);
-               $this->checkSqlErrors($sql);
-               $rows = $this->getRowsFromResult($result);
-               $this->databaseHandle->sql_free_result($result);
-               // Get language uid from querySettings.
-               // Ensure the backend handling is not broken (fallback to Get parameter 'L' if needed)
+
                $rows = $this->doLanguageAndWorkspaceOverlay($query->getSource(), $rows, $query->getQuerySettings());
-               // TODO: implement $objectData = $this->processObjectRecords($statementHandle);
+               return $rows;
+       }
+
+       /**
+        * Creates the parameters for the query methods of the database methods in the TYPO3 core, from an array
+        * that came from a parsed query.
+        *
+        * @param array $statementParts
+        * @return array
+        */
+       protected function createQueryCommandParametersFromStatementParts(array $statementParts) {
+               return array(
+                       'selectFields' => implode(' ', $statementParts['keywords']) . ' ' . implode(',', $statementParts['fields']),
+                       'fromTable'    => implode(' ', $statementParts['tables']) . ' ' . implode(' ', $statementParts['unions']),
+                       'whereClause'  => (!empty($statementParts['where']) ? implode('', $statementParts['where']) : '1')
+                               . (!empty($statementParts['additionalWhereClause'])
+                                       ? ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'])
+                                       : ''
+                       ),
+                       'orderBy'      => (!empty($statementParts['orderings']) ? implode(', ', $statementParts['orderings']) : ''),
+                       'limit'        => ($statementParts['offset'] ? $statementParts['offset'] . ', ' : '')
+                               . ($statementParts['limit'] ? $statementParts['limit'] : '')
+               );
+       }
+
+       /**
+        * Determines wether to use prepared statement or not and returns the rows from the corresponding method
+        *
+        * @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
+        * @param array $statementParts
+        * @param array $parameters
+        * @return array
+        */
+       protected function getRowsByStatementParts(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query, array $statementParts, array $parameters) {
+               if ($query->getQuerySettings()->getUsePreparedStatement()) {
+                       $rows = $this->getRowsFromPreparedDatabase($statementParts, $parameters);
+               } else {
+                       $rows = $this->getRowsFromDatabase($statementParts, $parameters);
+               }
+
+               return $rows;
+       }
+
+       /**
+        * Fetches the rows directly from the database, not using prepared statement
+        *
+        * @param array $statementParts
+        * @param array $parameters
+        * @return array the result
+        */
+       protected function getRowsFromDatabase(array $statementParts, array $parameters) {
+               $queryCommandParameters = $this->createQueryCommandParametersFromStatementParts($statementParts);
+
+               // @todo this work-around will be gone with queryParser
+               $this->replacePlaceholders($queryCommandParameters['whereClause'], $parameters);
+
+               $rows = $this->databaseHandle->exec_SELECTgetRows(
+                       $queryCommandParameters['selectFields'],
+                       $queryCommandParameters['fromTable'],
+                       $queryCommandParameters['whereClause'],
+                       '',
+                       $queryCommandParameters['orderBy'],
+                       $queryCommandParameters['limit']
+               );
+               $this->checkSqlErrors();
+
+               return $rows;
+       }
+
+       /**
+        * Fetches the rows from the database, using prepared statement
+        *
+        * @param array $statementParts
+        * @param array $parameters
+        * @return array the result
+        */
+       protected function getRowsFromPreparedDatabase(array $statementParts, array $parameters) {
+               $queryCommandParameters = $this->createQueryCommandParametersFromStatementParts($statementParts);
+               $preparedStatement = $this->databaseHandle->prepare_SELECTquery(
+                       $queryCommandParameters['selectFields'],
+                       $queryCommandParameters['fromTable'],
+                       $queryCommandParameters['whereClause'],
+                       '',
+                       $queryCommandParameters['orderBy'],
+                       $queryCommandParameters['limit']
+               );
+
+               $preparedStatement->execute($parameters);
+               $rows = $preparedStatement->fetchAll();
+
+               $preparedStatement->free();
+               return $rows;
+       }
+
+       /**
+        * Returns the object data using a custom statement
+        *
+        * @param \TYPO3\CMS\Extbase\Persistence\QueryInterface $query
+        * @return array
+        */
+       protected function getObjectDataByRawQuery(QueryInterface $query) {
+               $statement = $query->getStatement();
+               $parameters = $statement->getBoundVariables();
+
+               if ($statement instanceof \TYPO3\CMS\Core\Database\PreparedStatement) {
+                       $preparedStatement = $statement->getStatement();
+
+                       $preparedStatement->execute($parameters);
+                       $rows = $preparedStatement->fetchAll();
+
+                       $preparedStatement->free();
+               } else {
+
+                       /**
+                        * @deprecated since 6.2, this block will be removed in two versions
+                        * the deprecation log is in Qom\Statement
+                        */
+                       if (!empty($parameters)) {
+                               $sqlString = $statement->getStatement();
+                               $this->replacePlaceholders($sqlString, $parameters);
+                       }
+
+                       $result = $this->databaseHandle->sql_query($sqlString);
+                       $this->checkSqlErrors();
+
+                       $rows = array();
+                       while ($row = $this->databaseHandle->sql_fetch_assoc($result)) {
+                               if (is_array($row)) {
+                                       $rows[] = $row;
+                               }
+                       }
+               }
+
                return $rows;
        }
 
@@ -311,38 +433,42 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B
         * @return integer The number of matching tuples
         */
        public function getObjectCountByQuery(\TYPO3\CMS\Extbase\Persistence\QueryInterface $query) {
-               $constraint = $query->getConstraint();
-               if ($constraint instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\Statement) {
+               if ($query->getConstraint() instanceof \TYPO3\CMS\Extbase\Persistence\Generic\Qom\Statement) {
                        throw new \TYPO3\CMS\Extbase\Persistence\Generic\Storage\Exception\BadConstraintException('Could not execute count on queries with a constraint of type TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Qom\\StatementInterface', 1256661045);
                }
+
+               // @todo with queryParser we don't need this parameters anymore
                $parameters = array();
                $statementParts = $this->parseQuery($query, $parameters);
-               // Reset $statementParts for valid table return
-               reset($statementParts);
-               // if limit is set, we need to count the rows "manually" as COUNT(*) ignores LIMIT constraints
-               if (!empty($statementParts['limit'])) {
-                       $statement = $this->buildQuery($statementParts, $parameters);
-                       $this->replacePlaceholders($statement, $parameters, current($statementParts['tables']));
-                       $result = $this->databaseHandle->sql_query($statement);
-                       $this->checkSqlErrors($statement);
-                       $count = $this->databaseHandle->sql_num_rows($result);
-               } else {
-                       $statementParts['fields'] = array('COUNT(*)');
-                       // having orderings without grouping is not compatible with non-MySQL DBMS
-                       $statementParts['orderings'] = array();
-                       if (isset($statementParts['keywords']['distinct'])) {
-                               unset($statementParts['keywords']['distinct']);
-                               $statementParts['fields'] = array('COUNT(DISTINCT ' . reset($statementParts['tables']) . '.uid)');
-                       }
-                       $statement = $this->buildQuery($statementParts, $parameters);
-                       $this->replacePlaceholders($statement, $parameters, current($statementParts['tables']));
-                       $result = $this->databaseHandle->sql_query($statement);
-                       $this->checkSqlErrors($statement);
-                       $rows = $this->getRowsFromResult($result);
-                       $count = current(current($rows));
+
+               // @todo this work-around will be gone with queryParser
+               $statementParts['where'] = implode('', $statementParts['where']);
+               $this->replacePlaceholders($statementParts['where'], $parameters);
+
+               $fields = '*';
+               if (isset($statementParts['keywords']['distinct'])) {
+                       $fields = 'DISTINCT ' . reset($statementParts['tables']) . '.uid';
+               }
+
+               $count = $this->databaseHandle->exec_SELECTcountRows(
+                       $fields,
+                       implode(' ', $statementParts['tables']) . ' ' . implode(' ', $statementParts['unions']),
+                       (!empty($statementParts['where']) ? $statementParts['where'] : '1')
+                               . (!empty($statementParts['additionalWhereClause'])
+                                       ? ' AND ' . implode(' AND ', $statementParts['additionalWhereClause'])
+                                       : '')
+               );
+               $this->checkSqlErrors();
+
+               if ($statementParts['offset']) {
+                       $count -= $statementParts['offset'];
                }
-               $this->databaseHandle->sql_free_result($result);
-               return (int)$count;
+
+               if ($statementParts['limit']) {
+                       $count = min($count, $statementParts['limit']);
+               }
+
+               return (int)max(0, $count);
        }
 
        /**
@@ -361,12 +487,12 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B
                $sql['where'] = array();
                $sql['additionalWhereClause'] = array();
                $sql['orderings'] = array();
-               $sql['limit'] = array();
+               $sql['limit'] = ((int)$query->getLimit() ?: NULL);
+               $sql['offset'] = ((int)$query->getOffset() ?: NULL);
                $source = $query->getSource();
                $this->parseSource($source, $sql);
                $this->parseConstraint($query->getConstraint(), $source, $sql, $parameters);
                $this->parseOrderings($query->getOrderings(), $source, $sql);
-               $this->parseLimitAndOffset($query->getLimit(), $query->getOffset(), $sql);
                $tableNames = array_unique(array_keys($sql['tables'] + $sql['unions']));
                foreach ($tableNames as $tableName) {
                        if (is_string($tableName) && strlen($tableName) > 0) {
@@ -704,10 +830,13 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B
                        $operator = $this->resolveOperator($operator);
                        $constraintSQL = '';
                        if ($valueFunction === NULL) {
-                               $constraintSQL .= (!empty($tableName) ? $tableName . '.' : '') . $columnName . ' ' . $operator . ' ?';
+                               $constraintSQL .= (!empty($tableName) ? $tableName . '.' : '') . $columnName . ' ' . $operator;
                        } else {
-                               $constraintSQL .= $valueFunction . '(' . (!empty($tableName) ? $tableName . '.' : '') . $columnName . ') ' . $operator . ' ?';
+                               $constraintSQL .= $valueFunction . '(' . (!empty($tableName) ? $tableName . '.' : '') . $columnName . ') ' . $operator;
                        }
+
+                       $constraintSQL .= ' ?';
+
                        $sql['where'][] = $constraintSQL;
                }
        }
@@ -1075,38 +1204,6 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B
        }
 
        /**
-        * Transforms limit and offset into SQL
-        *
-        * @param integer $limit
-        * @param integer $offset
-        * @param array &$sql
-        * @return void
-        */
-       protected function parseLimitAndOffset($limit, $offset, array &$sql) {
-               if ($limit !== NULL && $offset !== NULL) {
-                       $sql['limit'] = (int)$offset . ', ' . (int)$limit;
-               } elseif ($limit !== NULL) {
-                       $sql['limit'] = (int)$limit;
-               }
-       }
-
-       /**
-        * Transforms a Resource from a database query to an array of rows.
-        *
-        * @param resource $result The result
-        * @return array The result as an array of rows (tuples)
-        */
-       protected function getRowsFromResult($result) {
-               $rows = array();
-               while ($row = $this->databaseHandle->sql_fetch_assoc($result)) {
-                       if (is_array($row)) {
-                               $rows[] = $row;
-                       }
-               }
-               return $rows;
-       }
-
-       /**
         * Performs workspace and language overlay on the given row array. The language and workspace id is automatically
         * detected (depending on FE or BE context). You can also explicitly set the language/workspace id.
         *
index 3b5c083..5443119 100644 (file)
@@ -40,7 +40,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        /**
         * Flag if the storage page should be respected for the query.
         *
-        * @var boolean
+        * @var bool
         */
        protected $respectStoragePage = TRUE;
 
@@ -56,7 +56,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
         * If--in addition to this--enableFieldsToBeIgnored is set, only fields specified there are ignored. If FALSE, all
         * enable fields are taken into account, regardless of the enableFieldsToBeIgnored setting.
         *
-        * @var boolean
+        * @var bool
         */
        protected $ignoreEnableFields = FALSE;
 
@@ -71,14 +71,14 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        /**
         * Flag whether deleted records should be included in the result set.
         *
-        * @var boolean
+        * @var bool
         */
        protected $includeDeleted = FALSE;
 
        /**
         * Flag if the sys_language_uid should be respected (default is TRUE).
         *
-        * @var boolean
+        * @var bool
         */
        protected $respectSysLanguage = TRUE;
 
@@ -99,19 +99,26 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        /**
         * Represensting sys_language_uid only valid for current context
         *
-        * @var integer
+        * @var int
         */
        protected $languageUid = 0;
 
        /**
         * Flag if the the query result should be returned as raw QueryResult.
         *
-        * @var boolean
+        * @var bool
         * @deprecated since Extbase 6.2, will be removed two versions later
         */
        protected $returnRawQueryResult = FALSE;
 
        /**
+        * Flag whether the query should use a prepared statement
+        *
+        * @var bool
+        */
+       protected $usePreparedStatement = FALSE;
+
+       /**
         * As long as we use a feature flag ignoreAllEnableFieldsInBe to determine the default behavior, the
         * initializeObject is responsible for handling that.
         */
@@ -143,7 +150,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        /**
         * Sets the flag if the storage page should be respected for the query.
         *
-        * @param boolean $respectStoragePage If TRUE the storage page ID will be determined and the statement will be extended accordingly.
+        * @param bool $respectStoragePage If TRUE the storage page ID will be determined and the statement will be extended accordingly.
         * @return \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface
         * @api
         */
@@ -155,7 +162,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        /**
         * Returns the state, if the storage page should be respected for the query.
         *
-        * @return boolean TRUE, if the storage page should be respected; otherwise FALSE.
+        * @return bool TRUE, if the storage page should be respected; otherwise FALSE.
         */
        public function getRespectStoragePage() {
                return $this->respectStoragePage;
@@ -183,7 +190,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        }
 
        /**
-        * @param boolean $respectSysLanguage TRUE if TYPO3 language settings are to be applied
+        * @param bool $respectSysLanguage TRUE if TYPO3 language settings are to be applied
         * @return \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface (fluent interface)
         * @api
         */
@@ -193,7 +200,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        }
 
        /**
-        * @return boolean TRUE if TYPO3 language settings are to be applied
+        * @return bool TRUE if TYPO3 language settings are to be applied
         */
        public function getRespectSysLanguage() {
                return $this->respectSysLanguage;
@@ -234,7 +241,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        }
 
        /**
-        * @param integer $languageUid
+        * @param int $languageUid
         * @return \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface instance of $this to allow method chaining
         * @api
         */
@@ -244,7 +251,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        }
 
        /**
-        * @return integer
+        * @return int
         */
        public function getLanguageUid() {
                return $this->languageUid;
@@ -253,7 +260,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        /**
         * Sets the language uid for the language overlay.
         *
-        * @param integer $sysLanguageUid language uid for the language overlay
+        * @param int $sysLanguageUid language uid for the language overlay
         * @return \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface instance of $this to allow method chaining
         * @deprecated since Extbase 6.2, will be removed two versions later. Use setLanguageUid() instead.
         */
@@ -265,7 +272,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        /**
         * Returns the language uid for the language overlay
         *
-        * @return integer language uid for the language overlay
+        * @return int language uid for the language overlay
         * @deprecated since Extbase 6.2, will be removed two versions later. Use getLanguageUid() instead.
         */
        public function getSysLanguageUid() {
@@ -276,7 +283,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        /**
         * Sets the flag if the visibility in the frontend should be respected.
         *
-        * @param boolean $respectEnableFields TRUE if the visibility in the frontend should be respected. If TRUE, the "enable fields" of TYPO3 will be added to the query statement.
+        * @param bool $respectEnableFields TRUE if the visibility in the frontend should be respected. If TRUE, the "enable fields" of TYPO3 will be added to the query statement.
         * @return \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface
         * @deprecated since Extbase 6.0, will be removed two versions later. Use setIgnoreEnableFields() and setEnableFieldsToBeIgnored() instead.
         * @see setIgnoreEnableFields()
@@ -293,7 +300,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        /**
         * Returns the state, if the visibility settings for the frontend should be respected for the query.
         *
-        * @return boolean TRUE, if the visibility settings for the frontend should should be respected; otherwise FALSE.
+        * @return bool TRUE, if the visibility settings for the frontend should should be respected; otherwise FALSE.
         * @deprecated since Extbase 6.0, will be removed two versions later. Use getIgnoreEnableFields() and getEnableFieldsToBeIgnored() instead.
         * @see getIgnoreEnableFields()
         * @see getEnableFieldsToBeIgnored()
@@ -308,7 +315,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
         * If--in addition to this--enableFieldsToBeIgnored is set, only fields specified there are ignored. If FALSE, all
         * enable fields are taken into account, regardless of the enableFieldsToBeIgnored setting.
         *
-        * @param boolean $ignoreEnableFields
+        * @param bool $ignoreEnableFields
         * @return \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface
         * @see setEnableFieldsToBeIgnored()
         * @api
@@ -324,7 +331,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
         * If TRUE, all enable fields are ignored. If--in addition to this--enableFieldsToBeIgnored is set, only fields specified there are ignored.
         * If FALSE, all enable fields are taken into account, regardless of the enableFieldsToBeIgnored setting.
         *
-        * @return boolean
+        * @return bool
         * @see getEnableFieldsToBeIgnored()
         */
        public function getIgnoreEnableFields() {
@@ -360,7 +367,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        /**
         * Sets the flag if the query should return objects that are deleted.
         *
-        * @param boolean $includeDeleted
+        * @param bool $includeDeleted
         * @return \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface
         * @api
         */
@@ -372,7 +379,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        /**
         * Returns if the query should return objects that are deleted.
         *
-        * @return boolean
+        * @return bool
         */
        public function getIncludeDeleted() {
                return $this->includeDeleted;
@@ -381,7 +388,7 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        /**
         * Sets the state, if the QueryResult should be returned unmapped.
         *
-        * @param boolean $returnRawQueryResult TRUE, if the QueryResult should be returned unmapped; otherwise FALSE.
+        * @param bool $returnRawQueryResult TRUE, if the QueryResult should be returned unmapped; otherwise FALSE.
         * @return \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface
         * @deprecated since Extbase 6.2, will be removed two versions later. Please use argument in query->execute() instead.
         */
@@ -394,11 +401,27 @@ class Typo3QuerySettings implements \TYPO3\CMS\Extbase\Persistence\Generic\Query
        /**
         * Returns the state, if the QueryResult should be returned unmapped.
         *
-        * @return boolean TRUE, if the QueryResult should be returned unmapped; otherwise FALSE.
+        * @return bool TRUE, if the QueryResult should be returned unmapped; otherwise FALSE.
         * @deprecated since Extbase 6.2, will be removed two versions later. Please use argument in query->execute() instead.
         */
        public function getReturnRawQueryResult() {
                // We do not log this call intentionally, otherwise the deprecation log would be filled up
                return $this->returnRawQueryResult;
        }
+
+       /**
+        * @param bool $usePreparedStatement
+        * @return \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface
+        */
+       public function usePreparedStatement($usePreparedStatement) {
+               $this->usePreparedStatement = $usePreparedStatement;
+               return $this;
+       }
+
+       /**
+        * @return bool
+        */
+       public function getUsePreparedStatement() {
+               return (bool)$this->usePreparedStatement;
+       }
 }