[DDC-723] Upgrade ORM to depend on DBAL Beta3 Created: 28/Jul/10 Updated: 28/Jul/10 Resolved: 28/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA2 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Improvement | Priority: | Major |
| Reporter: | Benjamin Eberlei | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
We need to raise the dependency of ORM to DBAL BETA 3 before the ORM Beta 3 release. |
| Comments |
| Comment by Benjamin Eberlei [ 28/Jul/10 ] |
|
Done! |
[DDC-719] Error in SQL subquery for a ManyToMany selfreferencing enitity when using the SIZE() or IS EMPTY dql function Created: 25/Jul/10 Updated: 07/Aug/10 Resolved: 07/Aug/10 |
|
| Status: | Closed |
| Project: | Doctrine 2 - ORM |
| Component/s: | DQL, Mapping Drivers, ORM |
| Affects Version/s: | 2.0-BETA2 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Steffen Vogel | Assignee: | Guilherme Blanco |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Environment: |
PHP 5.3, Ubuntu Lucid, MySQL 5, Apache 2.2 |
||
| Attachments: |
|
| Description |
|
I have an entity (Group) with a self-referencing ManyToMany association (parents, children). My DQL: SELECT g, c, d FROM Volkszaehler\Model\Group g LEFT JOIN g.children c LEFT JOIN g.channels d WHERE g.parents IS EMPTY throws this PDOException: object(PDOException)#31 (8) {
["message":protected]=>
string(89) "SQLSTATE[42S02]: Base table or view not found: 1146 Table 'volkszaehler.5_' doesn't exist"
["string":"Exception":private]=>
string(0) ""
["code":protected]=>
string(5) "42S02"
["file":protected]=>
string(90) "/home/steffen/workspace/doctrine/lib/vendor/doctrine-dbal/lib/Doctrine/DBAL/Connection.php"
["line":protected]=>
int(568)
["trace":"Exception":private]=>
array(9) {
[0]=>
array(6) {
["file"]=>
string(90) "/home/steffen/workspace/doctrine/lib/vendor/doctrine-dbal/lib/Doctrine/DBAL/Connection.php"
["line"]=>
int(568)
["function"]=>
string(5) "query"
["class"]=>
string(3) "PDO"
["type"]=>
string(2) "->"
["args"]=>
array(1) {
[0]=>
string(604) "SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g0_.uuid AS uuid3, g1_.name AS name4, g1_.description AS description5, g1_.id AS id6, g1_.uuid AS uuid7, c2_.name AS name8, c2_.description AS description9, c2_.indicator AS indicator10, c2_.resolution AS resolution11, c2_.cost AS cost12, c2_.id AS id13, c2_.uuid AS uuid14 FROM groups g0_ LEFT JOIN groups_groups g3_ ON g0_.id = g3_.parent_id LEFT JOIN groups g1_ ON g1_.id = g3_.child_id LEFT JOIN groups_channel g4_ ON g0_.id = g4_.group_id LEFT JOIN channels c2_ ON c2_.id = g4_.channel_id WHERE (SELECT COUNT(*) FROM 5_) = 0"
}
}
[1]=>
array(6) {
["file"]=>
string(85) "/home/steffen/workspace/doctrine/lib/Doctrine/ORM/Query/Exec/SingleSelectExecutor.php"
["line"]=>
int(46)
["function"]=>
string(12) "executeQuery"
["class"]=>
string(24) "Doctrine\DBAL\Connection"
["type"]=>
string(2) "->"
["args"]=>
array(3) {
[0]=>
string(604) "SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g0_.uuid AS uuid3, g1_.name AS name4, g1_.description AS description5, g1_.id AS id6, g1_.uuid AS uuid7, c2_.name AS name8, c2_.description AS description9, c2_.indicator AS indicator10, c2_.resolution AS resolution11, c2_.cost AS cost12, c2_.id AS id13, c2_.uuid AS uuid14 FROM groups g0_ LEFT JOIN groups_groups g3_ ON g0_.id = g3_.parent_id LEFT JOIN groups g1_ ON g1_.id = g3_.child_id LEFT JOIN groups_channel g4_ ON g0_.id = g4_.group_id LEFT JOIN channels c2_ ON c2_.id = g4_.channel_id WHERE (SELECT COUNT(*) FROM 5_) = 0"
[1]=>
array(0) {
}
[2]=>
array(0) {
}
}
}
[2]=>
array(6) {
["file"]=>
string(59) "/home/steffen/workspace/doctrine/lib/Doctrine/ORM/Query.php"
["line"]=>
int(265)
["function"]=>
string(7) "execute"
["class"]=>
string(44) "Doctrine\ORM\Query\Exec\SingleSelectExecutor"
["type"]=>
string(2) "->"
["args"]=>
array(3) {
[0]=>
object(Doctrine\DBAL\Connection)#16 (11) {
["_conn":protected]=>
object(Doctrine\DBAL\Driver\PDOConnection)#29 (0) {
}
["_config":protected]=>
object(Doctrine\ORM\Configuration)#7 (1) {
["_attributes":protected]=>
array(7) {
["metadataCacheImpl"]=>
object(Doctrine\Common\Cache\ApcCache)#8 (2) {
["_cacheIdsIndexId":"Doctrine\Common\Cache\AbstractCache":private]=>
string(18) "doctrine_cache_ids"
["_namespace":"Doctrine\Common\Cache\AbstractCache":private]=>
NULL
}
["queryCacheImpl"]=>
object(Doctrine\Common\Cache\ApcCache)#8 (2) {
["_cacheIdsIndexId":"Doctrine\Common\Cache\AbstractCache":private]=>
string(18) "doctrine_cache_ids"
["_namespace":"Doctrine\Common\Cache\AbstractCache":private]=>
NULL
}
["metadataDriverImpl"]=>
object(Doctrine\ORM\Mapping\Driver\AnnotationDriver)#13 (4) {
["_reader":"Doctrine\ORM\Mapping\Driver\AnnotationDriver":private]=>
object(Doctrine\Common\Annotations\AnnotationReader)#9 (2) {
["parser":"Doctrine\Common\Annotations\AnnotationReader":private]=>
object(Doctrine\Common\Annotations\Parser)#10 (6) {
["lexer":"Doctrine\Common\Annotations\Parser":private]=>
object(Doctrine\Common\Annotations\Lexer)#11 (5) {
["tokens":"Doctrine\Common\Lexer":private]=>
array(0) {
}
["position":"Doctrine\Common\Lexer":private]=>
int(0)
["peek":"Doctrine\Common\Lexer":private]=>
int(0)
["lookahead"]=>
NULL
["token"]=>
NULL
}
["isNestedAnnotation":"Doctrine\Common\Annotations\Parser":private]=>
bool(false)
["defaultAnnotationNamespace":"Doctrine\Common\Annotations\Parser":private]=>
string(21) "Doctrine\ORM\Mapping\"
["namespaceAliases":"Doctrine\Common\Annotations\Parser":private]=>
array(0) {
}
["context":"Doctrine\Common\Annotations\Parser":private]=>
string(0) ""
["autoloadAnnotations":"Doctrine\Common\Annotations\Parser":private]=>
bool(false)
}
["cache":"Doctrine\Common\Annotations\AnnotationReader":private]=>
object(Doctrine\Common\Cache\ArrayCache)#12 (3) {
["data":"Doctrine\Common\Cache\ArrayCache":private]=>
array(0) {
}
["_cacheIdsIndexId":"Doctrine\Common\Cache\AbstractCache":private]=>
string(18) "doctrine_cache_ids"
["_namespace":"Doctrine\Common\Cache\AbstractCache":private]=>
NULL
}
}
["_paths":protected]=>
array(1) {
[0]=>
string(58) "/home/steffen/workspace/volkszaehler.org/backend/lib/Model"
}
["_fileExtension":protected]=>
string(4) ".php"
["_classNames":protected]=>
NULL
}
["proxyDir"]=>
string(66) "/home/steffen/workspace/volkszaehler.org/backend/lib/Model/Proxies"
["proxyNamespace"]=>
string(26) "Volkszaehler\Model\Proxies"
["autoGenerateProxyClasses"]=>
bool(true)
["sqlLogger"]=>
object(Volkszaehler\Util\Debug)#22 (5) {
["queries":protected]=>
array(1) {
[0]=>
array(2) {
["sql"]=>
string(604) "SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g0_.uuid AS uuid3, g1_.name AS name4, g1_.description AS description5, g1_.id AS id6, g1_.uuid AS uuid7, c2_.name AS name8, c2_.description AS description9, c2_.indicator AS indicator10, c2_.resolution AS resolution11, c2_.cost AS cost12, c2_.id AS id13, c2_.uuid AS uuid14 FROM groups g0_ LEFT JOIN groups_groups g3_ ON g0_.id = g3_.parent_id LEFT JOIN groups g1_ ON g1_.id = g3_.child_id LEFT JOIN groups_channel g4_ ON g0_.id = g4_.group_id LEFT JOIN channels c2_ ON c2_.id = g4_.channel_id WHERE (SELECT COUNT(*) FROM 5_) = 0"
["parameters"]=>
array(0) {
}
}
}
["messages":protected]=>
array(0) {
}
["started":protected]=>
NULL
["level":protected]=>
string(1) "1"
["created"]=>
float(1280063214.6367)
}
}
}
["_eventManager":protected]=>
object(Doctrine\Common\EventManager)#14 (1) {
["_listeners":"Doctrine\Common\EventManager":private]=>
array(0) {
}
}
["_isConnected":"Doctrine\DBAL\Connection":private]=>
bool(true)
["_transactionNestingLevel":"Doctrine\DBAL\Connection":private]=>
int(0)
["_transactionIsolationLevel":"Doctrine\DBAL\Connection":private]=>
int(2)
["_params":"Doctrine\DBAL\Connection":private]=>
array(5) {
["driver"]=>
string(9) "pdo_mysql"
["host"]=>
string(9) "localhost"
["user"]=>
string(2) "vz"
["password"]=>
string(4) "demo"
["dbname"]=>
string(12) "volkszaehler"
}
["_platform":protected]=>
object(Doctrine\DBAL\Platforms\MySqlPlatform)#17 (1) {
["doctrineTypeMapping":protected]=>
NULL
}
["_schemaManager":protected]=>
NULL
["_driver":protected]=>
object(Doctrine\DBAL\Driver\PDOMySql\Driver)#15 (0) {
}
["_isRollbackOnly":"Doctrine\DBAL\Connection":private]=>
bool(false)
}
[1]=>
array(0) {
}
[2]=>
array(0) {
}
}
}
[3]=>
array(6) {
["file"]=>
string(67) "/home/steffen/workspace/doctrine/lib/Doctrine/ORM/AbstractQuery.php"
["line"]=>
int(522)
["function"]=>
string(10) "_doExecute"
["class"]=>
string(18) "Doctrine\ORM\Query"
["type"]=>
string(2) "->"
["args"]=>
array(0) {
}
}
[4]=>
array(6) {
["file"]=>
string(67) "/home/steffen/workspace/doctrine/lib/Doctrine/ORM/AbstractQuery.php"
["line"]=>
int(360)
["function"]=>
string(7) "execute"
["class"]=>
string(26) "Doctrine\ORM\AbstractQuery"
["type"]=>
string(2) "->"
["args"]=>
array(2) {
[0]=>
array(0) {
}
[1]=>
int(1)
}
}
[5]=>
array(6) {
["file"]=>
string(83) "/home/steffen/workspace/volkszaehler.org/backend/lib/Controller/GroupController.php"
["line"]=>
int(57)
["function"]=>
string(9) "getResult"
["class"]=>
string(26) "Doctrine\ORM\AbstractQuery"
["type"]=>
string(2) "->"
["args"]=>
array(0) {
}
}
[6]=>
array(6) {
["file"]=>
string(78) "/home/steffen/workspace/volkszaehler.org/backend/lib/Controller/Controller.php"
["line"]=>
int(54)
["function"]=>
string(3) "get"
["class"]=>
string(39) "Volkszaehler\Controller\GroupController"
["type"]=>
string(2) "->"
["args"]=>
array(0) {
}
}
[7]=>
array(6) {
["file"]=>
string(67) "/home/steffen/workspace/volkszaehler.org/backend/lib/Dispatcher.php"
["line"]=>
int(149)
["function"]=>
string(3) "run"
["class"]=>
string(34) "Volkszaehler\Controller\Controller"
["type"]=>
string(2) "->"
["args"]=>
array(1) {
[0]=>
string(3) "get"
}
}
[8]=>
array(6) {
["file"]=>
string(58) "/home/steffen/workspace/volkszaehler.org/backend/index.php"
["line"]=>
int(55)
["function"]=>
string(3) "run"
["class"]=>
string(23) "Volkszaehler\Dispatcher"
["type"]=>
string(2) "->"
["args"]=>
array(0) {
}
}
}
["previous":"Exception":private]=>
NULL
["errorInfo"]=>
array(3) {
[0]=>
string(5) "42S02"
[1]=>
int(1146)
[2]=>
string(37) "Table 'volkszaehler.5_' doesn't exist"
}
}
Here is my Group Entitiy: namespace Volkszaehler\Model; use Doctrine\Common\Collections; use Doctrine\Common\Collections\ArrayCollection; /** * Group entity * * @author Steffen Vogel <info@steffenvogel.de> * @package default * * @Entity * @Table(name="groups") */ class Group extends Entity { /** @Column(type="string", nullable=false) */ protected $name; /** @Column(type="string", nullable=true) */ protected $description; /** * @ManyToMany(targetEntity="Channel", inversedBy="groups") * @JoinTable(name="groups_channel", * joinColumns={@JoinColumn(name="group_id", referencedColumnName="id")}, * inverseJoinColumns={@JoinColumn(name="channel_id", referencedColumnName="id")} * ) */ protected $channels = NULL; /** * @ManyToMany(targetEntity="Group", inversedBy="parents") * @JoinTable(name="groups_groups", * joinColumns={@JoinColumn(name="parent_id", referencedColumnName="id")}, * inverseJoinColumns={@JoinColumn(name="child_id", referencedColumnName="id")} * ) */ protected $children = NULL; /** * @ManyToMany(targetEntity="Group", mappedBy="children") */ protected $parents = NULL; /** * construct */ public function __construct() { parent::__construct(); $this->channels = new ArrayCollection(); $this->children = new ArrayCollection(); $this->parents = new ArrayCollection(); } /** * adds group as new child * * @param Group $child * @todo check against endless recursion * @todo check if the group is already member of the group */ public function addGroup(Group $child) { $this->children->add($child); } /** * adds channel as new child * * @param Channel $child * @todo check if the channel is already member of the group */ public function addChannel(Channel $child) { $this->channels->add($child); } /** * getter & setter */ public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } public function getDescription() { return $this->description; } public function setDescription($description) { $this->description = $description; } public function getChildren() { return $this->children; } public function getParents() { return $this->parents; } public function getChannels() { return $this->channels; } } |
| Comments |
| Comment by Benjamin Eberlei [ 25/Jul/10 ] |
|
Looks like abug in the SQL Walker. btw, you can use $e->getTraceAsString() to get a nice looking output for an exception. Its not as verbose as var_dump on the exception |
| Comment by Guilherme Blanco [ 03/Aug/10 ] |
|
No, it is a bug on ClassMetadata. The var_dump on Association of parents refers to a NULL on joinTable. |
| Comment by Guilherme Blanco [ 03/Aug/10 ] |
|
Ok, it seems that on Mapping drivers we don't map jointables on opposite side. So, if you map something on inversedBy and you grab the association from mappedBy side, you'll never have the jointable definition, because it is not exported to us. |
| Comment by Roman S. Borschel [ 03/Aug/10 ] |
|
That the jointable info is only on the owning side is by design, not a flaw. |
| Comment by Guilherme Blanco [ 03/Aug/10 ] |
|
Path to |
| Comment by Guilherme Blanco [ 06/Aug/10 ] |
|
In http://github.com/doctrine/doctrine2/commit/35af98260a525a841c05be15f52f8df455000066 I committed a fix to this issue. |
| Comment by Roman S. Borschel [ 07/Aug/10 ] |
|
Reopening in order to correct the fixed version. |
| Comment by Roman S. Borschel [ 07/Aug/10 ] |
|
Closing with correct version. |
[DDC-716] Proxy autogeneration fails with concurrent requests Created: 22/Jul/10 Updated: 24/Jul/10 Resolved: 24/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA2 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Jaka Jancar | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Environment: |
Debian 5.0.5, Apache 2.2.9, PHP 5.3.2 |
||
| Description |
|
When doing concurrent requests with autogeneration of proxies enabled, the proxy file does not exist when ProxyFactory tries to use it: Doctrine/ORM/Proxy/ProxyFactory.php(92): spl_autoload_call('MyClassProxy'))
I think this is because file_put_contents is not atomic. |
| Comments |
| Comment by Jaka Jancar [ 22/Jul/10 ] |
|
The following fixes it on Linux, but I think will not work in Windows (iirc, rename() on Windows won't work if the dest file already exists, much less atomically):
--- Proxy/ProxyFactory.php (revision 2)
+++ Proxy/ProxyFactory.php (working copy)
@@ -144,7 +144,9 @@
$file = str_replace($placeholders, $replacements, $file);
- file_put_contents($fileName, $file);
+ $tmpFileName = $fileName.'-'.uniqid('', true);;
+ file_put_contents($tmpFileName, $file);
+ rename($tmpFileName, $fileName);
}
/**
|
| Comment by Jaka Jancar [ 22/Jul/10 ] |
|
I don't even know why this is needed. Can't the file just be returned as string and eval()'d, instead of being written to a file and then require()'d? |
| Comment by Benjamin Eberlei [ 22/Jul/10 ] |
|
we should just change file_put_Contents into: file_put_contents($fileName, $file, LOCK_EX); |
| Comment by Benjamin Eberlei [ 22/Jul/10 ] |
|
Setting the LOCK_EX constant now, this should solve the issue. However in high concurrency scenarios the "autoGenerateProxyClasses" flag should always be FALSE and the proxies be generated during build-time. |
| Comment by Jaka Jancar [ 22/Jul/10 ] |
|
LOCK_EX doesn't fix it for me. Apparently it's still possible that the file doesn't exist: E_WARNING (2): include(MyProxies/MyClassProxy.php) [<a href='function.include'>function.include</a>]: failed to open stream: No such file or directory Please note that my above uniqid+rename suggestion only works if more_entropy is true, if you decide to add it. However, I'd much prefer having an option of just not using these files at all. I've created an Improvement ticket DDC-717 for this. |
| Comment by Benjamin Eberlei [ 23/Jul/10 ] |
|
i now underestand what you are doing wrong. if you set autogenerate = false you have to call doctrine orm:generate-proxies |
| Comment by Jaka Jancar [ 23/Jul/10 ] |
|
I'm not setting it to false! > When doing concurrent requests with autogeneration of proxies enabled |
| Comment by Benjamin Eberlei [ 23/Jul/10 ] |
|
ah ok, eval is just a workaround. |
| Comment by Benjamin Eberlei [ 24/Jul/10 ] |
|
Closed again, See DDC-717 for a timetable of a fix using auto-generate = true in production. |
[DDC-714] Fix of DDC-167 creates FatalError when persisting a new entity Created: 22/Jul/10 Updated: 25/Jul/10 Resolved: 25/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA3, 2.0-BETA4 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Blocker |
| Reporter: | Michael Zach | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Environment: |
Debian 5 (64bit), Postgresql 8.3, ZendServer 5.0.2, PHP 5.3.2, Doctrine-HEAD |
||
| Attachments: |
|
| Description |
|
The resolution of ErrorException with Argument 2 passed to Doctrine\ORM\Mapping\ClassMetadata::setIdentifierValues() must be an array, integer given, called in /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php on line 612 and defined Backtrace: #0: Doctrine\ORM\Mapping\ClassMetadata->setIdentifierValues at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php:612 #1: Doctrine\ORM\UnitOfWork->persistNew at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php:1247 #2: Doctrine\ORM\UnitOfWork->doPersist at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php:1210 #3: Doctrine\ORM\UnitOfWork->persist at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/EntityManager.php:438 The relevant code in UoW is: $idValue = $idGen->generate($this->em, $entity); if ( ! $idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) { $this->entityIdentifiers[$oid] = array($class->identifier[0] => $idValue); $class->setIdentifierValues($entity, $idValue); We're using the SequenceGenerator @SequenceGenerator(allocationSize=1,sequenceName="address_id_seq")
which doesn't return an array, so the array typehint fails and generates an error. The fix, which worked for me, is attached. |
| Comments |
| Comment by Benjamin Eberlei [ 25/Jul/10 ] |
|
This bug also leads to about 400 test failures in the Postgres ORM Testsuite |
| Comment by Benjamin Eberlei [ 25/Jul/10 ] |
|
Fixed! Thanks for reporting. |
[DDC-710] DecimalType - NULL in database should be NULL not 0 Created: 21/Jul/10 Updated: 21/Jul/10 Resolved: 21/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA3 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Patrick Schwisow | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
DecimalType::convertToPHPValue should return NULL if the value in the database is NULL. This is similar to the issue with IntegerType that was fixed with # The fix would be to replace return (double) $value; with return is_null($value) ? null : (double) $value; |
| Comments |
| Comment by Benjamin Eberlei [ 21/Jul/10 ] |
|
Fixed |
[DDC-706] DriverChain::isTransient should return false not throw exception Created: 20/Jul/10 Updated: 21/Jul/10 Resolved: 21/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | Mapping Drivers |
| Affects Version/s: | 2.0-BETA2 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Benjamin Eberlei | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
The isTransient() method has to return false, not throw an exception if the given class is not an entity or mapped superclass. The current behaviour violates the Mapping\Driver contract. |
| Comments |
| Comment by Benjamin Eberlei [ 21/Jul/10 ] |
|
Fixed in master |
[DDC-697] Support for DateTime in query parameters Created: 19/Jul/10 Updated: 21/Jul/10 Resolved: 21/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | DQL |
| Affects Version/s: | None |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | New Feature | Priority: | Major |
| Reporter: | Patrik Votoček | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
Supports native DateTime in query parameters. (for more universal using) Column definition /** * @var DateTime * * @Column(type="datetime") */ private $published; Query $qb = $em->getRepository('Entities\Foo')->createQueryBuilder('f');
$qb->where("f.published <= ?1");
$qb->setParameter(1, new \DateTime('2012-12-21 23:59:59'));
Now display "Object of class DateTime could not be converted to string" error |
| Comments |
| Comment by Benjamin Eberlei [ 19/Jul/10 ] |
|
This is another case for possible optimizations in my opinion, is it possible to access the parameter needles from the ResultSetMapping? If so then we should add a convertToParam method to each Doctrine\DBAL\Types\Type and allow conversions to take place or just do nothing. This woulld help with this issue, aswell as with other more complex types to be bound. |
| Comment by Benjamin Eberlei [ 19/Jul/10 ] |
|
This affects the following loop inside Doctrine\ORM\Query: http://github.com/doctrine/doctrine2/blob/master/lib/Doctrine/ORM/Query.php#L234 |
| Comment by Benjamin Eberlei [ 20/Jul/10 ] |
|
According to Roman this should already be possible with: $qb->setParameter(1, new \DateTime('2012-12-21 23:59:59'), \Doctrine\DBAL\Types\Type::DATETIME);
can you verify this? |
| Comment by Patrik Votoček [ 20/Jul/10 ] |
$qb->setParameter(1, new \DateTime('2012-12-21 23:59:59'), \Doctrine\DBAL\Types\Type::DATETIME);
No this featrue not work for me. (Same error message) I'm tested at latest BETA release & latest code from master branch (GitHub). |
| Comment by Benjamin Eberlei [ 21/Jul/10 ] |
|
Fixed, QueryBuilder::setParameter() and QueryBuilder::setParameters() did not yet support setting parameter types. |
[DDC-693] NULL values from Postgres boolean columns are loaded as FALSE instead of NULL Created: 18/Jul/10 Updated: 21/Jul/10 Resolved: 21/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | None |
| Affects Version/s: | None |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Jan Tichý | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Environment: |
PostgreSQL 8.4.3 |
||
| Attachments: |
|
| Description |
|
I have column in PostgreSQL defined as "BOOLEAN NULL". If I store NULL value to database and then load it back, I get FALSE instead of NULL. Example entity and test attached. I am not sure if this issue applies to ORM or DBAL, so posting here. |
| Comments |
| Comment by Benjamin Eberlei [ 21/Jul/10 ] |
|
Fixed |
[DDC-681] PATCH: UnitOfWork#lock locks by column names instead of field names Created: 10/Jul/10 Updated: 10/Jul/10 Resolved: 10/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA2 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Dennis Verspuij | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Issue Links: |
|
||||||||
| Description |
|
On line 1700 of UnitOfWork#lock column names are used instead of field names, I think the line should read: array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]),
A related question: when I load an instance by relation, e.g. $author = $book->getAuthor(), I cannot specify a lock type at that point, so I have to call $em->lock($author, LockType::PESSIMISTIC_WRITE) afterwards, which results in two database queries for the same record. Is it possible to do this at once, without setting a transaction isolation level? |
| Comments |
| Comment by Benjamin Eberlei [ 10/Jul/10 ] |
|
Fixed the UoW issue. Thanks! As for the second issue, there is really only one way to skip the second database call: Use a Fetch Join DQL to get both book and author and apply the pessimistic lock to that DQL. (Locking both the book and author) All the other way already created the Author proxy object and you won't be able to specify a locking hint on those lazy loading select statements, requiring you to do the second locking call. |
| Comment by Dennis Verspuij [ 10/Jul/10 ] |
|
Hi Benjamin, thanks for the quick answer, I already thought you'd answer that. Only funny thing is that in my SQL log it seems the select query for the lock is called before the select query for populating the $author values! May be because it delays populating the values before its first use? In the lock could actually be done at once. Any ideas? |
| Comment by Benjamin Eberlei [ 10/Jul/10 ] |
|
Ah ok that is a very good optimization case, let me think about how and if this is easily implementable. I close this issue now and open up a new one |
[DDC-669] order of fields in sql query and parameters to be bound to the dbal's statement is different Created: 05/Jul/10 Updated: 05/Jul/10 Resolved: 05/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA2 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Sergei Lissovski | Assignee: | Benjamin Eberlei |
| Resolution: | Duplicate | Votes: | 0 |
| Labels: | None | ||
| Environment: |
win7, apache2, php 5.3.2 |
||
| Description |
|
Hi guys, It seems that I found a bug in the Doctrine\ORM\Persisters\BasicEntityPersister. Let me explain. Put it simply, I have the following entity: Foo.php /**
* @Entity
*/
class Foo
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ManyToOne(targetEntity="Foo", inversedBy="children")
* @JoinColumn(name="parent_id", referencedColumnName="id")
*/
private $parent;
/**
* @OneToMany(targetEntity="Foo", mappedBy="parent")
*/
private $children;
/**
* @Column(type="integer", nullable=false)
*/
private $verticalOrder;
// a bunch of getters/setters
As you can see, there's a mandatory field called "verticalOrder", it is not intended to be managed manually but rather with a listener attached to onFlush event. ( In that listener, after a value for the verticalOrder is set, the uow::recomputeSingleEntityChangeSet() method is invoked, as it was suggested in documentation ). The thing is that when I persist an instance of this class and then invoke flush ( without providing a value for it, the verticalOrder ), Doctrine dies silently, with no exception being thrown. The easy fix can be done with adding this piece of source code to the BasicEntityPersister on line 218: Dumb fix if (!$stmt->execute()) { throw new \Exception('Unable to execute query: '.var_export($stmt->errorInfo(), true)); } That was the first problem I have faced with while trying to find out why my entity is not persisted. After I have delved a bit deeper, I found out In my case it was reversed, instead of parent_id, a value for verticalOrder was inserted and vice versa. Array of parameters to be bound to the Doctrine\DBAL\Statement: Array
(
[verticalOrder] => 1278334736 // at the moment timestamp is used rather than MAX from the table
[parent_id] =>
)
And the query that will be executed: INSERT INTO Foo(parent_id, verticalOrder) VALUES (?, ?) These snippets clearly illustrate the point. I hope I was clear in my explanations. I will try to devote some spare time and write tests to lock this issue, but i'm not able to say at the moment when it happens. All the best, |
| Comments |
| Comment by Benjamin Eberlei [ 05/Jul/10 ] |
|
This issue is a duplicate of |
| Comment by Sergei Lissovski [ 05/Jul/10 ] |
|
Okay, thank you for a quick response. Will take it into account. |
[DDC-662] Missing default value for attribute 'AutoGenerateProxyClasses' Created: 30/Jun/10 Updated: 01/Jul/10 Resolved: 01/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA2 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Marcus Stöhr | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
When not setting the attribute 'AutoGenerateProxyClasses' and running orm:ensure-production-settings an exception is thrown: Notice: Undefined index: autoGenerateProxyClasses in Doctrine/ORM/lib/Doctrine/ORM/Configuration.php on line 332 There should be a default value set or a notice that this attribute is mandatory. |
| Comments |
| Comment by Roman S. Borschel [ 30/Jun/10 ] |
|
the code inside the method that checks the production settings should simply use the getters instead of accessing the attributes array directly. So: $this->getAutoGenerateProxyClasses() instead of $this->_attributes[....]. The default values are encapsulated in the getter methods to avoid having a large array with default values that grows with each new setting even if the setting is never used. |
| Comment by Benjamin Eberlei [ 01/Jul/10 ] |
|
fixed |
[DDC-660] quoting not-in-values Created: 29/Jun/10 Updated: 01/Jul/10 Resolved: 01/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA3 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Dietmar Bauer | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Environment: |
Win7 |
||
| Description |
|
There seems to be a bug in ORM/Query/expr.php method notIn. All literals should be quoted like: /**
* Creates a NOT IN() expression with the given arguments.
*
* @param string $x Field in string format to be restricted by NOT IN() function
* @param mixed $y Argument to be used in NOT IN() function.
* @return Expr\Func
*/
public function notIn($x, $y)
{
if (is_array($y)) {
foreach ($y as &$literal) {
if ( ! ($literal instanceof Expr\Literal)) {
$literal = $this->_quoteLiteral($literal);
}
}
}
return new Expr\Func($x . ' NOT IN', (array) $y);
}
|
| Comments |
| Comment by Dietmar Bauer [ 29/Jun/10 ] |
|
Of course without "unknown macro" in the code! |
| Comment by Benjamin Eberlei [ 29/Jun/10 ] |
|
this was fixed for the in() function already some time ago, good oversight |
| Comment by Dietmar Bauer [ 01/Jul/10 ] |
|
added { code }to description |
| Comment by Benjamin Eberlei [ 01/Jul/10 ] |
|
fixed |
[DDC-656] UnitOfWork::recomputeSingleEntityChangeSet does not preserve field order Created: 25/Jun/10 Updated: 04/Jul/10 Resolved: 04/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA2 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Critical |
| Reporter: | Andriy Savchenko | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Environment: |
php 5.3.2, ubuntu 10.4 |
||
| Description |
|
XML <?xml version="1.0" encoding="utf-8"?> <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xsi="http://www.w3.org/2001/XMLSchema-instance" schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> <entity name="Entities\Specification" table="specification"> <field name="name" type="string" column="name" length="255"/> <field name="type" type="string" column="type"/> <id name="specificationId" type="integer" column="specification_id"> <generator strategy="AUTO"/> </id> </entity> </doctrine-mapping> Code $spec = new \Entities\Specification(); $spec->setName('test1'); $spec->setType('type1'); $em->persist($spec); $em->getUnitOfWork()->computeChangeSet($em->getClassMetadata(get_class($spec)), $spec); $data1 = $em->getUnitOfWork()->getEntityChangeset($spec); $spec->setType('type2'); $em->getUnitOfWork()->recomputeSingleEntityChangeSet($em->getClassMetadata(get_class($spec)), $spec); $data2 = $em->getUnitOfWork()->getEntityChangeset($spec); // data1 contains keys in correct order: name, type var_dump($data1); // data2 contains keys in reverse order: type, name var_dump($data2); I got this issue when was trying to change entity properties using onFlush event |
| Comments |
| Comment by Benjamin Eberlei [ 27/Jun/10 ] |
|
fixed format |
| Comment by Benjamin Eberlei [ 27/Jun/10 ] |
|
how is this a bug? |
| Comment by Andriy Savchenko [ 30/Jun/10 ] |
|
when you call $em->flush() values are inserted switched |
| Comment by Benjamin Eberlei [ 01/Jul/10 ] |
|
Updating priority to critical |
| Comment by Benjamin Eberlei [ 04/Jul/10 ] |
|
fixed |
[DDC-649] SQL Error with single table inheritance and findAll method Created: 20/Jun/10 Updated: 04/Jul/10 Resolved: 04/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA1, 2.0-BETA2 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Critical |
| Reporter: | Paul Fariello | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Environment: |
Postgresql |
||
| Description |
|
When selecting all objects of SecondClass and ThirdClass which both inherit from FirstClass with the following code : $em->getRepository('FirstClass')->findAll(); Postgresql is throwing the following error : I think it's because of the empty string after the IN clause. There is the fail test case : Classes : class FirstClass {
private $id;
private $type;
}
class SecondClass extends FirstClass {
private $otherStuff;
}
class ThirdClass extends FirstClass {
private $otherStuff;
}
Mapping : <?xml version="1.0" encoding="UTF-8"?> <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> <entity name="FirstClass" table='"first_class"' inheritance-type="SINGLE_TABLE"> <id name="id" type="integer" column="id"> <generator strategy="SEQUENCE"/> <sequence-generator sequence-name="first_class_id_seq" allocation-size="1" initial-value="1"/> </id> <discriminator-column name="type" type="integer" field-name="type" /> <discriminator-map> <discriminator-mapping value="1" class="ThirdClass" /> <discriminator-mapping value="2" class="SecondClass" /> </discriminator-map> </entity> </doctrine-mapping> <?xml version="1.0" encoding="UTF-8"?> <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> <entity name="SecondClass"> <field name="otherStuff" column="other_stuff" type="string" /> </entity> </doctrine-mapping> <?xml version="1.0" encoding="UTF-8"?> <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd"> <entity name="ThirdClass"> <field name="otherStuff" column="other_stuff" type="string" /> </entity> </doctrine-mapping> The SQL : CREATE TABLE first_class
(
id serial NOT NULL,
"type" integer,
other_stuff character varying,
CONSTRAINT first_class_pkey PRIMARY KEY (id)
)
WITH (
OIDS=FALSE
);
The fail test : require_once 'class/FirstClass.php';
require_once 'class/SecondClass.php';
require_once 'class/ThirdClass.php';
$entities = $doctrineEntityManager->getRepository('FirstClass')->findAll();
|
| Comments |
| Comment by Paul Fariello [ 20/Jun/10 ] |
|
The problem seems to come from Doctrine\ORM\Persisters\SingleTablePersister::_getSelectConditionSQL() |
| Comment by Bryan Mills [ 22/Jun/10 ] |
|
I am also experiencing this exact issue. The value of the empty string in the 'IN' clause would appear to be the 'discriminatorValue' field on the instance of ClassMetadataInfo that gets passed into SingleTablePersister. For me, that value is null. As a side note, this came up when I was trying to switch all my entities from using Annotation to XML. When using Annotations, I do not experience the problem with this query, which leads me to believe the issues lies somewhere in the XML drivers. That's about as far as I've gotten trying to debug it. |
| Comment by Paul Fariello [ 24/Jun/10 ] |
|
Why do not juste change the _getSelectConditionSQL() to the following : protected function _getSelectConditionSQL(array $criteria, $assoc = null) { $conditionSql = parent::_getSelectConditionSQL($criteria, $assoc); // Append discriminator condition if ($conditionSql) $conditionSql .= ' AND '; if (isset($this->_class->discriminatorValue)) { $values = array($this->_conn->quote($this->_class->discriminatorValue)); } $discrValues = array_flip($this->_class->discriminatorMap); foreach ($this->_class->subClasses as $subclassName) { $values[] = $this->_conn->quote($discrValues[$subclassName]); } $conditionSql .= $this->_getSQLTableAlias($this->_class->name) . '.' . $this->_class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')'; return $conditionSql; } |
| Comment by Benjamin Eberlei [ 30/Jun/10 ] |
|
There is a fundamental flaw in your logic i believe. The parent entity has to be part of the entity map, otherwise it cannot be used as a starting point for findAll() on the repository. <discriminator-map>
<discriminator-mapping value="0" class="FirstClass" />
<discriminator-mapping value="1" class="ThirdClass" />
<discriminator-mapping value="2" class="SecondClass" />
</discriminator-map>
|
| Comment by Paul Fariello [ 30/Jun/10 ] |
|
But what if the FirstClass is abstract ? However I should be able to select all its children classes. Anyway I think Doctrine should allow me to do it |
| Comment by Benjamin Eberlei [ 01/Jul/10 ] |
|
even if it is abstract it is part of the hierachy and can be selected in DQL, repository and find methods. Since you cannot instantiate Maybe we should throw an exception in the mapping drivers if this is not done properly |
| Comment by Paul Fariello [ 01/Jul/10 ] |
|
Correct me if I'm wrong, but if I do such a thing then i'll have a discriminator value wich will never be used. Thank you for the explanation. |
| Comment by Benjamin Eberlei [ 01/Jul/10 ] |
|
add it for now, you are right its never used. We will look into a change. |
| Comment by Benjamin Eberlei [ 04/Jul/10 ] |
|
Fixed in master |
[DDC-647] string length not taken into account on id while using Yaml driver Created: 20/Jun/10 Updated: 28/Jun/10 Resolved: 22/Jun/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | Mapping Drivers |
| Affects Version/s: | 2.0-BETA1 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Guillaume ORIOL | Assignee: | Christian Heinrich |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
The string lenght specified in the Yaml file is not respected on id columns. Sample YAML file ---
# Entities.Stock.dcm.yml
Entities\Stock:
type: entity
table: stocks
id:
id:
type: string
length: 10
generator:
strategy: NONE
fields:
nature:
type: string
length: 10
name:
type: string
length: 40
creationDate:
name: creation_date
type: datetime
owner:
type: string
length: 8
Command used to generate the SQL statements: Result: CREATE TABLE stocks (id VARCHAR(255) NOT NULL, nature VARCHAR(12) NOT NULL, name VARCHAR(40) NOT NULL, creationDate DATETIME NOT NULL, owner VARCHAR(8) NOT NULL, PRIMARY KEY(id)) ENGINE = InnoDB The "id" is generated as VARCHAR(255) instead of VARCHAR(10). |
| Comments |
| Comment by Benjamin Eberlei [ 20/Jun/10 ] |
|
Fixed formating |
| Comment by Christian Heinrich [ 22/Jun/10 ] |
| Comment by Benjamin Eberlei [ 28/Jun/10 ] |
|
Hey Christian, please don't mark bugs as fixed that have not been merged into the main repository yet. Merged now |
[DDC-646] Missing inclusion of namespace Created: 19/Jun/10 Updated: 19/Jun/10 Resolved: 19/Jun/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | Tools |
| Affects Version/s: | 2.0-BETA2 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Blocker |
| Reporter: | Antoine Hedgecock | Assignee: | Roman S. Borschel |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand.php requires that you include the following namespace Doctrine\ORM\Tools\EntityGenerator, else the following error is thrown PHP Warning: require(Doctrine/ORM/Tools/Console/Command/EntityGenerator.php): failed to open stream: No such file or directory in /usr/local/zend/share/pear/Doctrine/Common/ClassLoader.php on line 148 PHP Fatal error: require(): Failed opening required 'Doctrine/ORM/Tools/Console/Command/EntityGenerator.php' (include_path='/Users/antoine/Sites/startaeget-3.3/application/library:.:/usr/local/zend/share/ZendFramework/library:/usr/local/zend/share/pear') in /usr/local/zend/share/pear/Doctrine/Common/ClassLoader.php on line 148 |
| Comments |
| Comment by Benjamin Eberlei [ 19/Jun/10 ] |
|
fixed. |
[DDC-644] [ORACLE] setting query maxResults together with firstResult leads to an error Created: 17/Jun/10 Updated: 28/Jul/10 Resolved: 28/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA2 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Critical |
| Reporter: | Martin Ivičič | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Environment: |
PHP 5.3.2 |
||
| Attachments: |
|
| Description |
|
I'm not very skilled in doctrine yet, but I found something that might be worth reporting. The code below produces "Class does not exist" error, which is quite misleading and just a side effect of a Notice that shows up in AbstractHydrator.php (lines 190 and 191). $q = DBConnection::Common()->createQueryBuilder()->select('c')->from('COMPANY', 'c')->getQuery();
$q->setMaxResults(15);
$q->setFirstResult(1);
var_dump($query->getResult());
The query becomes: SELECT b.* FROM (SELECT a.*, ROWNUM AS doctrine_rownum FROM (SELECT c0_.ID_COMPANY AS ID_COMPANY0, c0_.CONTACT AS CONTACT1, c0_.CONTRACT AS CONTRACT2, c0_.DESCRIPTION AS DESCRIPTION3, c0_.ID_DISPLAY_CASE AS ID_DISPLAY_CASE4, c0_.MAX_OPEN_WIN AS MAX_OPEN_WIN5, c0_.NAME AS NAME6, c0_.REFRESH_TIME AS REFRESH_TIME7, c0_.STATUS AS STATUS8, c0_.TABLESPACE AS TABLESPACE9 FROM COMPANY c0_) a ) b WHERE doctrine_rownum BETWEEN 2 AND 16 The problem occurs when iterating over the list of internal column aliases (ID_COMPANY0,DESCRIPTION3,ID_DISPLAY_CASE4,MAX_OPEN_WIN5,NAME6,REFRESH_TIME7,STATUS8,TABLESPACE9 and Result: $cache[$key]['dqlAlias'] becomes NULL $rowData[$dqlAlias][$cache[$key]['fieldName']] = $value; (line 203) creates a new key (named as empty string) in $rowData and things get screwed up since then in further iterations over $rowData array. Unfortunately I'm not familiar with the internal code of doctrine at all, and it seems too complex for me to be able to provide a fix (except for an ugly hack). I've attached the Exception trace (might help). BTW: Doctrine 2 is the cleanest piece of complex code I've ever seen. You guys rock !!! |
| Comments |
| Comment by Benjamin Eberlei [ 28/Jun/10 ] |
|
fixed formating |
| Comment by Benjamin Eberlei [ 28/Jul/10 ] |
|
Fixed |
[DDC-642] Conversion from annotation to xml mapping doesn't equal (missing inversedBy) Created: 16/Jun/10 Updated: 16/Jun/10 Resolved: 16/Jun/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | None |
| Affects Version/s: | None |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Frantisek Troster | Assignee: | Jonathan H. Wage |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
Hi, I've converted schema from annotations to XML, but after the conversion the association is missing inversedBy parameter: public $text; /** * @ManyToOne(targetEntity="CmsUser", inversedBy="articles") * @JoinColumn(name="user_id", referencedColumnName="id") */ converted XML is missing inversedBy attribute: <many-to-one field="user" target-entity="CmsUser" orphan-removal=""> <join-columns> <join-column name="user_id" referenced-column-name="id" nullable="1"/> </join-columns> </many-to-one> Thank you. |
[DDC-641] Conversion from annotation to xml mapping not matching XML schema (Cascade persist) Created: 16/Jun/10 Updated: 16/Jun/10 Resolved: 16/Jun/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA1 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Frantisek Troster | Assignee: | Jonathan H. Wage |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
Hi, I'm converting schema from annotations to XML, but the result doesn't match XML schema: /** * @ManyToMany(targetEntity="ECommerceProduct", cascade={"persist"}) * @JoinTable(name="ecommerce_carts_products", joinColumns={@JoinColumn(name="cart_id", referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="product_id", referencedColumnName="id")}) */ gets converted to: <many-to-many field="related" target-entity="ECommerceProduct" inversed-by="par-relates"> .... <cascade> <persist/> </cascade> ..... but the XML schema allows only: <xs:complexType name="cascade-type"> <xs:sequence> <xs:element name="cascade-all" type="orm:emptyType" minOccurs="0"/> <xs:element name="cascade-persist" type="orm:emptyType" minOccurs="0"/> <xs:element name="cascade-merge" type="orm:emptyType" minOccurs="0"/> <xs:element name="cascade-remove" type="orm:emptyType" minOccurs="0"/> <xs:element name="cascade-refresh" type="orm:emptyType" minOccurs="0"/> </xs:sequence> </xs:complexType> Thank you. |
[DDC-634] Merging an entity with a single valued association that has been set to null has no effect Created: 12/Jun/10 Updated: 30/Jul/10 Resolved: 30/Jul/10 |
|
| Status: | Closed |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | None |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Dave Keen | Assignee: | Roman S. Borschel |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
When merging an owning entity which has NULL as one of its associated properties, merge doesn't realize that it needs to disassociate the entity. class Doctor {
/** @Id @Column(type="integer") @GeneratedValue(strategy="IDENTITY") */
public $id;
/**
* @OneToMany(targetEntity="Patient", mappedBy="doctor", fetch="EAGER")
*/
public $patients;
public function __construct() {
$this->patients = new ArrayCollection();
}
}
class Patient {
/** @Id @Column(type="integer") @GeneratedValue(strategy="IDENTITY") */
public $id;
/**
* @OneToOne(targetEntity="Doctor", inversedBy="patients")
* @JoinColumn(name="doctor_id", referencedColumnName="id")
*/
public $doctor;
public function __construct() {
}
}
Assume that in the database there exists a doctor id=1 and a patient id=1. The patient belongs to the doctor, so the patient table has doctor_id = 1; $p1 = new \vo\Patient(); $p1->id = 1; $p1->doctor = null; $em->merge($p1); $em->flush(); This does not set doctor_id to null as expected. It can be fixed by changing the block at line 1373 in UnitOfWork.php as follow. Apologies for not providing a patch file, I haven't quite got the hang of git yet if ($other !== null) { $targetClass = $this->_em->getClassMetadata($assoc2->targetEntityName); $id = $targetClass->getIdentifierValues($other); $proxy = $this->_em->getProxyFactory()->getProxy($assoc2->targetEntityName, $id); $prop->setValue($managedCopy, $proxy); $this->registerManaged($proxy, $id, array()); } else { $prop->setValue($managedCopy, null); } |
| Comments |
| Comment by Roman S. Borschel [ 30/Jul/10 ] |
|
Fixed in http://github.com/doctrine/doctrine2/commit/69073c4b37ee28f988306db4965f512b70f45181 |
[DDC-627] Unexpected Duplicate Field Mapping Exception Created: 07/Jun/10 Updated: 02/Jun/11 Resolved: 13/Jun/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | Mapping Drivers |
| Affects Version/s: | None |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Alexandre Brina | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Environment: |
mysql Ver 14.14 Distrib 5.1.43, for Win32 (ia32) PHP 5.3.1 (cli) (built: Feb 8 2010 22:11:49)
|
||
| Issue Links: |
|
||||||||
| Description |
|
Trying to generate entities with annotation mappings using the CLI orm:convert-mapping command will throw an exception. – Schema to reproduce // configure a simple doctrine.php file to connect and run the following CLI command: Will throw an MappingException::duplicateFieldMapping on ActivityLog::idact, at line 1064 of class Doctrine\ORM\Mapping\ClassMetadataInfo |
| Comments |
| Comment by Benjamin Eberlei [ 13/Jun/10 ] |
|
Fixed and scheduled for BETA 3 |
| Comment by Cosmo [ 02/Jun/11 ] |
|
Hi, I've got this exact issue in 2.1.0BETA1. Any idea how to work around it? |
[DDC-625] orm:schema-tool:update --dump-sql showing SQL when DB is up-to-date Created: 03/Jun/10 Updated: 09/Jul/10 Resolved: 09/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | None |
| Affects Version/s: | 2.0-BETA1 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Glen Ainscow | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
With the following entities ... <?php /** * @Entity * @Table(name="users") */ class App_Model_User { /** * @Id * @GeneratedValue(strategy="IDENTITY") * @Column(type="integer") */ private $id; /** * @ManyToOne(targetEntity="App_Model_Role") * @JoinColumn(name="role_id", nullable=false) */ private $role; /** @Column(type="string", columnDefinition="CHAR(64) NOT NULL") */ private $password; } <?php /** * @Entity * @Table(name="roles") */ class App_Model_Role { /** * @Id * @GeneratedValue(strategy="IDENTITY") * @Column(type="smallint") */ private $id; } I'm getting this output from orm:schema-tool:update --dump-sql, when the database is already up-to-date: ALTER TABLE users CHANGE password password CHAR(64) NOT NULL; ALTER TABLE users DROP FOREIGN KEY users_ibfk_1; ALTER TABLE users ADD FOREIGN KEY (role_id) REFERENCES roles(id) |
| Comments |
| Comment by Christian Heinrich [ 22/Jun/10 ] |
|
This is most probably equivalent with the following problem:
See /DBAL/Schema/Comparator.php ll. 172ff. & 293 Comparing whether both Types return the same SQL-Statement might do the job but would be really ugly. |
| Comment by Benjamin Eberlei [ 28/Jun/10 ] |
|
The columnDefintion affected field can obviously not be solved by any means. The foreign key thing i see more often in my own projects though, i have to dig deeper there. |
| Comment by Benjamin Eberlei [ 09/Jul/10 ] |
|
Fixed Foreign Key issue in DBAL master, it was an error in the MySQL query to fetch the foreign keys. |
[DDC-620] Add support to multiple FROM clauses Created: 31/May/10 Updated: 20/Jul/10 Resolved: 20/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | None |
| Affects Version/s: | None |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | New Feature | Priority: | Major |
| Reporter: | Guilherme Blanco | Assignee: | Guilherme Blanco |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Issue Links: |
|
||||||||
| Description |
|
We support multiple FROM in EBNF, and also in Hydrators. |
| Comments |
| Comment by Christian Heinrich [ 21/Jun/10 ] |
|
I think this issue is a duplicate. |
| Comment by Benjamin Eberlei [ 20/Jul/10 ] |
|
Was fixed by guilherme, see |
[DDC-618] INDEX BY not working Created: 31/May/10 Updated: 28/Jun/10 Resolved: 28/Jun/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | DQL |
| Affects Version/s: | 2.0-BETA1 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Dennis Verspuij | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 1 |
| Labels: | None | ||
| Attachments: |
|
| Description |
|
It looks like INDEX BY does not work from DQL queries. |
| Comments |
| Comment by Dennis Verspuij [ 31/May/10 ] |
|
Test case |
| Comment by Benjamin Eberlei [ 28/Jun/10 ] |
|
INDEX BY got optimized away, the SqlWalker currently does not interpret the INDEX BY clause. |
| Comment by Benjamin Eberlei [ 28/Jun/10 ] |
|
Fixed |
[DDC-616] Reverse engineering with Oracle Created: 29/May/10 Updated: 20/Jun/10 Resolved: 20/Jun/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA1 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Mickael Perraud | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Environment: |
Ubuntu 10.04 + Oracle 11g Entreprise + PHP 5.3.2 + Doctrine2 Git (up-to-date) |
||
| Issue Links: |
|
||||||||
| Description |
|
I am playing with reverse engineering with Oracle and I have some problems: My schema: create table TABLE_TEST1 ( TEST1_FIRST_COLUMN NUMBER(4) not null, TEST1_SECOND_COLUMN VARCHAR2(50) not null, TEST1_THIRD_COLUMN DATE, constraint PK_TABLE_TEST1 primary key (TEST1_FIRST_COLUMN) using index tablespace TBS_INDEX storage ( initial 100K next 100K ) ) storage ( initial 100K next 100K ) tablespace TBS_DATA; create table TABLE_TEST2 ( TEST2_FIRST_COLUMN NUMBER(4) not null, TEST2_SECOND_COLUMN VARCHAR2(50) not null, TEST2_THIRD_COLUMN DATE, TEST1_FIRST_COLUMN NUMBER(4) not null, constraint PK_TABLE_TEST2 primary key (TEST2_FIRST_COLUMN) using index tablespace TBS_INDEX storage ( initial 100K next 100K ) ) storage ( initial 100K next 100K ) tablespace TBS_DATA; alter table TABLE_TEST2 add constraint TABLE_TEST2__TABLE_TEST1 foreign key (TEST1_FIRST_COLUMN) references TABLE_TEST1 (TEST1_FIRST_COLUMN); My reverse engineering code: ini_set('display_errors', 1);
set_include_path(realpath(__DIR__ . '/../doctrine-orm/lib/'));
require 'Doctrine/Common/ClassLoader.php';
$classLoader = new \Doctrine\Common\ClassLoader('Doctrine', realpath(__DIR__ . '/../doctrine-orm/lib/'));
$classLoader->register();
$config = new \Doctrine\ORM\Configuration;
$cache = new \Doctrine\Common\Cache\ApcCache;
$config->setMetadataCacheImpl($cache);
$driverImpl = $config->newDefaultAnnotationDriver(realpath(__DIR__. '/Infofab/Entities'));
$config->setMetadataDriverImpl($driverImpl);
$config->setQueryCacheImpl($cache);
$config->setProxyDir('Proxies');
$config->setProxyNamespace('Infofab');
$connectionOptions = array(
'dbname' => 'bddmkk',
'user' => 'doctrine',
'password' => 'xxxxxxx',
'host' => 'localhost',
'driver' => 'pdo_oci',
'driverOptions' => array(PDO::ATTR_CASE => PDO::CASE_LOWER)
);
$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config);
$sm = $em->getConnection()->getSchemaManager();
$em->getConfiguration()->setMetadataDriverImpl(
new \Doctrine\ORM\Mapping\Driver\DatabaseDriver(
$em->getConnection()->getSchemaManager()
)
);
$cmf = new \Doctrine\ORM\Tools\DisconnectedClassMetadataFactory($em);
$metadata = $cmf->getAllMetadata();
$cme = new \Doctrine\ORM\Tools\Export\ClassMetadataExporter();
$exporter = $cme->getExporter('annotation', 'Infofab');
$exporter->setMetadata($metadata);
$etg = new \Doctrine\ORM\Tools\EntityGenerator;
$exporter->setEntityGenerator($etg);
$exporter->export();
If I run this code, I obtain 2 entities: <?php /** * TableTest1 * * @Table(name="TABLE_TEST1") * @Entity */ class TableTest1 { /** * @var integer $test1FirstColumn * * @Column(name="TEST1_FIRST_COLUMN", type="integer", nullable=false) * @Id * @GeneratedValue(strategy="SEQUENCE") * @SequenceGenerator(sequenceName="TABLE_TEST1_TEST1_FIRST_COLUMN", allocationSize="10", initialValue="1") */ private $test1FirstColumn; /** * @var string $test1SecondColumn * * @Column(name="TEST1_SECOND_COLUMN", type="string", length=50, nullable=false) */ private $test1SecondColumn; /** * @var datetime $test1ThirdColumn * * @Column(name="TEST1_THIRD_COLUMN", type="datetime", nullable=true) */ private $test1ThirdColumn; } and <?php /** * TableTest2 * * @Table(name="TABLE_TEST2") * @Entity */ class TableTest2 { /** * @var integer $test2FirstColumn * * @Column(name="TEST2_FIRST_COLUMN", type="integer", nullable=false) * @Id * @GeneratedValue(strategy="SEQUENCE") * @SequenceGenerator(sequenceName="TABLE_TEST2_TEST2_FIRST_COLUMN", allocationSize="10", initialValue="1") */ private $test2FirstColumn; /** * @var integer $test1FirstColumn * * @Column(name="TEST1_FIRST_COLUMN", type="integer", nullable=false) */ private $test1FirstColumn; /** * @var string $test2SecondColumn * * @Column(name="TEST2_SECOND_COLUMN", type="string", length=50, nullable=false) */ private $test2SecondColumn; /** * @var datetime $test2ThirdColumn * * @Column(name="TEST2_THIRD_COLUMN", type="datetime", nullable=true) */ private $test2ThirdColumn; /** * @var TABLETEST1 * * @OneToOne(targetEntity="TABLETEST1") * @JoinColumns({ * @JoinColumn(name="TEST1_FIRST_COLUMN", referencedColumnName="TEST1_FIRST_COLUMN") * }) */ private $tEST1FIRSTCOLUMN; } As you can see, it declares 2 times the same column: private $test1FirstColumn; and private $tEST1FIRSTCOLUMN; |
| Comments |
| Comment by Benjamin Eberlei [ 13/Jun/10 ] |
|
Fixed and scheduled for BETA 3 |
| Comment by Mickael Perraud [ 13/Jun/10 ] |
|
Something is broken with your commit: Fatal error: Uncaught exception 'Doctrine\ORM\Mapping\MappingException' with message 'No identifier/primary key specified for Entity 'OUTILLAGE_OPERATION_COLLECTE'. Every Entity must have an identifier/primary key.' in /mkk01/doctrine/doctrine-orm/lib/Doctrine/ORM/Mapping/MappingException.php on line 37 Doctrine\ORM\Mapping\MappingException: No identifier/primary key specified for Entity 'OUTILLAGE_OPERATION_COLLECTE'. Every Entity must have an identifier/primary key. in /mkk01/doctrine/doctrine-orm/lib/Doctrine/ORM/Mapping/MappingException.php on line 37 In file 'lib/Doctrine/ORM/Mapping/Driver/DatabaseDriver.php ' (http://github.com/doctrine/doctrine2/commit/b7db8df7efed4517859be562fd58e6ce4cc6354a) around line 86, the iteration is not complete and it is not able to define the primary key |
| Comment by Benjamin Eberlei [ 13/Jun/10 ] |
|
It seems the detection of many to many tables can cause severe problems. Some internal refactoring has to be done to get this reverse-engineering right. |
| Comment by Benjamin Eberlei [ 20/Jun/10 ] |
|
This should now be finally solved. Many-To-Many tables are detected and supported in reverse engineering now. |
[DDC-614] Multiple Entities in FROM clause throws exception Created: 27/May/10 Updated: 20/Jul/10 Resolved: 20/Jul/10 |
|
| Status: | Closed |
| Project: | Doctrine 2 - ORM |
| Component/s: | DQL |
| Affects Version/s: | 2.0-BETA1 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Andy Aja deh | Assignee: | Guilherme Blanco |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Environment: |
PostgreSql 8.4 |
||
| Issue Links: |
|
||||||||
| Description |
|
When I query, SELECT k, d FROM OneMind\Domain\Sales\Kendaraan k, OneMind\Domain\Sales\DeliveryOrder d it raises exception: SQLSTATE[42P01]: Undefined table: 7 ERROR: missing FROM-clause entry for table "d1_" LINE 1: ...gan AS pelanggan8, k0_.keterangan AS keterangan9, d1_.id AS ... ^ then I try to var_dump the sql: SELECT k0_.id AS id0, k0_.tipe_kendaraan AS tipe_kendaraan1, k0_.warna AS warna2, k0_.no_rangka AS no_rangka3, k0_.no_mesin AS no_mesin4, k0_.tahun AS tahun5, k0_.rrn AS rrn6, k0_.salesman AS salesman7, k0_.pelanggan AS pelanggan8, k0_.keterangan AS keterangan9, d1_.id AS id10, d1_.nomor AS nomor11, d1_.tanggal AS tanggal12, d1_.kode_supplier AS kode_supplier13, d1_.tipe_kendaraan AS tipe_kendaraan14, d1_.warna AS warna15, d1_.no_rangka AS no_rangka16, d1_.no_mesin AS no_mesin17, d1_.tahun AS tahun18, d1_.harga_beli AS harga_beli19, d1_.no_sap AS no_sap20, d1_.tgl_sap AS tgl_sap21, d1_.dpp AS dpp22, d1_.ppn_masuk AS ppn_masuk23, d1_.bunga AS bunga24, d1_.jatuh_tempo AS jatuh_tempo25, d1_.rrn AS rrn26 FROM kendaraan k0_ Only the first entity appears in SQL FROM clause. The second one is missing. It is likely affect JOIN as well. |
| Comments |
| Comment by Felix-Johannes Jendrusch [ 09/Jul/10 ] |
|
PDOException SQLSTATE[42P01]: Undefined table: 7 ERROR: missing FROM-clause entry for table "t2_" LINE 1: ...N terminal_file t1_ ON f0_.id = t1_.file_ref AND (t2_.id IN ... ^' Join $qb->innerJoin('f.terminals', 'cts', Expr\Join::WITH,
$qb->expr()->in('cts.terminalRef.id', array_map(function($value) {
return (integer) $value;
}, (array) $value)));
DQL SELECT f FROM [...]\File f INNER JOIN f.terminals cts WITH cts.terminalRef.id IN(5) [...] SQL SELECT [...] FROM file f0_ INNER JOIN terminal_file t1_ ON f0_.id = t1_.file_ref AND (t2_.id IN (5)) INNER JOIN terminal t2_ ON t1_.terminal_ref = t2_.id [...] |
| Comment by Guilherme Blanco [ 20/Jul/10 ] |
|
On http://github.com/doctrine/doctrine2/commit/2c28872af820a27b36e4ff3ca28ef92ea8c1f0f3 this issue was fixed. |
[DDC-613] QueryBuilder doesn't permit any functions in select() Created: 25/May/10 Updated: 16/Jun/10 Resolved: 16/Jun/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | DQL |
| Affects Version/s: | 2.0-BETA1 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Critical |
| Reporter: | David Abdemoulaie | Assignee: | Jonathan H. Wage |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
Example: $expr = $qb->expr();
$qb->select($expr->count('e.id'));
Result: InvalidArgumentException: Expression of type 'Doctrine\ORM\Query\Expr\Func' not allowed in this context. |
[DDC-611] Clearing APC cache is broken Created: 24/May/10 Updated: 28/Jun/10 Resolved: 28/Jun/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | None |
| Affects Version/s: | 2.0-BETA1 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Romain D. | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
When running ./doctrine orm:clear-cache with the APC cache, it simply won't work because the cache is not shared between the different PHP processes. So, the php instance run by ./doctrine and the one run by apache are different and won't share their cache. This is an APC feature. A workaround is needed, other than calling it via apache, or simply remove it. And write it in the documentation. |
| Comments |
| Comment by Benjamin Eberlei [ 28/Jun/10 ] |
|
Fixed, throwing an exception now |
[DDC-600] Persisting Entities with unmanaged related associations produces ugly notices Created: 18/May/10 Updated: 24/Jul/10 Resolved: 07/Jul/10 |
|
| Status: | Closed |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA1 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Improvement | Priority: | Major |
| Reporter: | Benjamin Eberlei | Assignee: | Roman S. Borschel |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Issue Links: |
|
||||||||
| Description |
|
It often happens that you forget to persist related entities during development, producing ugly notices about $object hash not being part of certain arrays, for example in "getEntityIdentifier". Maybe this can be gracefully intercepted without cluttering the code? |
[DDC-596] Add @DiscriminatorMap validation to orm:validate-schema Created: 17/May/10 Updated: 10/Jul/10 Resolved: 10/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | None |
| Affects Version/s: | 2.0-BETA2 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Improvement | Priority: | Major |
| Reporter: | Marc Hodgins | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
Assuming that all child classes of an @InheritanceType("SINGLE_TABLE") and @InheritanceType("JOINED") table should be defined in the topmost parent's @DiscriminatorMap, it would be very helpful to add this validation to the CLI orm:valdiate-schema. At present, if a child table is defined in the topmost table @DiscriminatorMap on a SINGLE_TABLE inheritance, then orm:validate-schema states that the mapping files are correct but then a Doctrine\DBAL\Schema\SchemaException is thrown. The same exception is also thrown for other schema-tool commands (such as 'create'). Haven't tested what happens on a JOINED table.. /**
* @Entity @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="discr", type="string")
* @DiscriminatorMap({"employee" = "Employee"}) // note, the second child table was accidentally omitted here
*/
class Person
{
// ...
}
/**
* @Entity
*/
class Employee extends Person
{
// ...
}
/**
* @Entity
*/
class Customer extends Person
{
// ....
}
Schema tool commands (create, validate-schema) then throw: [Doctrine\DBAL\Schema\SchemaException] The table with name 'person' already exists. ... and orm:validate-schema actually says that the mapping is correct (which it obviously isn't) before throwing the exception: [Mapping] OK - The mapping files are correct. [Doctrine\DBAL\Schema\SchemaException] The table with name 'person' already exists. Not critical since the mapping is obviously wrong here, but having this extra check in the validator could save a lot of debugging time. A duplicate table name error is confusing in this situation, and it seems wrong that the validate-schema command presently reports that the mapping files are correct when in fact the inheritance is broken. |
| Comments |
| Comment by Roman S. Borschel [ 17/May/10 ] |
|
Note: Not all child classes must be specified, only those that are entities. You can have non-mapped as well as mapped-superclasses as subclasses as well. |
| Comment by Benjamin Eberlei [ 30/Jun/10 ] |
|
The validation task to perform here would be: Throw a warning if there exists and entity that extends the Parentclass, is however not part of the DiscriminatorMap |
| Comment by Benjamin Eberlei [ 10/Jul/10 ] |
|
Implemented |
Query Hint for LOCK mechanisms plus support in $em->find()
(DDC-178)
|
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | None |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Sub-task | Priority: | Major |
| Reporter: | Benjamin Eberlei | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
Support Pessimistic Locks for Entities with a JoinedSubclassPersister |
| Comments |
| Comment by Benjamin Eberlei [ 04/Jul/10 ] |
|
Fixed. |
[DDC-577] Change default allocationSize from 10 to 1 Created: 07/May/10 Updated: 12/Aug/10 Resolved: 13/Jun/10 |
|
| Status: | Closed |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA1 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Improvement | Priority: | Major |
| Reporter: | Jan Tichý | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
I propose to change the default value of allocationSize for sequence columns from current 10 to 1. It's defined in $definition['allocationSize'] in Doctrine/ORM/Mapping/ClassMetaFactory.php. The improvement request is based on detailed discussion on http://www.doctrine-project.org/jira/browse/DDC-569?focusedCommentId=12855&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#action_12855 Current default value for allocationSize=10 is CONFUSING and I am sure it will cause problems and misunderstoods for many people in future. The core of the problem is that lot of people define table structure manually, separately from entity annotations. And while the default allocationSize is 10, they have to remember (and they have to know they should remember) each time they write new table and new entity, that: a) They have to define each the sequence in database manually with INCREMENT BY 10 Shortly, in an intuitive way everybody assumes the behaviour as allocationSize would be 1. Current default value 10 is not in accord with this intuitive perception and will cause many questions and misunderstoods. The "preloading" of 10 values at once is a kind of "advanced optimalization" - it's great it is available and implemented - but it should not be applied automatically, but only after explicit setup. |
| Comments |
| Comment by Roman S. Borschel [ 05/Jun/10 ] |
|
This should be changed for BETA3. |
| Comment by Benjamin Eberlei [ 13/Jun/10 ] |
|
Fixed and scheduled for 2.0 BETA 3 |
| Comment by Guilherme Blanco [ 12/Aug/10 ] |
|
Actually patch was only committed now. http://github.com/doctrine/doctrine2/commit/5719f8523bbb3c8a6fd11c749e5317b238d3d2b3 |
[DDC-575] Ignoring some annotations Created: 07/May/10 Updated: 09/Jul/10 Resolved: 09/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA1 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Václav Novotný | Assignee: | Benjamin Eberlei |
| Resolution: | Cannot Reproduce | Votes: | 0 |
| Labels: | None | ||
| Attachments: |
|
| Description |
|
Some placement of handler annotation is ignored. PrePersist is ignored: /**
* @PrePersist
*
* Will trigger error.
*/
public function error()
{
trigger_error('ERROR');
}
PrePersist works fine: /**
* @PrePersist
* @foo
*
* Will trigger error.
*/
public function error()
{
trigger_error('ERROR');
}
|
| Comments |
| Comment by Roman S. Borschel [ 01/Jul/10 ] |
|
Is this issue still valid? Is Doctrine Common Beta3 still affected? or is it an ORM issue? |
| Comment by Benjamin Eberlei [ 09/Jul/10 ] |
|
Cannot reproduce, i have added a test-case that shows exactly the described code: http://github.com/doctrine/common/commit/3b42776ada70cf4161082c155f5d8ba9326ce674 However here the relevant annotation is detected correctly in both cases. Closed. |
[DDC-555] @ManyToMany - curious behaviour - assigned values toggle Created: 29/Apr/10 Updated: 03/Jul/10 Resolved: 03/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-ALPHA4 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Minor |
| Reporter: | Romeo Disca | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Environment: |
ubuntu x86_64 PHP Version 5.3.2-0.dotdeb.2 (/etc/apt/sources.list: deb http://php53.dotdeb.org stable all) Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies |
||
| Description |
|
BUG: SOLUTION: Code: $article = $em->find('Entity\Blog\Article', 1);
printf("-- %s\n", count($article->getCategories()));
$article->getCategories()->clear();
//$em->flush(); // [*]
foreach(array(4) as $id)$article->getCategories()->add(
$em->getReference('Entity\Blog\Category', $id)
);
$em->flush();
=== Entities: class Article
{
/**
* @ManyToMany(targetEntity="Entity\Blog\Category", inversedBy="articles")
* @JoinTable(name="article_category",
* joinColumns={@JoinColumn(name="fk_article", referencedColumnName="pk")},
* inverseJoinColumns={@JoinColumn(name="fk_category", referencedColumnName="pk")}
* )
*/
private $categories;
}
class Category
{
/**
* @ManyToMany(targetEntity="Entity\Blog\Article", mappedBy="categories")
*/
private $articles;
}
tried to apply cascade= {"all"}, but has no effect. |
| Comments |
| Comment by Benjamin Eberlei [ 29/Apr/10 ] |
|
I dont understand this issue, can you please elaborate what exactly is wrong? |
| Comment by Romeo Disca [ 30/Apr/10 ] |
|
To be more verbose: I have two tables article and category. You can see the entity configuration snippets within the code Entities. To reconstruct this bug, set up your database with these 3 tables. Put in some data, but keep the table article_category empty. Now, with an properly configured entity manager, you can execute the displayed code. There is a line which shows the number of categories connected with one article. Please adjust array(4) to use valid category primary keys. You will notice, as a result of this script, that sequential uses will produce -- 0 -- n -- 0 -- n -- 0 ... with n the number of categories you add. it will be 1 in this example (see: array(4)) If you check the database between the runs, you will notice that the table article_categories is filled, empty, filled, empty, filled, ... corresponding to the script results. SOLUTION: You can solve this with one extra line: $em->flush(); // [*]
I think the described behavior is not intended. |
| Comment by Benjamin Eberlei [ 01/Jul/10 ] |
|
The problem is that clear() is actually scheduling the deletion of the whole colleciton. This is a completly different operation compared to: foreach ($col AS $obj) {
$col->removeElement($obj);
}
If you do it this way and than re-add some of the objects, those associations will not be changed. clear() however deletes all, then adds the new ones. again |
| Comment by Benjamin Eberlei [ 03/Jul/10 ] |
|
Verified as a bug though. |
| Comment by Romeo Disca [ 03/Jul/10 ] |
|
I see. I suggest an additional method: public function removeElements() { ... }
|
| Comment by Benjamin Eberlei [ 03/Jul/10 ] |
|
Not necessary, this was actually a bug. It should be fixed in the current master. |
[DDC-544] Extract Interface on Doctrine\ORM\Repository Created: 27/Apr/10 Updated: 09/Apr/13 Resolved: 09/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-ALPHA4 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Improvement | Priority: | Minor |
| Reporter: | Benjamin Eberlei | Assignee: | Roman S. Borschel |
| Resolution: | Won't Fix | Votes: | 0 |
| Labels: | None | ||
| Description |
|
There should be an interface composed of all the methods on the Repository, so that in userland you can do something like: interface IMyRepository extends Doctrine\ORM\IEntityRepository { } class MyRepository extends Doctrine\ORM\EntityRepository implements IMyRepository { } That way in your code you could type-hint for IMyRepository, or for the EntityRepository Interface even and it would be indefinately easier to mock or replace the implementation. |
| Comments |
| Comment by Roman S. Borschel [ 28/Apr/10 ] |
|
To match our naming conventions that should rather be: interface EntityRepository and the old EntityRepository => BasicEntityRepository or similar. |
| Comment by Christian Heinrich [ 04/May/10 ] |
|
Roman, do you mean that we should rename the file (and class) EntityRepository to BasicEntityRepository and create a new EntityRepository that contains an interface EntityRepository? If you want me to, I could do this. Regards |
| Comment by Roman S. Borschel [ 19/May/10 ] |
|
@Christian: Yes, thats what I meant. I will schedule this for BETA3 though. I want BETA2 to be a smooth upgrade without any BC issues, if possible. |
| Comment by Roman S. Borschel [ 13/Jun/10 ] |
|
I am wondering whether this is really necessary? There are not many methods on EntityRepository and its easy to create your own interface(s) for your repositories that include these few methods. To be consistent with our naming standards there would have to be a bc break which seems unnecessary to me, unless we invent some other name for the interface, i.e. "ObjectRepository". |
| Comment by Benjamin Eberlei [ 09/Jul/10 ] |
|
Wont fix |
| Comment by Saem Ghani [ 08/Apr/13 ] |
|
It's unfortunate this wasn't implemented, it's actually a significant issue for us. I have to say I completely disagree with Roman's reasoning in his last comment. We have a very large database and commensurately many repositories. We're now at a point that we need to fire our own application level events (as an example, we end up having to compose in services a lot), some of them originating within repositories (doctrine events are insufficient, and we need application specific ones). We're using Symfony2 for DIC and build repositories not through doctrine but via services. We consider using setter injection to be a significant anti-pattern, an object should be valid/fully-initialized post construction so we'd rather not do that – if you were wondering. Now for all our repositories that we would like to build in extra functionality we create our own repository class, compose in the doctrine repository and implement the interface by convention (we could write our own, but the mileage sucks). Now where we could have passed in an instance of anything obeying that interface we're stuck (there goes type hinting). We can avoid some drudgery through traits but it's still an unfortunate solution. If this is at all possible (even if it has a less than ideal name), we'd very much appreciate this, thank you. PS. A BC break in beta would have been very easy and now it sucks even more. |
| Comment by Marco Pivetta [ 08/Apr/13 ] |
|
Saem Ghani there is such an interface in doctrine common. Check Doctrine\Common\Persistence\ObjectRepository at https://github.com/doctrine/common/blob/2.3.2/lib/Doctrine/Common/Persistence/ObjectRepository.php |
| Comment by Saem Ghani [ 08/Apr/13 ] |
|
Marco: I'm afraid that is incomplete. EntityRepository has a number of methods that fall outside of the scope of those prototyped in ObjectRepository and Selectable interfaces that it implements. |
| Comment by Benjamin Eberlei [ 08/Apr/13 ] |
|
Why don't you ship your own layer of repositories and disregard the Doctrine ones completly? I do that all the time, and then constructor injection is very simple as well. |
| Comment by Saem Ghani [ 08/Apr/13 ] |
|
Because the repositories provide features we use. Reusing/staying close to Symfony/Doctrine makes it easier to learn/train, and gives us herd immunity benefits around shared knowledge and most importantly testing. We're trying to reduce maintenance burden, this interface would not only help us do that but anyone else in a similar situation. Additionally, we have many projects most of them large, going across them we've got something like 400+ tables. |
| Comment by Marco Pivetta [ 08/Apr/13 ] |
|
Saem Ghani your last comment does not really provide any rationale behind extraction of the remaining methods into an interface. There's no advantage in extracting those methods to an own interface. createQueryBuilder, createResultSetMappingBuilder, createNamedQuery, createNativeNamedQuery, clear, __call, getEntityManager, getClassMetadata are all utility methods that are not good candidates for an interface. You can easily re-implement those on an existing entity manager. Just use the Doctrine\Common\Persistence\ObjectRepository API: you should actually stick to that to increase portability across the various object managers in doctrine project. If you need all those methods too, you are looking for inheritance, not composition (and probably are delegating too much responsibility to the repository). |
| Comment by Saem Ghani [ 09/Apr/13 ] |
|
Marco, rather than looking at each comment in isolation (which is what seems to be happening), please take things in aggregate. I've provided the rationale in previous comments. If you require more information I can clarify further. Extracting as an interface would:
|
[DDC-518] Merging an entity with a one to one association to a MANAGED entity with no id throws 'The given entity has no identity.' Created: 13/Apr/10 Updated: 30/Jul/10 Resolved: 30/Jul/10 |
|
| Status: | Closed |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | None |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Critical |
| Reporter: | Dave Keen | Assignee: | Roman S. Borschel |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Attachments: |
|
| Description |
|
Calling merge($entity) where $entity has a one to one association to another entity that has been persisted but not yet flushed (when using auto-generated ids) throws 'The given entity has no identity.' It looks like it does this because _doMerge in UnitOfWork assumes for one to one associations only that the associated entity has an id and calls registerManaged, which then calls addToIdentityMap)on it. I think that registeredManaged should only be called if !isset($this->_entityInsertions[spl_object_hash($other)]) Reproduce.php // This is a new element $doctor = new \vo\Doctor(); $d1->name = "New doctor"; // This is a detached element which is in the database $patient = new \vo\Patient(); $p1->name = "Existing patient"; $p1->id = 1; $doctor->patients->add($patient); $patient ->doctor = $doctor; $em->persist($doctor); // This throws InvalidArgumentException: The given entity has no identity. in D:\Projects\ORM\flextrine2\flextrine\web\lib\Doctrine\ORM\UnitOfWork.php on line 1014 $em->merge($patient); Doctor.php class Doctor {
/** @Id @Column(type="integer") @GeneratedValue(strategy="IDENTITY") */
public $id;
/** @Column(length=100, type="string") */
public $name;
/**
* @OneToMany(targetEntity="Patient", mappedBy="doctor", fetch="EAGER")
*/
public $patients;
public function __construct() {
$this->patients = new ArrayCollection();
}
}
Patient.php class Patient {
var $_explicitType = "vo/Patient";
/** @Id @Column(type="integer") @GeneratedValue(strategy="IDENTITY") */
public $id;
/** @Column(length=100, type="string") */
public $name;
/**
* @OneToOne(targetEntity="Doctor", inversedBy="patients")
* @JoinColumn(name="doctor_id", referencedColumnName="id")
*/
public $doctor;
}
|
| Comments |
| Comment by Roman S. Borschel [ 08/May/10 ] |
|
I think the order of operations in your example is not correct even though the error is misleading. You are associating a "new doctor" to a "detached patient". That does not seem right, remember that merge() returns a managed copy, thus when merging the patient later, the "new doctor" is still associated with the "detached patient", not with the managed one. The following should work: // Merge detached patient $managedPatient = $em->merge($patient); // Associate new doctor with patient $doctor->patients->add($managedPatient); $managedPatient->doctor = $doctor; // Persist new doctor and flush $em->persist($doctor); $em->flush(); |
| Comment by Dave Keen [ 16/May/10 ] |
|
You are quite right - that does work. However, I am now trying to implement my use case (turning a tree of detached objects into a tree of managed objects) and implementing the correct order you describe above seems to cause another problem. I am not sure if this should be another ticket, but I'll put it here for the moment. Note that the part within the stars that creates the unmanaged doctor and the detached patient simulates exactly what is received by Doctrine in my application so I can't put any merges in here. /********************************************************************/ // This is a new element $doctor = new \vo\Doctor(); $doctor->name = "New doctor"; // This is a detached element which is in the database $patient = new \vo\Patient(); $patient->name = "Existing patient"; $patient->id = 1; $doctor->patients->add($patient); $patient->doctor = $doctor; /********************************************************************/ // Now replace $patient with its managed version $managedPatient = $em->merge($patient); $doctor->patients->set(0, $managedPatient); // Persist the doctor $em->persist($doctor); $em->flush(); This works up to the flush, which throws an error of the form: Notice: Undefined index: 000000007dd346c3000000005d0908d2 in \Doctrine\ORM\UnitOfWork.php on line 1955 In the database this results in a new doctor being created, but doctor_id in the patients table is set to NULL for the patient that is supposed to be linking to it. |
| Comment by Benjamin Eberlei [ 06/Jun/10 ] |
|
I think this can't work, because obviously $doctor->patients still points to the unmanaged $patient, not the managed one. Can you elaborate on why the star part is done by Doctrine? Where is doctrine doing that? what are you doing with the public API? All the detached entities should be merged before doing anything regarding a NEW entity in my opinion. |
| Comment by Benjamin Eberlei [ 06/Jun/10 ] |
|
Moved to beta3 for now |
| Comment by Benjamin Eberlei [ 06/Jun/10 ] |
|
Patch with test-case for this merging scenario |
| Comment by Roman S. Borschel [ 30/Jul/10 ] |
|
Fixed in http://github.com/doctrine/doctrine2/commit/69073c4b37ee28f988306db4965f512b70f45181 |
[DDC-501] Merging entities that contained unloaded proxy collections will delete those associations Created: 08/Apr/10 Updated: 01/Jul/10 Resolved: 01/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-BETA1 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Critical |
| Reporter: | Markus Wößner | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Environment: |
PHP-Version 5.3.2 |
||
| Attachments: |
|
| Description |
|
If loading an entity without its -to-many-collections, detaching and merging it back WITHOUT having touched those associations will result in two strange behaviours: oneToMany (bidrectional, mapped by loaded entity): After merge the collection remains empty. Flushing EM and reloading entity will reveal associated entities again manyToMany (bidirectional, mapped by targeted entity): After merge the collection remains empty. Flushing EM will physically delete associations.
|
| Comments |
| Comment by Markus Wößner [ 08/Apr/10 ] |
|
Forgot to tell trunk revision |
| Comment by Christian Heinrich [ 12/May/10 ] |
|
This issue is mainly caused by the entity not being initialized before serialization. Additionally, the PersistenCollection does loose all information that is needed to regain the kept entities because the collection itself is not initialized before serialization. I've added an initialization call here http://github.com/Shurakai/doctrine2/commit/6c185a2891111dfbd83d381bad8c5a2b16536cad#diff-0 However, I'm not sure whether this is the best solution. Any thoughts? |
| Comment by Roman S. Borschel [ 13/May/10 ] |
|
@Christian: I looked at the testcase you committed there and the assumptions it makes are not correct. The original test case provided by Markus made the right assumptions, that is, after the user is unserialized it can and should not know about its groups or phonenumbers since these were not serialized. So the problem must come later, at the point of merging. |
| Comment by Roman S. Borschel [ 03/Jun/10 ] |
|
Pushing back to beta3. |
| Comment by Benjamin Eberlei [ 01/Jul/10 ] |
|
Fixed. |
[DDC-455] E_NOTICE Undefined index when setting field to a property that is not persisted Created: 21/Mar/10 Updated: 07/Jul/10 Resolved: 07/Jul/10 |
|
| Status: | Closed |
| Project: | Doctrine 2 - ORM |
| Component/s: | None |
| Affects Version/s: | None |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Jaka Jancar | Assignee: | Roman S. Borschel |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Issue Links: |
|
||||||||
| Description |
|
Affects trunk. A and B have a One-To-One mapping, with A being the owning side of the relationship. No cascade persist is set. // Create entity A $a = new A(); $em->persist($pt); $em->flush(); echo "Created A {$b->getId()}\n"; // Create B and add it to A $b = new B(); $a->setB($b); //$em->persist($b); // oops, forgot $em->flush(); echo "Created B {$b->getId()}\n"; Expected: either throw an exception saying that A is attempting to reference an instance of B that is not persisted, or silently ignore the field. Actual: Cryptic notice: E_NOTICE (8): Undefined index: 0000000069d80795000000006ebfc57d (Doctrine/ORM/UnitOfWork.php:1903) |
| Comments |
| Comment by Guilherme Blanco [ 23/Mar/10 ] |
|
The issue you have is the same as if you use result cache. The entity is not managed by EM. Cheers, |
| Comment by Benjamin Eberlei [ 23/Mar/10 ] |
|
Given that the combination: $hash = spl_object_hash($object); return $this->somefoo[$hash]; is probably one of the most called constructs in the complete code I tend to disagree with a check on each and everyone of them. However i to see the tendency towards errors of this kind as annoying, but maybe we can catch them earlier in those spots where they occour most often? |
[DDC-410] review all sql to ensure that it works with concurrent requests Created: 11/Mar/10 Updated: 04/Jul/10 Resolved: 04/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | None |
| Affects Version/s: | 2.0-ALPHA4 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Task | Priority: | Minor |
| Reporter: | Lukas Kahwe | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Attachments: |
|
||||||||
| Issue Links: |
|
||||||||
| Description |
|
this is just a reminder ticket .. it is important that all SQL code in D2 takes concurrent requests into account. this means simply fetching data and then writing data back to the RDBMS needs to take into account that there could be concurrent requests that change the fetched data. this means employing necessary explicit locking or optimistic locking etc. |
| Comments |
| Comment by Roman S. Borschel [ 12/Mar/10 ] |
|
I think we dont use a read - update - write approach that is supposed to be atomic anywhere. Its obvious that this is fragile. Its like the select max() + increment in memory + write record variant for generating primary keys which is obviously a bad idea (at least it would need SERIALIZABLE transaction isolation I think). (Obviously, any object retrieval -> modification -> persistence is a read-update-write but thats expectedly not "concurrency safe". Thats what optimistic or pessimistic (not yet implemented) locking is for.) |
| Comment by Roman S. Borschel [ 12/Mar/10 ] |
|
As this is a "reminder" and I dont see any issues currently, this is surely not a blocker until someone finds a serious (and valid) problem related to this. |
| Comment by Lukas Kahwe [ 12/Mar/10 ] |
|
and i want to reverse that logic .. i want to ensure that things have been reviewed so that it is clear that things will work concurrently. for example using CURRENT_TIMESTAMP, even just as an option, for versionable versions is uhm a very bad idea. at least make it microtime. also D2 needs to support LOCK TABLE and it needs to be ensured that its possible to write "behaviors" that figure out the list of tables that need to be locked at the beginning of a flush() execution, because without it you are in a world of concurrency hurt when you want to implement stuff like Sortable or NestedSet on top of D2. |
| Comment by Roman S. Borschel [ 12/Mar/10 ] |
|
What is your definition of "that things work concurrently" ? The default transaction isolation for most databases (READ COMMITTED) is designed to allow concurrency for more liveness. This implies that things like lost updates and other stuff are allowed to happen. If you dont want that you can simply use a SERIALIZABLE isolation level for the transactions which will give stronger consistency but less liveness/performance, right? I'm not convinced (yet) that timestamps are a "very bad idea" for optimistic locking (thats what it is used for, there is no "versionable" behavior or whatever built into Doctrine). Yes, you can still get lost updates in rare, high concurrency situations but most of the time lost updates are prevented and if this is fine for the particular application and a timestamp is a more useable and meaningful value then I dont see anything wrong with it. We added that feature because it was in the JPA spec and every major ORM I know supports timestamps for optimistic locking and I'm relatively confident that they know what they're doing (Its still possible that we just have a broken implementation in Doctrine, I dont know that yet, I didnt have the time to look in detail at the other implementations). As for LOCK TABLE, we will probably provide an abstraction over the specific SQL in the different platforms in the DBAL. Then maybe Doctrine will use these in some scenarios for pessimistic locking but that is stuff for the future. I'm not sure where you're aiming at with this ticket. Yes, locking features are going to be improved. Yes, concurrent transactions can cause lost updates and other stuff unless you use a higher isolation level. But this is all not new. The transaction isolation level is already under your control. The locking features will be improved. Where is the flaw you're apparently seeing? At least I get the feeling out of your ticket and comment that you seem to be very worried about concurrency, thinking that Doctrine doesnt do enough or does it wrong. I, however, dont yet see what more we should do than to provide support for optimistic and pessimistic locking and I dont see the world of concurrency hurt you're seeing and in which way this is the fault of Doctrine. I think I am very aware of concurrency-related problems but as its often a very difficult topic, its easy to overlook something. |
| Comment by Lukas Kahwe [ 12/Mar/10 ] |
|
i am aiming at being able to deliver algorithms on top of D2 that ensure the C in ACID .. for simple INSERT/UPDATE etc you can rely on transactions and native isolation levels, but for the things D2 needs to be able to do (versionable, sortable etc.) we need more than that. of course most apps do not going to have a bazillion of parallel requests .. but its still quite common for people to double submit .. or for multiple admins spotting and fixing the same data issue at the same time and if this breaks your nested set tree .. you are screwed. currently i have no reason to believe that D2 was written with sufficient brain cycles spend on these issues. if you are willing to release D2 1.0 without this being checked, then i think you are doing a horrible mistake. D1 behaviors have tons of flaws that you have mentioned in your blog. but the biggest one never got mentioned, nor did it ever generate a response when i mailed the dev list. i fear that there is just too little attention being paid to this topic, maybe also because there isnt enough expertise in this area. but i think its mainly just not being looked at enough. anyways, if you are certain enough that none of my concerns above are valid and that if there are any issues you can fix them without a BC break, then close the ticket. |
| Comment by Roman S. Borschel [ 12/Mar/10 ] |
|
"i am aiming at being able to deliver algorithms on top of D2 that ensure the C in ACID .. for simple INSERT/UPDATE etc you can rely on transactions and native isolation levels, but for the things D2 needs to be able to do (versionable, sortable etc.) we need more than that." I assume you mean the "application level" consistency right? Such that an ordering is not messed up with 2 items having the same position or that a nested set tree is not corrupt? I understand that but why can't these be solved with a higher isolation level for the operations that modify the order/the tree?. I would be especially interested in such examples that can not be solved by using a higher isolation level. I currently cant see how it would be possible to break consistency of a sorted association or nestedset tree when using a SERIALIZABLE isolation level for the transactions that modify the order or the tree. I'm not trying to dismiss your critiques here, I'm taking you very seriously, but you need to get more specific than "ensure the sql works with concurrent requests". That alone doesnt tell us much. We need concrete examples of problematic concurrent scenarios and what Doctrine itself can or should do to avoid these problems. Currently, we dont know what to look for. Thanks for your help. |
| Comment by Lukas Kahwe [ 12/Mar/10 ] |
|
the issue is that with FOR UPDATE .. and ultra paranoid isolation levels you can get locks on things that exist, but its not possible to necessarily get a LOCK on things that are just being inserted or altered to suddenly fall in your "range" or worse yet that get appended to your "range". some RDBMS do support range locks (like if I do WHERE foo BETWEEN 1 AND 100), but not all do .. and IIRC you do not really have much control over those (its simply a LOCK escalation strategy RDBMS may use to reduce the number of row level locks). this means in many situations fetching data with a FOR UPDATE even, might not be enough to ensure consistency when you do rights later on. i have not done a code review of D2 .. actually i have spend very little time looking at any of the code, but from the questions and comments i have seen, there doesnt seem to be enough emphasis. for example i also saw code for the slug extension. which again employs the model of fetching all potential collisions and then doing an insert on the fetched data, without ensuring that between fetching and inserting there are no new collisions. stuff like this should try harder to do things in a single commit if possible, or getting the necessary locks. locking is non trivial business and so expecting end users to get this right is a bad move imho. but aside from any of the internal sql, i would also urge to ensure that some of the things i have been asking for (an efficient way to determine the affected tables and if they need to be locked at the start of a flush) a solid way to mark specific models or all model instances as dirty to ensure that users can either choose to reload certain dirty models at once or automatically when they are accessed again (see at the same time i acknowledge that i am just pointing at theoretical problems, without a promise to do the actual analysis and fixing. its just that this month i have promised people to spend my spare time on other stuff. next month might be more doable, i am traveling a lot .. where i can hopefully spend some time reviewing, then again traveling means that providing feedback and discussing things is more difficult. |
| Comment by Benjamin Eberlei [ 13/Mar/10 ] |
|
Ok I have read some stuff, InnoDb for example locks on index ranges also locking the prev and next keys to avoid insert behind problms. We are also planing to add support for "FOR UPDATE" and "FOR SHARE" as a query hint to all DQL queries aswell as a "Lock" Enum that allows to set this flags for transactions with the Entity Repositories (i.e ntityManager::find(), EntityRepository::findOne, findall() and such). See the linked issue An automatic lock table is slightly more complex to compute but its possible using the "onFlush" event. My draft of a table locking manager is attached to the ticket. |
| Comment by Benjamin Eberlei [ 13/Mar/10 ] |
|
A TableLocking Manager, usage: $lock = new TableLocking(); $evm = new EventManager(); $evm->addListener($lock); $em = EntityManager::create(..., $evm); // do stuff $em->flush(); $lock->unlockTables(); There should probably be a postCommit() event or something to clean up the mess automatically Btw, this won't work at all with Joined Inheritance. @lsmith: can you comment on the code? |
| Comment by Lukas Kahwe [ 13/Mar/10 ] |
|
This is looking quite promising. I guess it would be wise to give each model class the choice if it wants to cause a lock or not, because especially in high concurrency scenarios locking a table can of course have some drastic performance effects. Actually if I for example have a sortable behavior and I am not messing with the sorting at all, I do not need to lock the table either. So probably the best approach would be that if a model method is called that requires locking. Each model could register an onFlush() event (theoretically it could try and be smart and only register the event when it knows it has something to do) and then given the value of the flag tell the lock class to issue a lock for the given underlying table. BTW: as for innodb .. like i said some RDBMS have this behavior .. but its nothing we can rely on at all .. |
| Comment by Benjamin Eberlei [ 14/Mar/10 ] |
|
yeah of course, this code locks all the time, however some kind of configuration to check for models that need full table locks is probably necessary, but as you can see from the code, easy to implement. My idea would be to add a "TableLockRequired" marker interface and in the loops check if any dirty model implements this marker interface. |
| Comment by Jordi Boggiano [ 14/Mar/10 ] |
|
Just as a reminder, I think if some code attempts to lock and the current RDBMS/Engine doesn't allow the requested lock, D2 should (make it an option maybe, but default true imo) throw an exception saying locking doesn't work with the current engine. Silently doing nothing is good in production to avoid pages exploding, but in development I want to know when I lock if it's not really locking in the background. And if I can know that without reading all detailed specs about whatever engine is in use that's even better. |
| Comment by Roman S. Borschel [ 15/Mar/10 ] |
|
Synchronizing schedule with |
| Comment by Benjamin Eberlei [ 04/Jul/10 ] |
|
resolved. |
[DDC-203] Detached entities are recognized as new ones Created: 09/Dec/09 Updated: 06/Jul/10 Resolved: 06/Jul/10 |
|
| Status: | Closed |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | 2.0-ALPHA3 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Major |
| Reporter: | Giorgio Sironi | Assignee: | Roman S. Borschel |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Environment: |
Cli tests with PHPUnit |
||
| Attachments: |
|
||||||||
| Issue Links: |
|
||||||||
| Description |
|
From
So I do this: |
| Comments |
| Comment by Giorgio Sironi [ 09/Dec/09 ] |
|
Patch tentative. Resolved this bug but breaks another test of DetachedEntityTest, so I added another to make clear what is the issue: relying on keys to know if an entity is detached is not correct when entities have natural keys like CmsPhonenumber. |
| Comment by Roman S. Borschel [ 09/Dec/09 ] |
|
Yes, that exactly is the problem and I am/was aware of this but hitting the database is a no-go for that. Determining the state (UnitOfWork#getEntityState) has to be very fast. So its just a matter of finding the best compromise. |
| Comment by Giorgio Sironi [ 09/Dec/09 ] |
|
We can't save any information in Doctrine objects because serialized or cached entities travel between http requests and sessions. merge() uses the same assumption that entities without identifiers defined are new and when passing a phonenumber it gives a false protection because the defined natural key does not result always in a real entity in the database. |
| Comment by Benjamin Eberlei [ 07/Feb/10 ] |
|
My take at this issue: 1. _doPersist() assumes all entities are new. This is wrong. Now the heuristics: 1. If the IdGenerator is NOT (Assigned <-> None) and we find identifiers then it is obviusly a detached Entity Problematic case: IdGenerator is Assigned and we find identifiers: Here i assume the entity is new, by definition of an entity persist() leads to a duplicate key error and catches this error. I think this is the best approach. |
| Comment by Benjamin Eberlei [ 07/Feb/10 ] |
|
Updated version that introduces the "EntityExistsException" |
| Comment by Roman S. Borschel [ 16/Feb/10 ] |
|
The problem I have with this is that by doing this, every single entity passed to persist() gets its identifier read out using reflection just to determine whether its really new. I would rather change the contract and the documentation to clearly indicate that detached instances must not be passed to persist(). |
| Comment by Giorgio Sironi [ 16/Feb/10 ] |
|
There are no problems in a stricter contract for persist() if detached instances are kept separate from new ones in applications, which is usually true since they come from a cache or a store. I would go with the change as it's the simplest reliable solution. |
| Comment by Roman S. Borschel [ 06/May/10 ] |
|
I updated the documentation to be more clear about this issue: http://www.doctrine-project.org/documentation/manual/2_0/en/working-with-objects:persisting-entities What do you think? The problem is, we can not do it reliably for all cases. Yes, we could look into the identifier property if the identifier is generated and conclude from that whether it is detached or not but that does not work for manually assigned identifiers. For these we would need to hit the database and I just don't think we can do that. So right now changing the persist contract as I did in the documentation, explicitly stating that detached entities should not be passed to persist() and that the behavior is undefined if done so, is the "best" thing. Tell me what you think. The alternative would be to do the above (peek into identifier field of generated identifiers or look the identifier up in the db if it is assigned manually). I'm pretty sure the JPA implementations do just that but I dont know for sure and I dont think we want that. I am still undecided though |
| Comment by Giorgio Sironi [ 06/May/10 ] |
|
If an identifier is a natural key, and not generated, a detached object of that class and a new one are really indistinguishable if they have the same values on the other columns. |
| Comment by Roman S. Borschel [ 03/Jun/10 ] |
|
Pushing final resolution back to beta3. The problem is that this missing detection of detached objects can lead to weird errors in other cases as well. Need to think about it some more. |
[DDC-178] Query Hint for LOCK mechanisms plus support in $em->find() Created: 26/Nov/09 Updated: 04/Jul/10 Resolved: 04/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | None |
| Affects Version/s: | None |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | New Feature | Priority: | Major |
| Reporter: | Benjamin Eberlei | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 2 |
| Labels: | None | ||
| Attachments: |
|
|||||||||||||||
| Issue Links: |
|
|||||||||||||||
| Sub-Tasks: |
|
|||||||||||||||
| Description |
| Comments |
| Comment by Guilherme Blanco [ 09/Dec/09 ] |
|
We should be aware that PHP 5.3 now uses mysqlnd driver internally. We need to do some testing before effectively apply any type of approach here. |
| Comment by Benjamin Eberlei [ 09/Dec/09 ] |
|
What does this have to do with locking? I don't understand it |
| Comment by Benjamin Eberlei [ 09/Dec/09 ] |
|
Attached a quick try for pessimistic lock. Optimistic lock enforcement is much more difficult... |
| Comment by Benjamin Eberlei [ 09/Dec/09 ] |
|
Update of the patch adding tests to the Query SELECT tests. |
| Comment by Roman S. Borschel [ 19/Dec/09 ] |
|
Regarding Nr. 5, indeed more than one root entity is supported in DOctrine 2 when querying. Example (assuming Customer and Employee are not related in any way): $q = $em->createQuery("SELECT c, e FROM Customer c, Employee e WHERE c.hatsize = e.shoesize"
The result would (or should) be an array with both Customer and Employee objects. |
| Comment by Benjamin Eberlei [ 26/Jan/10 ] |
|
We should probably go only for the PESSIMISTIC lock in the interface. However it might be convenient to make optimistic locking possible via an additional parameter. Given that PHP is Stateless it might be a good pattern to do: $id = $_POST['id'];
$lastKnownVersion = $_POST['version'];
$entity = $em->find('Yadda', $id, $lastKnownVersion);
// do stuff with $entity
$em->flush();
|
| Comment by Benjamin Eberlei [ 13/Mar/10 ] |
|
Hm given our discussion yesterday, we should translate the OPTIMISTIC_FORCE_INCREMENT into PHPs execution context (script per request): final abstract class Lock { const NONE = 0; const OPTIMISTIC_READ = 1; const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 4; } OPTIMISTIC_READ requires the fourth parameter of EM::find() to find being a positive integer and adds a version chck for that given version. This wont work with Dql though. EntityRepository should also be changed, this affects: EntityRepository::find($id, Lock $lockMode = null, $lockVersion = null); EntityRpository::findOneBy(array $criteria, Lock $lockMode = null, $lockVersion = null); Entity Repository would translate a $lockVersion != null into a $criteria. Then we onlly need to change: StandardEntityPersister::load($criteria, $entity, $assoc, $hints = array(), $lockMode = null) StandardEntityPersister::_getSelectEntitiesSQL($criteria, $assoc=null, $lockMode = null) The all queries have to be done with DQL to get pessimistic locks imho. A DQL Query with locking would look like: $q->setHint(Query::HINT_LOCK_MODE, Lock::PESSIMISTIC_READ); |
| Comment by Roman S. Borschel [ 13/Mar/10 ] |
|
I would also like to see an EntityManager#lock() method. However, after re-reading the spec and testing with Hibernate 3.5.0-CR-2, I'm not sure we really need the *_FORCE_INCREMENT variations. In Hibernate, em.lock() with OPTIMISTIC doesnt do anything (it doesnt need to, versions are compared on update anyway). Even with OPTIMISTIC_FORCE_INCREMENT nothing happens, except the normal version update when the entity changed. Not sure whether this is a bug or intended. PESSIMISTIC_FORCE_INCREMENT works as expected. So I currently think we should have the following as a start: final abstract class LockMode { const NONE = 1; // default const PESSIMISTIC_READ = 2; const PESSIMISTIC_WRITE = 3; } The LockMode can then be used either in queries or with EntityManager#lock(). Moreso, these lock modes work independantly of whether the entity is versioned (@Version) or not. PESSIMISTIC_READ would acquire a shared read lock while PESSIMISTIC_WRITE would acquire an exclusive write lock. What I'm not sure yet about are the following things: 1) Whether to add a query hint or an explicit setLockMode() method + $_lockMode property on the query. I'm tending towards the latter to give a more explicit API for locking (hints are a bit non-obvious and may be overlooked). 2) Whether we need any kind of OPTIMISTIC support in LockMode or *_FORCE_INCREMENT support. I currently think we should start without both. |
| Comment by Benjamin Eberlei [ 14/Mar/10 ] |
|
In my opinion EntityManager#lock() is not necessary for PHP I think it has a place in long running scripts, where you dont know if you have already a write lock for an entity you have retrieved from the session some time ago, however in PHP you certainly know if a script you are executing needs a read/write consistency lock or not. This is also where optimistic force increment shines in Java, because you update the version column you are certain you have a read consistent version of your entity. In PHP the time-frame between retrievial and a call to lock() is just unnecessary small and could be solved by directly locking the entity. This is why i think EntityManager::find needs to be able to set the version also. A long running script in php means: 1. Session A retrieves Entity with Id 1 and Version 1 and displays a form This would be the case without checking the version column. A pessimistic lock in this case is not possible, because it would be lost between point 1 and point 4. However an optimistic lock could check upon retrieval of the entity in point 4, if it is still at version 1. |
| Comment by Roman S. Borschel [ 14/Mar/10 ] |
|
You dont always know at the point of reading an entity whether you want to lock it or not because these decisions can be made in different layers of the application, even during a single HTTP request. And pessimistic (online) locks are never retrieved in "long-running scripts" / "business transactions", i.e. held during user think time, that would be a concurrency nightmare! And the pessimistic locks in JPA are online locks (select ... for update), not offline locks, so there is no difference between Java and PHP when it comes to the pessimistic online locks (select ... for update). We are really only talking about pessimistic online locks and optimistic offline locks here. (I dont even know if there is such a thing as an optimistic online lock). All/Most of the examples on the following blog do not refer to long-running business transactions (transactions that span multiple database transactions with user-think time in between). Of course EntityManaher#find et al should be extended with optional lockMode and lockVersion arguments like you said but nevertheless there should still be EntityManager#lock(). |
| Comment by Roman S. Borschel [ 14/Mar/10 ] |
|
Let me clarify, pessimistic online locks only really make sense for the duration of a transaction since they're released at the end of a transaction. You dont want to hold such a transaction open during a long-running business transaction (user-think time / multiple requests). Thats why there is no difference in usage of such locks in Java or PHP. |
| Comment by Lukas Kahwe [ 14/Mar/10 ] |
|
actually pessimistic (time limited) offline locks are also commonly used. |
| Comment by Roman S. Borschel [ 14/Mar/10 ] |
|
Moreso, even if you do know you want to lock at the point of reading the entity (which may not be the case like I said above, these decisions can be made in different layers, or even in code of an extension that receives an existing object and now wants to lock it to do its work properly. Without em.lock() that would mean re-reading the whole entity), you may still not want to lock at the point of reading: 1) read entity
2) do some (potentially expensive) computations/calculations/whatever based on the state of the entity but we dont need a lock yet
3) after inspecting the outcome of 2) we decide we want to lock
(yes, locking after reading risks stale data but it locks for a shorter duration, its a compromise) When you can only lock during reading, this would mean in such a case holding the lock unnecessarily long. |
| Comment by Lukas Kahwe [ 14/Mar/10 ] |
|
Err .. what you just described is optimistic offline locking. Aka you read the version identifier, go off do your thing, commit .. possibly discover that there was a concurrent write .. |
| Comment by Roman S. Borschel [ 14/Mar/10 ] |
|
@Lukas: Yea. We're really only talking about pessimistic online locks and optimistic offline locks here. I know there are pessimistic offline locks (D1 even has an implementation of that which I wrote) its not currently in the focus for D2 and can even be provided by an extension. |
| Comment by Benjamin Eberlei [ 14/Mar/10 ] |
|
Ok given layering EntityManager#lock() has use-cases. However from my experience I want to lock entities at the beginning of long running scripts, because these runs are more important to my business than those small runs from users. Say i have an entity that at a certain point in time needs to calculate very expensive stuff, before I start the calculations i would want to make sure that these wont get busted by a user updating the entity during that run, so i would apply SELECT * FOR UPDATE before the calculations and not after them. At point 3 when you apply the lock, you only know if you have a consistent lock between 1 and 3 when you have a version field to check against, a pessimistic lock wont help there. The pessimistic lock in EntityManager#lock() makes sense if you want to make sure that after the aquiring no changes happen, for example: 1. read entity |
| Comment by Roman S. Borschel [ 14/Mar/10 ] |
|
Based on all the discussion so far I updated the main issue description with an API specification. Feel free to comment. |
| Comment by Roman S. Borschel [ 14/Mar/10 ] |
|
Next on my list is working out the transaction semantics for all the cases, i.e. where to enforce that it is invoked inside an active transaction and in which cases the Optimistic/Pessimistic exceptions cause a transaction rollback (or whether there is actually a case in which they do not result in a rollback). |
| Comment by Roman S. Borschel [ 14/Mar/10 ] |
|
First update for transaction requirements. |
| Comment by Roman S. Borschel [ 15/Mar/10 ] |
|
Updated refresh() specification. |
| Comment by Benjamin Eberlei [ 03/Apr/10 ] |
|
Add another condition for PESSIMITIC locks in combination with find()*
|
| Comment by Benjamin Eberlei [ 03/Apr/10 ] |
|
hm actually only EntityManager#lock($entity, $mode) has to be called. |
| Comment by Benjamin Eberlei [ 03/Apr/10 ] |
|
Patch for the said functionality, its missing refresh() changes though. |
| Comment by Benjamin Eberlei [ 04/Apr/10 ] |
|
I made the following observations on lock timeouts:
Unrelated but:
Question: Should we support something like lock timeouts via a query hint? If yes:
Vendors except Oracle and Postgres would simply ignore this option. Postgres would only support TIMEOUT = 0 as NOWAIT |
| Comment by Benjamin Eberlei [ 02/May/10 ] |
|
The latest version of lock-support is available here: http://github.com/beberlei/doctrine2/tree/lock-support The Lock Support is now tested using Gearman Job Server allowing to have functional scenarios where waiting for lock releases is necessary, see: http://www.whitewashing.de/blog/articles/129 Refresh() is still missing, I am not sure if this should be included in the 2.0 version (use-case is very slim). |
| Comment by Roman S. Borschel [ 19/May/10 ] |
|
Great work so far. I think we can skip refresh() support for now, so post-2.0 if at all. @"Should we support something like lock timeouts via a query hint?" I think setHint(Query::HINT_LOCK_TIMEOUT, 0) would be good. That way we keep the possibility open for later enhancements regarding other timeout values (i.e. if features change on databases) without requiring public API changes. However, it should be clearly documented that this is a hint and not a guarantee and it should be documented which database vendors interpret the timeout in which way I think. |
| Comment by Roman S. Borschel [ 19/May/10 ] |
|
You can reschedule the lock timeout / nowait to beta3 if you want that. I think we already have enough for beta2. |
| Comment by Roman S. Borschel [ 05/Jun/10 ] |
|
Pushing outstanding work back to beta3. |
| Comment by Benjamin Eberlei [ 04/Jul/10 ] |
|
Implemented, Lock Timeouts will be handled in a dedicated ticket. |
[DDC-167] New method: EntityManager#getPartialReference Created: 20/Nov/09 Updated: 22/Jul/10 Resolved: 20/Jul/10 |
|
| Status: | Closed |
| Project: | Doctrine 2 - ORM |
| Component/s: | ORM |
| Affects Version/s: | None |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Improvement | Priority: | Major |
| Reporter: | Roman S. Borschel | Assignee: | Roman S. Borschel |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
Currently, EntityManager#getReference can be used to retrieve cheap references to "persistent" objects without actually loading them. The returned proxies only initialize themselves automatically when one of their methods is invoked. This can lead to unnecessary/unwanted initialization in cases of bidirectional associations where the "association management methods" (setFoo) properly set the other side of the association. Example (one-one, User-Address, User owning side): class User {
private $address;
public function setAddress(Address $address) {
if ($address !== $this->address) {
$this->address = $address;
$address->setUser($this);
}
}
}
class Address {
private $user;
public function setUser(User $user) {
if ($user !== $this->user) {
$this->user = $user;
$user->setAddress($this);
}
}
}
Now, assuming the following code that makes use of getReference to associate a user to an address but actually wants to avoid loading the address. // assuming $user is already loaded and now we want to associate it to an existing address // but without actually loading the address $addressId = 42; // from request, $_POST/$_GET/... $address = $em->getReference('Address', $addressId); $user->setAddress($address); // calls $address->setUser which initializes the proxy! $em->flush(); A possible solution could be to allow obtaining single references to partial objects through the EntityManager. Partial objects can already be fetched through querying with the Query::HINT_FORCE_PARTIAL_LOAD query hint so the possibility to get a single partial object by ID seems to make sense. A proposed method name is: getPartialReference($className, $id). It would return a managed (partial) instance of $className where only the identifier is populated and that will not initialize itself (hence "partial"). In the example above, usage of a partial reference object would avoid the extra initialization. |
| Comments |
| Comment by Benjamin Eberlei [ 20/Nov/09 ] |
|
Sounds good to me, the method name is also very clear about that the retrieved object is partial, the only negative is that its really the only use-case for this method. |
| Comment by Roman S. Borschel [ 21/Nov/09 ] |
|
I think this has some more use-cases. For example if you only want a single object and you have its ID and all you really need is the ID during that request. Proxy objects are initialized even if you call getId() because we (the proxy) cant know what happens inside that method. Of course, partial objects are always dangerous and I think this is made pretty clear in the manual but the choice is up to the user in the end. |
| Comment by Roman S. Borschel [ 12/Mar/10 ] |
|
Another interesting use-case: updating an object without loading it (as an alternative to a DQL bulk UPDATE): $user = $em->getPartialReference('User', $userId);
$user->setName('newname');
$em->flush();
Of course one needs to be aware that the original entity data in such an update is not "correct", i.e. when using event listeners. |
| Comment by Roman S. Borschel [ 19/May/10 ] |
|
Rescheduling for beta3. |
| Comment by Michael Zach [ 21/Jul/10 ] |
|
Hello, this fix needs to be implemented in UnitOfWork on line 612 (function persistNew()) as well - when I try to save an entity, I get an Exception from my error handler: ErrorException with Argument 2 passed to Doctrine\ORM\Mapping\ClassMetadata::setIdentifierValues() must be an array, integer given, called in /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php on line 612 and defined Backtrace: #0: Doctrine\ORM\Mapping\ClassMetadata->setIdentifierValues at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php:612 #1: Doctrine\ORM\UnitOfWork->persistNew at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php:1247 #2: Doctrine\ORM\UnitOfWork->doPersist at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php:1210 #3: Doctrine\ORM\UnitOfWork->persist at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/EntityManager.php:438 The relevant code in UoW is: $idValue = $idGen->generate($this->em, $entity); if ( ! $idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) { $this->entityIdentifiers[$oid] = array($class->identifier[0] => $idValue); $class->setIdentifierValues($entity, $idValue); We're using the SequenceGenerator @SequenceGenerator(allocationSize=1,sequenceName="address_id_seq")
which doesn't return an array, so the array typehint fails and generates an error. |
| Comment by Michael Zach [ 21/Jul/10 ] |
|
Possible fix in UoW, line 612: $class->setIdentifierValues($entity, $this->entityIdentifiers[$oid]);
|
| Comment by Benjamin Eberlei [ 21/Jul/10 ] |
|
please open a new issue, or reopen this one. posting into closed issues does more harm than good |
| Comment by Michael Zach [ 22/Jul/10 ] |
|
Hello Benjamin, since I've no rights to re-open this entry I had to create a new one: http://www.doctrine-project.org/jira/browse/DDC-714 |
| Comment by Benjamin Eberlei [ 22/Jul/10 ] |
|
hm, maybe our user-rirghts arre strange. I look into it later |
[DDC-130] Cascading and @ManyToMany associations is broken Created: 09/Nov/09 Updated: 10/Jul/10 Resolved: 10/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | None |
| Affects Version/s: | None |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Critical |
| Reporter: | Nico Kaiser | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 1 |
| Labels: | None | ||
| Issue Links: |
|
||||||||
| Description |
|
I have two Entities: Users and Alerts. They are associated with @ManyToMany, the assiciation table should be "user_alert". Entities: Sample code: The "remove" command in the sample code deletes the user entry and alert entry, but not the user_alert entry (which was automatically created though). This leaves an orphan entry (or the DBMS will complain because of FK constraints). INSERT INTO users (name) VALUES (?)
array(1) {
[1]=>
string(3) "Bob"
}
INSERT INTO alert (name) VALUES (?)
array(1) {
[1]=>
string(9) "Testalert"
}
INSERT INTO user_alert (userId, alertId) VALUES (?, ?)
array(2) {
[0]=>
int(1)
[1]=>
int(1)
}
DELETE FROM users WHERE id = ?
array(1) {
[0]=>
int(1)
}
DELETE FROM alert WHERE id = ?
array(1) {
[0]=>
int(1)
}
The user_alert table should be automatically updated (on creation and removal)... |
| Comments |
| Comment by Roman S. Borschel [ 11/Nov/09 ] |
|
I think in most cases the entry in the association table should be deleted by the FK constraint. However I think that is currently not the default. You can force it by using onDelete="CASCADE" on the @JoinColumn definitions inside the @JoinTable. That should probably be set by default on join columns on an association table. I'm not sure its worth supporting manual deletion of association table entries through Doctrine as all databases support proper foreign key constraints and this is the most effective way to delete these entries and also enforces the integrity on the database side. So I'm rather voting for making onDelete="CASCADE" the default for join columns of association tables unless specified otherwise. |
| Comment by Roman S. Borschel [ 13/Nov/09 ] |
|
It appears that this doesnt work in JPA(2) either, or well, it depends on the implementation provider. But cascade=REMOVE on @ManyToMany is not allowed per the spec. This issue here is probably one of the reasons for this. Meaning there might be no easy way for the ORM to deal with entries in association tables transparently on cascade=REMOVE. We have the following options: 1) Disallow cascade=REMOVE on ManyToManyMapping (throw a MappingException) 2) Allow it but clearly document that entries in association tables are not removed by Doctrine, so that you need to apply onDelete="CASCADE" on the @JoinColumn definitions. Are there any more caveats with this I can not think of currently? 3) Someone has an idea for transparently deleting entries in association tables on cascade=REMOVE. Solutions would need to be performant and not too cumbersome to implement. Waiting for feedback. |
| Comment by Benjamin Eberlei [ 14/Nov/09 ] |
|
Hm after thinking about it the problem is that the collection has to be either: 1. loaded into memory completely and put through to regular remove a single entity process. This would be required to mark entities for deleted that are also hydrated but attached to another many to many collection of this type. |
| Comment by Nico Kaiser [ 16/Nov/09 ] |
|
Hm, I don't know if my report was unclear... What I meant was automatic update of the join table ("user_alert" in this example). When I delete e.g. a User, I don't want to have to care about updates in the "user_alert" table - this table was automatically generated (by the @JoinTable statement), so it should be automatically updated, e.g. when I delete either a User or an Alert... |
| Comment by Alexander Brouwer [ 19/Nov/09 ] |
|
How about removing a relation between a user and an alert? Meaning the user and alert both remain in the database but they are not connected anymore (think about users with multiple roles). |
| Comment by Roman S. Borschel [ 19/Nov/09 ] |
|
@Alexander: Just like in normal OOP. If a user as a collection of alerts and an alert a collection of users (bidirectional many-many) and you want to remove an alert from a user you simply remove the alert from the collection and the user from the collection of the corresponding alert. This is very clean, the only problem with this is the efficiency because in order to remove the elements from the collections, the collections must be loaded. (Strictly speaking, you only need to adjust the "owning side", so only 1 collection needs to be loaded). Of course you can always use some custom SQL that directly manipulates the intermediate table. See also DDC-128. Also there is Collections of entities always only represent the association of the entity that has the collection and the contained entities. If you remove an entity from a collection, the association is removed, not the entity itself. "The association is removed" means that either the foreign key is NULLed out (in the case of one-to-many) or that the entry in the association table is removed. |
| Comment by Alexander Brouwer [ 19/Nov/09 ] |
|
@Roman: All sounds very simple. But in practice this doesn't seem to work. $uRep = $em->getRepository('System_User');
$user = $uRep->findOneById(1);
unset($user->roles[0]);
$em->persist($user);
$em->flush();
Now, when I clear the manager and reload the user:
|
| Comment by Roman S. Borschel [ 19/Nov/09 ] |
|
@Alexander: Please open a separate issue for this. Thanks. |
| Comment by Marc Hodgins [ 06/May/10 ] |
|
Pastebin entries have expired and this bug is not attached to a specific version so it is difficult to tell the status. Nico, is this still a problem with the current release? |
| Comment by Roman S. Borschel [ 17/May/10 ] |
|
My currently proposed solution to this would be to simply make onDelete="CASCADE" the default for join columns in join tables. Anyone has a strong objection or better idea? |
| Comment by Benjamin Eberlei [ 30/Jun/10 ] |
|
sounds good to me |
| Comment by Benjamin Eberlei [ 09/Jul/10 ] |
|
Other tools actually delete the related records manually, no cascading involved. Since Sqlite doesn't even support foreign keys we should probably do the same instead of relying on "onDelete"="cascade". Whats your take roman? |
| Comment by Benjamin Eberlei [ 09/Jul/10 ] |
|
We have to support this anyways for the following reason: Doctrine 2 gives you absolutely no access to the many-to-many join table, i.e. to be working with cascade it should not only be the default, but the only option (since other options don't work). We need to extend the way "delete" works inside each persister to also clean up many-to-many tables. We need this mechanism anyways for Element Collections to delete all the related entries. The option onDelete="cascade|noaction" is therefore only a hint for the persisters to decide if they perform this action themselves, or let the database vendor perform it. |
| Comment by Benjamin Eberlei [ 09/Jul/10 ] |
|
I pushed my proposed changes to a feature branch: http://github.com/doctrine/doctrine2/tree/DDC-130 There are definatly refactorings that need to be done, however in that state its currently doing its job very well. |
| Comment by Benjamin Eberlei [ 10/Jul/10 ] |
|
What about DQL DELETE? DELETE FROM Boo WHERE bar For each join table: DELETE FROM join_foo WHERE (SELECT id FROM foo WHERE bar) Can this be done determinstically? |
| Comment by Benjamin Eberlei [ 10/Jul/10 ] |
|
The DQL stuff is just way to much to add. Foreign Key constraints should fail in these cases imho |
| Comment by Roman S. Borschel [ 10/Jul/10 ] |
|
I think we have to do this on bulk deletion then, too. Examples from EclipseLink: em.createQuery("delete from User2 u where u.id=1").executeUpdate();
=>
DELETE FROM USER2_GROUP2 WHERE EXISTS(SELECT ID FROM USER2 WHERE (ID = ?) AND ID = USER2_GROUP2.User2_ID)
DELETE FROM USER2 WHERE (ID = ?)
em.createQuery("delete from User2 u").executeUpdate();
=>
DELETE FROM USER2_GROUP2
DELETE FROM USER2
|
| Comment by Benjamin Eberlei [ 10/Jul/10 ] |
|
Fixed in master using the following semantics:
Tested against normal entities, self-referential, CTI and STI, from the owning and the inverse side. |
[DDC-119] Collection change tracking broken with NOTIFY policy Created: 06/Nov/09 Updated: 16/Jul/10 Resolved: 15/Jul/10 |
|
| Status: | Closed |
| Project: | Doctrine 2 - ORM |
| Component/s: | None |
| Affects Version/s: | 2.0-ALPHA2 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Bug | Priority: | Critical |
| Reporter: | Roman S. Borschel | Assignee: | Roman S. Borschel |
| Resolution: | Fixed | Votes: | 1 |
| Labels: | None | ||
| Description |
|
Looks like change tracking of collections together with the NOTIFY policy doesnt work well as collection updates are detected in _computeAssociationChanges. Perhaps the collection itself should inform the UnitOfWork directly? |
| Comments |
| Comment by Kawsar Saiyeed [ 16/Mar/10 ] |
|
Not sure if the issue is identical but seems at least related. Using NOTIFY change tracking with many-to-many bidirectional associations does not work. Objects added to the associations do not get persisted when calling EntityManager#flush. Tested on r7404. |
| Comment by Michael Zach [ 16/Jul/10 ] |
|
Dear Roman, the line # 456 in UnitOfWork.php seems wrong to me: $isChangeTrackingNotify = isset($this->entityChangeSets[$oid]);
Shouldn't this only be set if the entity has @ChangeTrackingPolicy("NOTIFY") *
set in his class docBlock? The current behaviour now is to assign $changeset if changes exists, leaving the NOTIFY tracking policy out: $changeSet = $isChangeTrackingNotify ? $this->entityChangeSets[$oid] : array();
Because of this change, all our unit tests involving saving of entites break (basically, the whole application), which implement @postUpdate for logging purposes logging an own computed changeset. |
| Comment by Roman S. Borschel [ 16/Jul/10 ] |
|
Hi, you're right, I did that naivly because I thought the only case where an entity would already have a changeset on flush is with the NOTIFY policy. I did not think of custom use cases like yours. It is fixed now in master. My apologies. However, this still means your approach would be broken if you would use the NOTIFY policy right? That sounds like maybe there is potential to improve the approach you're currently using. If you're missing anything in the API or implementation that forbids a different approach feel free to raise some new JIRA issues so we can possibly improve the situation. |
| Comment by Michael Zach [ 16/Jul/10 ] |
|
Thank you for fixing this real quick! Our current approach is to compute the changeset on @preUpdate and after a successful save write the changeset to the database in @postUpdate. This conflicted with the changes made, I will however look into it and see if it's feasible for us to implement another approach. Once again, thanks for your reply and fix. |
[DDC-13] Enhance DQL chapter Created: 15/Sep/09 Updated: 15/Jul/10 Resolved: 15/Jul/10 |
|
| Status: | Resolved |
| Project: | Doctrine 2 - ORM |
| Component/s: | Documentation |
| Affects Version/s: | 2.0-BETA1 |
| Fix Version/s: | 2.0-BETA3 |
| Security Level: | All |
| Type: | Task | Priority: | Major |
| Reporter: | Roman S. Borschel | Assignee: | Benjamin Eberlei |
| Resolution: | Fixed | Votes: | 0 |
| Labels: | None | ||
| Description |
|
The first half of the chapter "DQL - Doctrine Query Language" needs more examples and detailed explanations about DQL. The explanations need to be understandable and friendly for newcomers with no Doctrine 1.x experience. |
| Comments |
| Comment by Roman S. Borschel [ 16/Mar/10 ] |
|
Benjaming already made quite some progress on this chapter. More to come for BETA2. |
| Comment by Roman S. Borschel [ 15/Jul/10 ] |
|
Feel free to close this if you think the DQL docs are already good enough |
| Comment by Benjamin Eberlei [ 15/Jul/10 ] |
|
Cover all the important topics now, refactoring and extension could be another ticket. |