Great, I'll try it out. Thanks, Mike!
On Tue, Nov 24, 2015 at 10:15 PM, Mike Bayer <mike...@zzzcomputing.com> wrote: > > > On 11/24/2015 08:19 PM, Mike Bayer wrote: > > I've bisected it down and this one is going to be tough. > > > https://bitbucket.org/zzzeek/sqlalchemy/issues/3593/eager-loading-single-inh-polymorphic-join > > is added. > > > I've figured out exactly where a simple thing was going wrong with this > and produced a good patch that is safe to commit. Even though this > use case broke between 0.8.0 and 0.8.1, the new approach begun in 0.8.1 > on forward removes most of the guessing from how the ORM joins things > together and this fix just closes up a loose end, whereas the use case > working in 0.8.0 and previously was because the old approach of guessing > how to join things just happened to make the right guess here. > > I will try to add the necessary tests for this tomorrow so that it's > good for 1.0.10 at least, feel free to try the patch ahead of time to > confirm it resolves all your issues. > > > > > > > > > > > > On 11/24/2015 07:20 PM, Mike Bayer wrote: > >> > >> > >> On 11/24/2015 07:16 PM, Christopher Lee wrote: > >>> The test passes for me in 0.8.0. (Yes, we have 0.8.0 running in > >>> production, and are finally getting around to upgrading... sigh...) > >> > >> > >> agree. and it then fails in 0.8.1, so this is something very immediate > >> and old in the 0.8 series I'll start looking to see the actual > >> condition here. > >> > >> > >> > >>> > >>> > >>> On Tue, Nov 24, 2015 at 4:09 PM, Christopher Lee <c...@sirdodger.net > >>> <mailto:c...@sirdodger.net>> wrote: > >>> > >>> I'll see if I can nail down the repro; I had to abstract some > >>> production code that I am migrating between versions, and I may > have > >>> lost some important detail. > >>> > >>> Looking at the SQL it generates before and after moving the "links" > >>> relationship, it appears that it is outer joining against the wrong > >>> target, which explains why the identity map is getting confused. > >>> (e.g., it is joining against the innermost anon_1 subquery in the > >>> bad case, and against the todo_elements_1 reference in the good > >>> case, which is why the good case is forming a nice tree.) > >>> > >>> On Tue, Nov 24, 2015 at 3:49 PM, Mike Bayer > >>> <mike...@zzzcomputing.com <mailto:mike...@zzzcomputing.com>> > wrote: > >>> > >>> > >>> > >>> On 11/24/2015 06:14 PM, Christopher Lee wrote: > >>> > > >>> > I am having a problem with SQLAlchemy 1.0.9 that cropped up > when I > >>> > upgraded from 0.8. > >>> > >>> this is a nicely written test but I get the same recursion > overflow > >>> error when I run it in 0.8, 0.9, and 1.0, even back in 0.8.3. > >>> Can you > >>> provide a script that illustrates success in 0.8 and not in 1.0 > >>> ? thanks. > >>> > >>> > >>> > >>> > >>> > >>> > >>> > > >>> > I have the following polymorphic relationship defined. For > >>> some reason, > >>> > when I build a tree, and I try to access the children of the > >>> middle node > >>> > of the tree, it is picking up the wrong edge object and going > >>> into a > >>> > recursive loop. (Querying for the children of n2 is picking > >>> up the edge > >>> > e12, instead of the edges e23, e24 and e25.) > >>> > > >>> > Looking at the identity map, it looks like the query isn't > >>> populating > >>> > the correct objects. > >>> > > >>> > If I move the "links" relationship from the NodesWithEdges > >>> class to the > >>> > base Node class, the query works fine. Also, if I drop > >>> join_depth or > >>> > remove with with_polymorphic mapper arg, it forces multiple > >>> queries, and > >>> > the problem doesn't occur. Is this a bug with the new > >>> release, or am I > >>> > doing something bad? > >>> > > >>> > > >>> > from __future__ import print_function, unicode_literals > >>> > > >>> > import sqlalchemy as sa > >>> > import sqlalchemy.orm > >>> > import sqlalchemy.ext.associationproxy > >>> > import sqlalchemy.ext.declarative > >>> > import sqlalchemy.ext.orderinglist > >>> > > >>> > Base = sqlalchemy.ext.declarative.declarative_base() > >>> > > >>> > > >>> > class Node(Base): > >>> > __tablename__ = 'todo_elements' > >>> > > >>> > element_id = sa.Column(sa.Integer, nullable=False, > >>> primary_key=True) > >>> > element_type = sa.Column(sa.String(20), nullable=False) > >>> > > >>> > __mapper_args__ = { > >>> > 'polymorphic_on': element_type, > >>> > 'with_polymorphic': ('*', None), > >>> > } > >>> > > >>> > > >>> > class NodeWithEdges(Node): > >>> > __mapper_args__ = {'polymorphic_identity': 'todo.list'} > >>> > > >>> > > >>> > class LeafNode(Node): > >>> > __mapper_args__ = {'polymorphic_identity': 'todo.item'} > >>> > > >>> > my_flag = sa.Column(sa.Boolean, default=False) > >>> > > >>> > > >>> > class Edge(Base): > >>> > __tablename__ = 'todo_links' > >>> > __table_args__ = ( > >>> > sa.PrimaryKeyConstraint('parent_id', 'child_id'), > >>> > sa.ForeignKeyConstraint(['parent_id'], [Node.element_id]), > >>> > sa.ForeignKeyConstraint(['child_id'], [Node.element_id]), > >>> > ) > >>> > > >>> > parent_id = sa.Column(sa.Integer, nullable=False) > >>> > child_id = sa.Column(sa.Integer, nullable=False) > >>> > > >>> > > >>> > Edge.child = sa.orm.relationship( > >>> > Node, > >>> > uselist=False, > >>> > primaryjoin=Edge.child_id == Node.element_id, > >>> > lazy=False, > >>> > cascade='all', > >>> > passive_updates=False, > >>> > join_depth=8, > >>> > ) > >>> > > >>> > > >>> > NodeWithEdges.links = sa.orm.relationship( > >>> > Edge, > >>> > primaryjoin=NodeWithEdges.element_id == Edge.parent_id, > >>> > lazy=False, > >>> > cascade='all, delete-orphan', > >>> > single_parent=True, > >>> > passive_updates=False, > >>> > join_depth=8, > >>> > ) > >>> > > >>> > NodeWithEdges.children = > >>> sa.ext.associationproxy.association_proxy( > >>> > 'links', 'child', > >>> > creator=lambda child: Edge(child_id=child.element_id)) > >>> > > >>> > > >>> > > >>> > engine = sa.create_engine('sqlite:///:memory:', echo=True) > >>> > Base.metadata.create_all(engine) > >>> > > >>> > Session = sa.orm.sessionmaker(bind=engine) > >>> > session = Session() > >>> > > >>> > > >>> > # > >>> > # 1 --> 2 --> 3 > >>> > # --> 4 > >>> > # --> 5 > >>> > # > >>> > > >>> > n1 = NodeWithEdges(element_id=1) > >>> > n2 = NodeWithEdges(element_id=2) > >>> > n3 = LeafNode(element_id=3) > >>> > n4 = LeafNode(element_id=4, my_flag=True) > >>> > n5 = LeafNode(element_id=5) > >>> > > >>> > e12 = Edge(parent_id=n1.element_id, child_id=n2.element_id) > >>> > e23 = Edge(parent_id=n2.element_id, child_id=n3.element_id) > >>> > e24 = Edge(parent_id=n2.element_id, child_id=n4.element_id) > >>> > e25 = Edge(parent_id=n2.element_id, child_id=n5.element_id) > >>> > > >>> > session.add_all([n1, n2, n3, n4, n5, e12, e23, e24, e25]) > >>> > session.commit() > >>> > session.expunge_all() > >>> > > >>> > new_n1 = > >>> > session.query(NodeWithEdges).filter(NodeWithEdges.element_id==1).first() > >>> > print(session.identity_map.keys()) > >>> > > >>> > > >>> > def traverse(node, f, depth=0): > >>> > f(node, depth) > >>> > if hasattr(node, 'children'): > >>> > for c in node.children: > >>> > traverse(c, f, depth + 1) > >>> > > >>> > def indent_print(node, depth): > >>> > print(' ' * depth + str(node.element_id)) > >>> > if hasattr(node, 'my_flag'): > >>> > print(node.my_flag) > >>> > > >>> > traverse(new_n1, indent_print) > >>> > > >>> > -- > >>> > You received this message because you are subscribed to the > Google > >>> > Groups "sqlalchemy" group. > >>> > To unsubscribe from this group and stop receiving emails > from it, send > >>> > an email to sqlalchemy+unsubscr...@googlegroups.com > >>> <mailto:sqlalchemy%2bunsubscr...@googlegroups.com> > >>> > <mailto:sqlalchemy+unsubscr...@googlegroups.com > >>> <mailto:sqlalchemy%2bunsubscr...@googlegroups.com>>. > >>> > To post to this group, send email to > sqlalchemy@googlegroups.com <mailto:sqlalchemy@googlegroups.com> > >>> > <mailto:sqlalchemy@googlegroups.com <mailto: > sqlalchemy@googlegroups.com>>. > >>> > Visit this group at > http://groups.google.com/group/sqlalchemy. > >>> > For more options, visit https://groups.google.com/d/optout. > >>> > >>> -- > >>> You received this message because you are subscribed to the > >>> Google Groups "sqlalchemy" group. > >>> To unsubscribe from this group and stop receiving emails from > >>> it, send an email to sqlalchemy+unsubscr...@googlegroups.com > >>> <mailto:sqlalchemy%2bunsubscr...@googlegroups.com>. > >>> To post to this group, send email to > sqlalchemy@googlegroups.com > >>> <mailto:sqlalchemy@googlegroups.com>. > >>> Visit this group at http://groups.google.com/group/sqlalchemy. > >>> For more options, visit https://groups.google.com/d/optout. > >>> > >>> > >>> > >>> -- > >>> You received this message because you are subscribed to the Google > >>> Groups "sqlalchemy" group. > >>> To unsubscribe from this group and stop receiving emails from it, send > >>> an email to sqlalchemy+unsubscr...@googlegroups.com > >>> <mailto:sqlalchemy+unsubscr...@googlegroups.com>. > >>> To post to this group, send email to sqlalchemy@googlegroups.com > >>> <mailto:sqlalchemy@googlegroups.com>. > >>> Visit this group at http://groups.google.com/group/sqlalchemy. > >>> For more options, visit https://groups.google.com/d/optout. > >> > > > > -- > You received this message because you are subscribed to the Google Groups > "sqlalchemy" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to sqlalchemy+unsubscr...@googlegroups.com. > To post to this group, send email to sqlalchemy@googlegroups.com. > Visit this group at http://groups.google.com/group/sqlalchemy. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "sqlalchemy" group. To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at http://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.