From e7f471ef3e1ffa5e180623115a45f423572549e4 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Sun, 28 Aug 2011 13:48:15 -0300 Subject: [PATCH] Fixed issue with CTI during DQL update that was incorrectly setting parameter types during multi table execution. Fixes DDC-1341. --- lib/Doctrine/ORM/AbstractQuery.php | 21 +++++++++++++++++++ .../Query/Exec/MultiTableUpdateExecutor.php | 19 ++++++++++++++--- .../Tests/Models/Company/CompanyEmployee.php | 13 ++++++++++++ .../Functional/ClassTableInheritanceTest.php | 15 ++++++++++++- .../Tests/ORM/Mapping/ClassMetadataTest.php | 2 +- .../ORM/Query/SelectSqlGenerationTest.php | 6 +++--- 6 files changed, 68 insertions(+), 8 deletions(-) diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index 88bbfc2c1..5d71cf0aa 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -162,6 +162,16 @@ abstract class AbstractQuery { return $this->_params; } + + /** + * Get all defined parameter types. + * + * @return array The defined query parameter types. + */ + public function getParameterTypes() + { + return $this->_paramTypes; + } /** * Gets a query parameter. @@ -174,6 +184,17 @@ abstract class AbstractQuery return isset($this->_params[$key]) ? $this->_params[$key] : null; } + /** + * Gets a query parameter type. + * + * @param mixed $key The key (index or name) of the bound parameter. + * @return mixed The parameter type of the bound parameter. + */ + public function getParameterType($key) + { + return isset($this->_paramTypes[$key]) ? $this->_paramTypes[$key] : null; + } + /** * Gets the SQL query that corresponds to this query object. * The returned SQL syntax depends on the connection driver that is used diff --git a/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php b/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php index d5cd9fb81..7becaec98 100644 --- a/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php @@ -106,7 +106,8 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor //FIXME (URGENT): With query cache the parameter is out of date. Move to execute() stage. if ($newValue instanceof AST\InputParameter) { $paramKey = $newValue->name; - $this->_sqlParameters[$i][] = $sqlWalker->getQuery()->getParameter($paramKey); + $this->_sqlParameters[$i]['parameters'][] = $sqlWalker->getQuery()->getParameter($paramKey); + $this->_sqlParameters[$i]['types'][] = $sqlWalker->getQuery()->getParameterType($paramKey); ++$this->_numParametersInUpdateClause; } @@ -154,11 +155,23 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor $conn->executeUpdate($this->_createTempTableSql); // Insert identifiers. Parameters from the update clause are cut off. - $numUpdated = $conn->executeUpdate($this->_insertSql, array_slice($params, $this->_numParametersInUpdateClause), $types); + $numUpdated = $conn->executeUpdate( + $this->_insertSql, + array_slice($params, $this->_numParametersInUpdateClause), + array_slice($types, $this->_numParametersInUpdateClause) + ); // Execute UPDATE statements for ($i=0, $count=count($this->_sqlStatements); $i<$count; ++$i) { - $conn->executeUpdate($this->_sqlStatements[$i], isset($this->_sqlParameters[$i]) ? $this->_sqlParameters[$i] : array()); + $parameters = array(); + $types = array(); + + if (isset($this->_sqlParameters[$i])) { + $parameters = isset($this->_sqlParameters[$i]['parameters']) ? $this->_sqlParameters[$i]['parameters'] : array(); + $types = isset($this->_sqlParameters[$i]['types']) ? $this->_sqlParameters[$i]['types'] : array(); + } + + $conn->executeUpdate($this->_sqlStatements[$i], $parameters, $types); } // Drop temporary table diff --git a/tests/Doctrine/Tests/Models/Company/CompanyEmployee.php b/tests/Doctrine/Tests/Models/Company/CompanyEmployee.php index 46b66cecc..61be7c5e6 100644 --- a/tests/Doctrine/Tests/Models/Company/CompanyEmployee.php +++ b/tests/Doctrine/Tests/Models/Company/CompanyEmployee.php @@ -17,6 +17,11 @@ class CompanyEmployee extends CompanyPerson * @Column(type="string", length=255) */ private $department; + + /** + * @Column(type="datetime", nullable=true) + */ + private $startDate; public function getSalary() { return $this->salary; @@ -33,4 +38,12 @@ class CompanyEmployee extends CompanyPerson public function setDepartment($dep) { $this->department = $dep; } + + public function getStartDate() { + return $this->startDate; + } + + public function setStartDate($date) { + $this->startDate = $date; + } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php b/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php index 3456d4ba0..e8768e7ff 100644 --- a/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/ClassTableInheritanceTest.php @@ -291,8 +291,21 @@ class ClassTableInheritanceTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertTrue(count($this->_em->createQuery( 'SELECT count(p.id) FROM Doctrine\Tests\Models\Company\CompanyEmployee p WHERE p.salary = 1') - ->getResult()) > 0); + ->getResult()) > 0); + } + + /** + * @group DDC-1341 + */ + public function testBulkUpdateNonScalarParameterDDC1341() + { + $dql = 'UPDATE Doctrine\Tests\Models\Company\CompanyEmployee AS p SET p.startDate = ?0 WHERE p.department = ?1'; + $query = $this->_em->createQuery($dql) + ->setParameter(0, new \DateTime()) + ->setParameter(1, 'IT'); + $result = $query->execute(); + } /** diff --git a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php index 7868fb2fb..d16ec19f0 100644 --- a/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/Tests/ORM/Mapping/ClassMetadataTest.php @@ -45,7 +45,7 @@ class ClassMetadataTest extends \Doctrine\Tests\OrmTestCase $this->assertEquals('UserParent', $cm->rootEntityName); $this->assertEquals(array('Doctrine\Tests\Models\CMS\One', 'Doctrine\Tests\Models\CMS\Two', 'Doctrine\Tests\Models\CMS\Three'), $cm->subClasses); $this->assertEquals(array('UserParent'), $cm->parentClasses); - $this->assertEquals('UserRepository', $cm->customRepositoryClassName); + $this->assertEquals('Doctrine\Tests\Models\CMS\UserRepository', $cm->customRepositoryClassName); $this->assertEquals(array('name' => 'disc', 'type' => 'integer', 'fieldName' => 'disc'), $cm->discriminatorColumn); $this->assertTrue($cm->associationMappings['phonenumbers']['type'] == ClassMetadata::ONE_TO_ONE); $this->assertEquals(1, count($cm->associationMappings)); diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 4c081402d..f132296fe 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -418,7 +418,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase { $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\Company\CompanyEmployee u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyManager", - "SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c0_.discr AS discr4 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id WHERE c0_.discr IN ('manager')" + "SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c0_.discr AS discr5 FROM company_employees c1_ INNER JOIN company_persons c0_ ON c1_.id = c0_.id WHERE c0_.discr IN ('manager')" ); } @@ -426,7 +426,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase { $this->assertSqlGeneration( "SELECT u FROM Doctrine\Tests\Models\Company\CompanyManager u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyManager", - "SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c2_.title AS title4, c0_.discr AS discr5 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id WHERE c0_.discr IN ('manager')" + "SELECT c0_.id AS id0, c0_.name AS name1, c1_.salary AS salary2, c1_.department AS department3, c1_.startDate AS startDate4, c2_.title AS title5, c0_.discr AS discr6 FROM company_managers c2_ INNER JOIN company_employees c1_ ON c2_.id = c1_.id INNER JOIN company_persons c0_ ON c2_.id = c0_.id WHERE c0_.discr IN ('manager')" ); } @@ -563,7 +563,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase $this->_em->getClassMetadata(get_class($person))->setIdentifierValues($person, array('id' => 101)); $q3->setParameter('param', $person); $this->assertEquals( - 'SELECT c0_.id AS id0, c0_.name AS name1, c1_.title AS title2, c1_.car_id AS car_id3, c2_.salary AS salary4, c2_.department AS department5, c0_.discr AS discr6, c0_.spouse_id AS spouse_id7 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE EXISTS (SELECT 1 FROM company_persons_friends c3_ INNER JOIN company_persons c4_ ON c3_.friend_id = c4_.id WHERE c3_.person_id = c0_.id AND c4_.id = ?)', + 'SELECT c0_.id AS id0, c0_.name AS name1, c1_.title AS title2, c1_.car_id AS car_id3, c2_.salary AS salary4, c2_.department AS department5, c2_.startDate AS startDate6, c0_.discr AS discr7, c0_.spouse_id AS spouse_id8 FROM company_persons c0_ LEFT JOIN company_managers c1_ ON c0_.id = c1_.id LEFT JOIN company_employees c2_ ON c0_.id = c2_.id WHERE EXISTS (SELECT 1 FROM company_persons_friends c3_ INNER JOIN company_persons c4_ ON c3_.friend_id = c4_.id WHERE c3_.person_id = c0_.id AND c4_.id = ?)', $q3->getSql() ); }