On 04/25/2016 05:16 PM, Alex Dev wrote:
Hello,

I have a broken query when migrating from SQLAlchemy 0.9.4 to 1.0.12. It
seems to be linked to a behavioral change in the ORM
(http://docs.sqlalchemy.org/en/rel_1_0/changelog/migration_10.html#right-inner-join-nesting-now-the-default-for-joinedload-with-innerjoin-true)

Here is simplified version of the code:

|
# -*- coding: utf-8 -*-
fromsqlalchemy import*
fromsqlalchemy.ext.declarative importdeclarative_base
fromsqlalchemy.orm import*


Base=declarative_base()

_plant_table =Table('plant',Base.metadata,
Column('id',Integer,primary_key=True)
)

_plant_dimensionsseries_table =Table('plant_dimensionsseries',Base.metadata,
Column('plant_id',Integer,primary_key=True),
Column('dimensionsseriestype_id',Integer,primary_key=True),
ForeignKeyConstraint(['plant_id'],['plant.id'])
)

_view_plant_dimensionsseries_table
=Table('view_plant_dimensionsseries',Base.metadata,
Column('plant_id',Integer,primary_key=True),
Column('dimensionsseriestype_id',Integer,primary_key=True),
ForeignKeyConstraint(
['plant_id','dimensionsseriestype_id'],
['plant_dimensionsseries.plant_id','plant_dimensionsseries.dimensionsseriestype_id'])
)

classPlant(Base):
     __table__ =_plant_table

classPlantDimensionsseries(Base):
     __table__ =_plant_dimensionsseries_table
     _plant_id =__table__.c.plant_id
     _dimensionsseriestype_id =__table__.c.dimensionsseriestype_id

     plant =relationship('Plant',
                 innerjoin=True,
                 backref=backref('plant_dimensionsseries'))

classPlantDimensionsseriesDataComputed(Base):
     __table__ =_view_plant_dimensionsseries_table
     _plant_id =__table__.c.plant_id
     _dimensionsseriestype_id =__table__.c.dimensionsseriestype_id

# One-to-one relationship
     dimensionsseries =relationship('PlantDimensionsseries',
                 innerjoin=True,
                 backref=backref('data_computed',
                     innerjoin=True))

if__name__ =='__main__':

     engine
=create_engine('postgresql://nurseryacme_employee@localhost:5432/nurseryacme')
Session=sessionmaker(bind=engine)
     session =Session()

# query 1:
# SQLAlchemy 0.9.4: Correct SQL generated
# SQLAlchemy 1.0.12: Wrong SQL generated, a inner JOIN is used instead
of a LEFT OUTER JOIN between plant_dimensionsseries and
view_plant_dimensionsseries
printsession.query(Plant).\
         outerjoin(Plant.plant_dimensionsseries).\
         options(contains_eager(Plant.plant_dimensionsseries)).\

options(joinedload(Plant.plant_dimensionsseries,PlantDimensionsseries.data_computed))

well this usage above is wrong. You can't have contains_eager() and joinedload() along the same paths at the same time like that. Also, chaining joinedload() from contains_eager() is not a very typical thing to do, it works, but joinedload() does not coordinate with contains_eager() in any way, and it has no idea that you are using outerjoin() to the left of it.

The two correct ways to do this are:

    print session.query(Plant).\
        join(Plant.plant_dimensionsseries).\

options(contains_eager(Plant.plant_dimensionsseries).joinedload(PlantDimensionsseries.data_computed, ))

and

    print session.query(Plant).\
        outerjoin(Plant.plant_dimensionsseries).\

options(contains_eager(Plant.plant_dimensionsseries).joinedload(PlantDimensionsseries.data_computed, innerjoin=False))


or of course:


    print session.query(Plant).\
        outerjoin(Plant.plant_dimensionsseries).\

options(joinedload(Plant.plant_dimensionsseries).joinedload(PlantDimensionsseries.data_computed))


where in the last one, the joinedload() to the right coordinates with the joinedload to the left to become a LEFT OUTER JOIN.






# query 2:
# SQLAlchemy 1.0.12: Correct SQL generated
printsession.query(Plant).\
         outerjoin(Plant.plant_dimensionsseries).\
         options(contains_eager(Plant.plant_dimensionsseries)).\

options(joinedload(Plant.plant_dimensionsseries,PlantDimensionsseries.data_computed,innerjoin=False))

|

Result with SQLAlchemy 0.9.4:
|
# query 1
SELECT ...
FROM plant LEFT OUTER JOIN plant_dimensionsseries ON plant.id
=plant_dimensionsseries.plant_id *LEFT OUTER
JOIN*view_plant_dimensionsseries AS view_plant_dimensionsseries_1 ON
plant_dimensionsseries.plant_id =view_plant_dimensionsseries_1.plant_id
AND plant_dimensionsseries.dimensionsseriestype_id
=view_plant_dimensionsseries_1.dimensionsseriestype_id
# query 2
SELECT ...
FROM plant LEFT OUTER JOIN plant_dimensionsseries ON plant.id
=plant_dimensionsseries.plant_id *LEFT OUTER
JOIN*view_plant_dimensionsseries AS view_plant_dimensionsseries_1 ON
plant_dimensionsseries.plant_id =view_plant_dimensionsseries_1.plant_id
AND plant_dimensionsseries.dimensionsseriestype_id
=view_plant_dimensionsseries_1.dimensionsseriestype_id

|

Result with SQLAlchemy 1.0.12:
|
# query 1
SELECT ...
FROM plant LEFT OUTER JOIN plant_dimensionsseries ON plant.id
=plant_dimensionsseries.plant_id *JOIN*view_plant_dimensionsseries AS
view_plant_dimensionsseries_1 ON plant_dimensionsseries.plant_id
=view_plant_dimensionsseries_1.plant_id AND
plant_dimensionsseries.dimensionsseriestype_id
=view_plant_dimensionsseries_1.dimensionsseriestype_id
# query 2
SELECT ...
FROM plant LEFT OUTER JOIN plant_dimensionsseries ON plant.id
=plant_dimensionsseries.plant_id *LEFT OUTER
JOIN*view_plant_dimensionsseries AS view_plant_dimensionsseries_1 ON
plant_dimensionsseries.plant_id =view_plant_dimensionsseries_1.plant_id
AND plant_dimensionsseries.dimensionsseriestype_id
=view_plant_dimensionsseries_1.dimensionsseriestype_id
|

In query 1 under SQLAlchemy 1.0.12, the query discard many rows due the
JOIN chained to the LEFT OUTER JOIN and this precisely what SQLAlchemy
wanted to avoid if I refer to the description of "Right-nested inner
joins available in joined eager loads"
(http://docs.sqlalchemy.org/en/rel_1_0/changelog/migration_09.html#feature-2976).

I can fix the query 1 by adding the |innerjoin=False| as in query 2 but
I am wondering if this is a bug or the expected behavior following the
behavioral change in SQLAlchemy 1.0.x
(http://docs.sqlalchemy.org/en/rel_1_0/changelog/migration_10.html#right-inner-join-nesting-now-the-default-for-joinedload-with-innerjoin-true).
Is this a bug?

--
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 [email protected]
<mailto:[email protected]>.
To post to this group, send email to [email protected]
<mailto:[email protected]>.
Visit this group at https://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 [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to