#1521: Doctrine Query Cache not storing/rebuilding query with 3 part relation
properly
-----------------------------------+----------------------------------------
 Reporter:  Jay.Klehr              |       Owner:  romanb
     Type:  defect                 |      Status:  new   
 Priority:  minor                  |   Milestone:  1.0.3 
Component:  Query/Hydration        |     Version:  1.0.2 
 Keywords:  query cache            |    Has_test:  0     
 Mystatus:  Pending Core Response  |   Has_patch:  0     
-----------------------------------+----------------------------------------
 I've been playing with Doctrine's Query cache coupled with APC in an app
 I'm working on and noticed that the cache isn't rebuilding (or storing)
 the query components properly for certain queries.

 Models:


 {{{
 class SecurityAcl extends Doctrine_Record
 {
     public function setTableDefinition()
     {
         $this->setTableName('security_acl');
         $this->hasColumn('id', 'integer', 4, array('unsigned' => true,
 'primary' => true, 'autoincrement' => true, 'type' => 'integer', 'length'
 => '4'));
         $this->hasColumn('module_id', 'integer', 4, array('unsigned' =>
 true, 'notnull' => true, 'type' => 'integer', 'length' => '4'));
         $this->hasColumn('resource_id', 'integer', 4, array('unsigned' =>
 true, 'notnull' => true, 'type' => 'integer', 'length' => '4'));
         $this->hasColumn('privilege_id', 'integer', 4, array('unsigned' =>
 true, 'notnull' => true, 'type' => 'integer', 'length' => '4'));

         $this->index('unique', array('fields' => array(0 => 'module_id', 1
 => 'resource_id', 2 => 'privilege_id'), 'type' => 'unique'));
     }

     public function setUp()
     {
         $this->hasOne('SecurityAclPart as Module', array('local' =>
 'module_id',
                                                          'foreign' =>
 'id'));

         $this->hasOne('SecurityAclPart as Resource', array('local' =>
 'resource_id',
                                                            'foreign' =>
 'id'));

         $this->hasOne('SecurityAclPart as Privilege', array('local' =>
 'privilege_id',
                                                             'foreign' =>
 'id'));

         $this->hasMany('SecurityGroup as Groups', array('refClass' =>
 'SecurityGroupAcl',
                                                         'local' =>
 'acl_id',
                                                         'foreign' =>
 'group_id'));
     }
 }

 class SecurityAclPart extends Doctrine_Record
 {
     public function setTableDefinition()
     {
         $this->setTableName('security_acl_part');
         $this->hasColumn('id', 'integer', 4, array('unsigned' => true,
 'primary' => true, 'autoincrement' => true, 'type' => 'integer', 'length'
 => '4'));
         $this->hasColumn('name', 'string', 32, array('notnull' => true,
 'type' => 'string', 'length' => '32'));

         $this->index('unique', array('fields' => array(0 => 'name'),
 'type' => 'unique'));
     }

     public function setUp()
     {
         $this->hasMany('SecurityAcl as Modules', array('local' => 'id',
                                                           'foreign' =>
 'module_id'));

         $this->hasMany('SecurityAcl as Resources', array('local' => 'id',
                                                          'foreign' =>
 'resource_id'));

         $this->hasMany('SecurityAcl as Privileges', array('local' => 'id',
                                                           'foreign' =>
 'privilege_id'));
     }
 }

 class SecurityGroup extends Doctrine_Record
 {
     public function setTableDefinition()
     {
         $this->setTableName('security_group');
         $this->hasColumn('id', 'integer', 4, array('unsigned' => true,
 'primary' => true, 'autoincrement' => true, 'type' => 'integer', 'length'
 => '4'));
         $this->hasColumn('name', 'string', 32, array('notnull' => true,
 'type' => 'string', 'length' => '32'));
         $this->hasColumn('description', 'string', 255, array('type' =>
 'string', 'length' => '255'));

         $this->index('unique', array('fields' => array(0 => 'name'),
 'type' => 'unique'));
     }

     public function setUp()
     {
         $this->hasMany('SecurityAcl as Acl', array('refClass' =>
 'SecurityGroupAcl',
                                                    'local' => 'group_id',
                                                    'foreign' =>
 'acl_id'));
     }
 }

 class SecurityGroupAcl extends Doctrine_Record
 {
     public function setTableDefinition()
     {
         $this->setTableName('security_group_acl');
         $this->hasColumn('group_id', 'integer', 4, array('unsigned' =>
 true, 'primary' => true, 'type' => 'integer', 'length' => '4'));
         $this->hasColumn('acl_id', 'integer', 4, array('unsigned' => true,
 'primary' => true, 'type' => 'integer', 'length' => '4'));
     }

     public function setUp()
     {

     }
 }
 }}}

 I've only included what's related.  These were generated by Doctrine from
 a schema file (these models are part of an ACL module for Zend Framework
 that I found on the net (still an early build)).

 Now, the query in question has the following DQL:

 {{{
 Doctrine_Query::create()
                         ->from('SecurityAcl a')
                         ->innerJoin('a.Module m')
                         ->innerJoin('a.Resource r')
                         ->innerJoin('a.Privilege p')
                         ->leftJoin('a.Groups g INDEXBY g.id')
                         ->orderby('m.name, r.name, p.name')
                         ->execute();
 }}}

 Which generates the following SQL:

 {{{
 SELECT s.id AS s__id, s.module_id AS s__module_id, s.resource_id AS
 s__resource_id, s.privilege_id AS s__privilege_id, s2.id AS s2__id,
 s2.name AS s2__name, s3.id AS s3__id, s3.name AS s3__name, s4.id AS
 s4__id, s4.name AS s4__name, s5.id AS s5__id, s5.name AS s5__name,
 s5.description AS s5__description FROM security_acl s INNER JOIN
 security_acl_part s2 ON s.module_id = s2.id INNER JOIN security_acl_part
 s3 ON s.resource_id = s3.id INNER JOIN security_acl_part s4 ON
 s.privilege_id = s4.id LEFT JOIN security_group_acl s6 ON s.id = s6.acl_id
 LEFT JOIN security_group s5 ON s5.id = s6.group_id ORDER BY s2.name,
 s3.name, s4.name
 }}}

 And to help debug this, I've outputted the "alias" and the _toString of
 the table in Doctrine_Query_Abstract::_exec() (I foreached
 $this->_queryComponents for this particular query for this output after
 $query is found (either through cache or not)).

 Here's that output from the non-cached version of this query:

 {{{
 a

 Component   : SecurityAcl
 Table       : security_acl


 m

 Component   : SecurityAclPart
 Table       : security_acl_part


 r

 Component   : SecurityAclPart
 Table       : security_acl_part


 p

 Component   : SecurityAclPart
 Table       : security_acl_part


 g

 Component   : SecurityGroup
 Table       : security_group


 a.Groups.SecurityGroupAcl

 Component   : SecurityGroupAcl
 Table       : security_group_acl
 }}}

 The single line is the alias, and the "Component: Table:" parts are the
 _toString output from the table object
 ($this->_queryComponents[$alias]!['table']).

 Now, when the query is retrieved from the cache, the SQL is identical, but
 these components are not.  Here's the same output above, but from the
 cached query:

 {{{
 a

 Component   : SecurityAcl
 Table       : security_acl


 m

 Component   : SecurityAclPart
 Table       : security_acl_part


 r

 Component   : SecurityAclPart
 Table       : security_acl_part


 p

 Component   : SecurityAclPart
 Table       : security_acl_part


 g

 Component   : SecurityGroup
 Table       : security_group


 a.Groups.SecurityGroupAcl

 Component   : SecurityGroup
 Table       : security_group
 }}}

 Notice that the last alias' table is wrong.

 I dug in the cache some, and pulled this out for the query in question
 (unserialized array of what's stored in the cache):

 {{{
 Array
 (
     [0] => SELECT s.id AS s__id, s.module_id AS s__module_id,
 s.resource_id AS s__resource_id, s.privilege_id AS s__privilege_id, s2.id
 AS s2__id, s2.name AS s2__name, s3.id AS s3__id, s3.name AS s3__name,
 s4.id AS s4__id, s4.name AS s4__name, s5.id AS s5__id, s5.name AS
 s5__name, s5.description AS s5__description FROM security_acl s INNER JOIN
 security_acl_part s2 ON s.module_id = s2.id INNER JOIN security_acl_part
 s3 ON s.resource_id = s3.id INNER JOIN security_acl_part s4 ON
 s.privilege_id = s4.id LEFT JOIN security_group_acl s6 ON s.id = s6.acl_id
 LEFT JOIN security_group s5 ON s5.id = s6.group_id ORDER BY s2.name,
 s3.name, s4.name
     [1] => Array
         (
             [a] => Array
                 (
                     [0] => SecurityAcl
                 )

             [m] => Array
                 (
                     [0] => a.Module
                 )

             [r] => Array
                 (
                     [0] => a.Resource
                 )

             [p] => Array
                 (
                     [0] => a.Privilege
                 )

             [g] => Array
                 (
                     [0] => a.Groups
                     [1] => id
                 )

             [a.Groups.SecurityGroupAcl] => Array
                 (
                     [0] => a.Groups.Groups
                 )

         )

     [2] => Array
         (
             [s] => a
             [s2] => m
             [s3] => r
             [s4] => p
             [s5] => g
             [s6] => a.Groups.SecurityGroupAcl
         )

 )
 }}}

 I don't think that last alias is supposed to be "a.Groups.Groups".

 As a test, I tried forcing the saved value for that last piece to
 "a.Groups.SecurityGroupAcl", which stored fine, but then the part of
 Doctrine_Query_Abstract that makes a query from the cache doesn't parse it
 properly, and still makes it "a.Groups.Groups".  I suppose I could
 probably also force that and this would work properly, but I haven't tried
 that yet.

 I suspect this hasn't been noticed yet as it seems to only happen with
 slightly more complex queries (possibly due to the alias having 3 parts,
 rather than just 2).

 Any ideas on a fix?  I simplified some of the models some, removing
 unnecessary relations, but that hasn't helped (what I've pasted above is
 after the cleanup I've done).  If it's something that can be fixed in the
 models/query, I'm all ears, and I'll submit the feedback to the module's
 author, but the query works alright as long as it doesn't get cached, so I
 believe this to be a Doctrine issue.

 I'm using PHP 5.2.5, Doctrine 1.0.2+ (revision 5009 of 1.0 trunk), Zend
 Framework 1.6.1, APC 3.0.19.

-- 
Ticket URL: <http://trac.doctrine-project.org/ticket/1521>
Doctrine <http://www.phpdoctrine.org>
PHP Doctrine Object Relational Mapper
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"doctrine-svn" group.
 To post to this group, send email to [email protected]
 To unsubscribe from this group, send email to [EMAIL PROTECTED]
 For more options, visit this group at 
http://groups.google.co.uk/group/doctrine-svn?hl=en-GB
-~----------~----~----~----~------~----~------~--~---

Reply via email to