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.
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.