From 08b2c458bc27c2689c33895ae94a52b6b02789ef Mon Sep 17 00:00:00 2001 From: Felix Oertel <felix@oer.tel> Date: Sun, 2 Feb 2014 00:25:12 +0100 Subject: [PATCH] [BUGFIX] Make Typo3DbBackend use DatabaseConnection While Typo3DbBackend claims to be Extbase's connection to the TYPO3 API, in reality it writes a lot of SQL state- ments itself. This replaces self-written statements with the according DatabaseConnection API call. Releases: 6.2 Resolves: #55571 Change-Id: Ia4f6ef0aadda16b6c5e89c7b36c8f91b185aac25 Reviewed-on: https://review.typo3.org/27254 Reviewed-by: Wouter Wolters Tested-by: Wouter Wolters Reviewed-by: Tymoteusz Motylewski Tested-by: Tymoteusz Motylewski Reviewed-by: Mathias Brodala Reviewed-by: Stefan Neufeind Tested-by: Stefan Neufeind --- .../Generic/Storage/BackendInterface.php | 20 +- .../Generic/Storage/Typo3DbBackend.php | 205 ++++++++---------- 2 files changed, 106 insertions(+), 119 deletions(-) diff --git a/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/BackendInterface.php b/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/BackendInterface.php index 18327aa245da..3ce7e072298a 100644 --- a/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/BackendInterface.php +++ b/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/BackendInterface.php @@ -36,50 +36,50 @@ interface BackendInterface { * Adds a row to the storage * * @param string $tableName The database table name - * @param array $row The row to insert + * @param array $fieldValues The fieldValues to insert * @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default * @return integer the UID of the inserted row */ - public function addRow($tableName, array $row, $isRelation = FALSE); + public function addRow($tableName, array $fieldValues, $isRelation = FALSE); /** * Updates a row in the storage * * @param string $tableName The database table name - * @param array $row The row to update + * @param array $fieldValues The fieldValues to update * @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default * @return mixed|void */ - public function updateRow($tableName, array $row, $isRelation = FALSE); + public function updateRow($tableName, array $fieldValues, $isRelation = FALSE); /** * Updates a relation row in the storage * * @param string $tableName The database relation table name - * @param array $row The row to be updated + * @param array $fieldValues The fieldValues to be updated * @return boolean */ - public function updateRelationTableRow($tableName, array $row); + public function updateRelationTableRow($tableName, array $fieldValues); /** * Deletes a row in the storage * * @param string $tableName The database table name - * @param array $identifier An array of identifier array('fieldname' => value). This array will be transformed to a WHERE clause + * @param array $where An array of where array('fieldname' => value). This array will be transformed to a WHERE clause * @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default * @return mixed|void */ - public function removeRow($tableName, array $identifier, $isRelation = FALSE); + public function removeRow($tableName, array $where, $isRelation = FALSE); /** * Fetches maximal value for given table column * * @param string $tableName The database table name - * @param array $identifier An array of identifier array('fieldname' => value). This array will be transformed to a WHERE clause + * @param array $where An array of where array('fieldname' => value). This array will be transformed to a WHERE clause * @param string $columnName column name to get the max value from * @return mixed the max value */ - public function getMaxValueFromTable($tableName, $identifier, $columnName); + public function getMaxValueFromTable($tableName, array $where, $columnName); /** * Returns the number of items matching the query. diff --git a/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbBackend.php b/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbBackend.php index 10a563956151..4e985385414f 100644 --- a/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbBackend.php +++ b/typo3/sysext/extbase/Classes/Persistence/Generic/Storage/Typo3DbBackend.php @@ -114,28 +114,19 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B * Adds a row to the storage * * @param string $tableName The database table name - * @param array $row The row to be inserted - * @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default + * @param array $fieldValues The row to be inserted + * @param bool $isRelation TRUE if we are currently inserting into a relation table, FALSE by default * @return integer The uid of the inserted row */ - public function addRow($tableName, array $row, $isRelation = FALSE) { - $fields = array(); - $values = array(); - $parameters = array(); - if (isset($row['uid'])) { - unset($row['uid']); + public function addRow($tableName, array $fieldValues, $isRelation = FALSE) { + if (isset($fieldValues['uid'])) { + unset($fieldValues['uid']); } - foreach ($row as $columnName => $value) { - $fields[] = $columnName; - $values[] = '?'; - $parameters[] = $value; - } - $sqlString = 'INSERT INTO ' . $tableName . ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $values) . ')'; - $this->replacePlaceholders($sqlString, $parameters, $tableName); - // debug($sqlString,-2); - $this->databaseHandle->sql_query($sqlString); - $this->checkSqlErrors($sqlString); + + $this->databaseHandle->exec_INSERTquery($tableName, $fieldValues); + $this->checkSqlErrors(); $uid = $this->databaseHandle->sql_insert_id(); + if (!$isRelation) { $this->clearPageCache($tableName, $uid); } @@ -146,141 +137,137 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B * Updates a row in the storage * * @param string $tableName The database table name - * @param array $row The row to be updated - * @param boolean $isRelation TRUE if we are currently inserting into a relation table, FALSE by default + * @param array $fieldValues The row to be updated + * @param bool $isRelation TRUE if we are currently inserting into a relation table, FALSE by default * @throws \InvalidArgumentException - * @return boolean + * @return bool */ - public function updateRow($tableName, array $row, $isRelation = FALSE) { - if (!isset($row['uid'])) { + public function updateRow($tableName, array $fieldValues, $isRelation = FALSE) { + if (!isset($fieldValues['uid'])) { throw new \InvalidArgumentException('The given row must contain a value for "uid".'); } - $uid = (int)$row['uid']; - unset($row['uid']); - $fields = array(); - $parameters = array(); - foreach ($row as $columnName => $value) { - $fields[] = $columnName . '=?'; - $parameters[] = $value; - } - $parameters[] = $uid; - $sqlString = 'UPDATE ' . $tableName . ' SET ' . implode(', ', $fields) . ' WHERE uid=?'; - $this->replacePlaceholders($sqlString, $parameters, $tableName); - // debug($sqlString,-2); - $returnValue = $this->databaseHandle->sql_query($sqlString); - $this->checkSqlErrors($sqlString); + + $uid = (int)$fieldValues['uid']; + unset($fieldValues['uid']); + + $updateSuccessful = $this->databaseHandle->exec_UPDATEquery($tableName, 'uid = '. $uid, $fieldValues); + $this->checkSqlErrors(); + if (!$isRelation) { $this->clearPageCache($tableName, $uid); } - return $returnValue; + + return $updateSuccessful; } /** * Updates a relation row in the storage. * * @param string $tableName The database relation table name - * @param array $row The row to be updated + * @param array $fieldValues The row to be updated * @throws \InvalidArgumentException - * @return boolean + * @return bool */ - public function updateRelationTableRow($tableName, array $row) { - if (!isset($row['uid_local']) && !isset($row['uid_foreign'])) { + public function updateRelationTableRow($tableName, array $fieldValues) { + if (!isset($fieldValues['uid_local']) && !isset($fieldValues['uid_foreign'])) { throw new \InvalidArgumentException( - 'The given row must contain a value for "uid_local" and "uid_foreign".', 1360500126 + 'The given fieldValues must contain a value for "uid_local" and "uid_foreign".', 1360500126 ); } - $uidLocal = (int)$row['uid_local']; - $uidForeign = (int)$row['uid_foreign']; - unset($row['uid_local']); - unset($row['uid_foreign']); - $fields = array(); - $parameters = array(); - foreach ($row as $columnName => $value) { - $fields[] = $columnName . '=?'; - $parameters[] = $value; - } - $parameters[] = $uidLocal; - $parameters[] = $uidForeign; - $sqlString = 'UPDATE ' . $tableName . ' SET ' . implode(', ', $fields) . ' WHERE uid_local=? AND uid_foreign=?'; - $this->replacePlaceholders($sqlString, $parameters); + $where['uid_local'] = (int)$fieldValues['uid_local']; + $where['uid_foreign'] = (int)$fieldValues['uid_foreign']; + unset($fieldValues['uid_local']); + unset($fieldValues['uid_foreign']); - $returnValue = $this->databaseHandle->sql_query($sqlString); - $this->checkSqlErrors($sqlString); + $updateSuccessful = $this->databaseHandle->exec_UPDATEquery( + $tableName, + $this->resolveWhereStatement($where, $tableName), + $fieldValues + ); + $this->checkSqlErrors(); - return $returnValue; + return $updateSuccessful; } /** * Deletes a row in the storage * * @param string $tableName The database table name - * @param array $identifier An array of identifier array('fieldname' => value). This array will be transformed to a WHERE clause - * @param boolean $isRelation TRUE if we are currently manipulating a relation table, FALSE by default - * @return boolean + * @param array $where An array of where array('fieldname' => value). + * @param bool $isRelation TRUE if we are currently manipulating a relation table, FALSE by default + * @return bool */ - public function removeRow($tableName, array $identifier, $isRelation = FALSE) { - $statement = 'DELETE FROM ' . $tableName . ' WHERE ' . $this->parseIdentifier($identifier); - $this->replacePlaceholders($statement, $identifier, $tableName); - if (!$isRelation && isset($identifier['uid'])) { - $this->clearPageCache($tableName, $identifier['uid'], $isRelation); + public function removeRow($tableName, array $where, $isRelation = FALSE) { + $deleteSuccessful = $this->databaseHandle->exec_DELETEquery( + $tableName, + $this->resolveWhereStatement($where, $tableName) + ); + $this->checkSqlErrors(); + + if (!$isRelation && isset($where['uid'])) { + $this->clearPageCache($tableName, $where['uid']); } - // debug($statement, -2); - $returnValue = $this->databaseHandle->sql_query($statement); - $this->checkSqlErrors($statement); - return $returnValue; + + return $deleteSuccessful; } /** * Fetches maximal value for given table column from database. * * @param string $tableName The database table name - * @param array $identifier An array of identifier array('fieldname' => value). This array will be transformed to a WHERE clause + * @param array $where An array of where array('fieldname' => value). * @param string $columnName column name to get the max value from * @return mixed the max value */ - public function getMaxValueFromTable($tableName, $identifier, $columnName) { - $sqlString = 'SELECT ' . $columnName . ' FROM ' . $tableName . ' WHERE ' . $this->parseIdentifier($identifier) . ' ORDER BY ' . $columnName . ' DESC LIMIT 1'; - $this->replacePlaceholders($sqlString, $identifier); - - $result = $this->databaseHandle->sql_query($sqlString); - $row = $this->databaseHandle->sql_fetch_assoc($result); - $this->checkSqlErrors($sqlString); - return $row[$columnName]; + public function getMaxValueFromTable($tableName, array $where, $columnName) { + $result = $this->databaseHandle->exec_SELECTgetSingleRow( + $columnName, + $tableName, + $this->resolveWhereStatement($where, $tableName), + '', + $columnName . ' DESC', + TRUE + ); + $this->checkSqlErrors(); + + return $result[0]; } /** * Fetches row data from the database * * @param string $tableName - * @param array $identifier The Identifier of the row to fetch - * @return array|boolean + * @param array $where An array of where array('fieldname' => value). + * @return array|bool */ - public function getRowByIdentifier($tableName, array $identifier) { - $statement = 'SELECT * FROM ' . $tableName . ' WHERE ' . $this->parseIdentifier($identifier); - $this->replacePlaceholders($statement, $identifier, $tableName); - // debug($statement,-2); - $res = $this->databaseHandle->sql_query($statement); - $this->checkSqlErrors($statement); - $row = $this->databaseHandle->sql_fetch_assoc($res); - if ($row !== FALSE) { - return $row; - } else { - return FALSE; - } + public function getRowByIdentifier($tableName, array $where) { + $row = $this->databaseHandle->exec_SELECTgetSingleRow( + '*', + $tableName, + $this->resolveWhereStatement($where, $tableName) + ); + $this->checkSqlErrors(); + + return $row ?: FALSE; } /** - * @param array $identifier + * Converts an array to an AND concatenated where statement + * + * @param array $where array('fieldName' => 'fieldValue') + * @param string $tableName table to use for escaping config + * * @return string */ - protected function parseIdentifier(array $identifier) { - $fieldNames = array_keys($identifier); - $suffixedFieldNames = array(); - foreach ($fieldNames as $fieldName) { - $suffixedFieldNames[] = $fieldName . '=?'; + protected function resolveWhereStatement(array $where, $tableName = 'foo') { + $whereStatement = array(); + + foreach ($where as $fieldName => $fieldValue) { + $whereStatement[] = $fieldName . ' = ' . $this->databaseHandle->fullQuoteStr($fieldValue, $tableName); } - return implode(' AND ', $suffixedFieldNames); + + return implode(' AND ', $whereStatement); } /** @@ -483,7 +470,7 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B * @param array &$sql The query parts * @return void */ - protected function addRecordTypeConstraint($className, &$sql) { + protected function addRecordTypeConstraint($className, array &$sql) { if ($className !== NULL) { $dataMap = $this->dataMapper->getDataMap($className); if ($dataMap->getRecordTypeColumnName() !== NULL) { @@ -728,7 +715,7 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B /** * @param string &$className * @param string &$tableName - * @param array &$propertyPath + * @param string &$propertyPath * @param array &$sql * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InvalidRelationConfigurationException @@ -936,13 +923,13 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B * Returns constraint statement for frontend context * * @param string $tableName - * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored + * @param bool $ignoreEnableFields A flag indicating whether the enable fields should be ignored * @param array $enableFieldsToBeIgnored If $ignoreEnableFields is true, this array specifies enable fields to be ignored. If it is NULL or an empty array (default) all enable fields are ignored. - * @param boolean $includeDeleted A flag indicating whether deleted records should be included + * @param bool $includeDeleted A flag indicating whether deleted records should be included * @return string * @throws \TYPO3\CMS\Extbase\Persistence\Generic\Exception\InconsistentQuerySettingsException */ - protected function getFrontendConstraintStatement($tableName, $ignoreEnableFields, $enableFieldsToBeIgnored = array(), $includeDeleted) { + protected function getFrontendConstraintStatement($tableName, $ignoreEnableFields, array $enableFieldsToBeIgnored = array(), $includeDeleted) { $statement = ''; if ($ignoreEnableFields && !$includeDeleted) { if (count($enableFieldsToBeIgnored)) { @@ -963,8 +950,8 @@ class Typo3DbBackend implements \TYPO3\CMS\Extbase\Persistence\Generic\Storage\B * Returns constraint statement for backend context * * @param string $tableName - * @param boolean $ignoreEnableFields A flag indicating whether the enable fields should be ignored - * @param boolean $includeDeleted A flag indicating whether deleted records should be included + * @param bool $ignoreEnableFields A flag indicating whether the enable fields should be ignored + * @param bool $includeDeleted A flag indicating whether deleted records should be included * @return string */ protected function getBackendConstraintStatement($tableName, $ignoreEnableFields, $includeDeleted) { -- GitLab