Verified that the patch works. On Wed, Nov 25, 2015 at 10:16 AM, Christopher Lee <c...@sirdodger.net> wrote:
> 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.