Skip to content

Joined tables inheritance and Many To One lead to incorrect SQL #1082

Closed
@sqlalchemy-bot

Description

@sqlalchemy-bot
Collaborator

Migrated issue, originally created by Anonymous

Version '0.4.2p3'

Python Program that should be copy/pastable to reproduce the bug

import sqlalchemy
from sqlalchemy import create_engine, Table, Column, Integer, String, MetaData, ForeignKey
from sqlalchemy.orm import mapper, relation, sessionmaker, scoped_session, eagerload
from sqlalchemy.sql import select

import os

STATUS_INCONSISTENT = 'INCONSISTENT'
STATUS_OK = 'OK'

IMAGE_FORMAT_JPEG = 'JPEG'

class AbstractItem(object):
def init(self, itemId, status, size, format):
super(AbstractItem, self).init()

    self.id = itemId
    self.status = status
    self.width = size[0](0) if type(size[0](0)) == int else int(size[0](0))
    self.height = size[1](1) if type(size[1](1)) == int else int(size[1](1))
    self.format = format

class OriginalItem(AbstractItem):
def init(self, itemId, status, size, format):
assert itemId is not None
super(OriginalItem, self).init(itemId, status, size, format)

class DerivedItem(AbstractItem):
def init(self, status, size, format, originalItem):
self.originalItem = originalItem
super(DerivedItem, self).init("%s-%sx%s-%s" % (originalItem.id, size0, size1, format),status, size, format)

engine = create_engine('sqlite:///:memory:', encoding='utf-8', echo=False)
metadata = MetaData()
sm = sessionmaker(bind=engine, autoflush=True, transactional=True)

abstract_item = Table('abstract_item', metadata,
Column('id', String(255), primary_key=True),
Column('status', String(255), index=True, nullable=False),
Column('width', Integer, index=True, nullable=False),
Column('height', Integer, index=True, nullable=False),
Column('format', String(255), index=True, nullable=False),
Column('type', String(255), nullable=False)
)

original_item = Table('original_item', metadata,
Column('id', String(255), ForeignKey('abstract_item.id'), primary_key=True),
)

derived_item = Table('derived_item', metadata,
Column('id', String(255), ForeignKey('abstract_item.id'), primary_key=True),
Column('original_item_id', String(255), ForeignKey('original_item.id', ondelete="CASCADE"))
)

mapper(AbstractItem, abstract_item, polymorphic_on=abstract_item.c.type, polymorphic_identity='ABSTRACT_ITEM')
mapper(OriginalItem, original_item, inherits=AbstractItem, polymorphic_identity='ORIGINAL_ITEM')
mapper(DerivedItem, derived_item,
properties={
'originalItem' : relation(OriginalItem, primaryjoin=derived_item.c.original_item_id==original_item.c.id)
}, inherits=AbstractItem , polymorphic_identity='DERIVED_ITEM')

metadata.create_all(engine)

session = sm()
originalItem = OriginalItem('MYID12435', STATUS_OK, (800, 600), IMAGE_FORMAT_JPEG)
session.save(originalItem)
session.commit()
session.close()

session = sm()
item = DerivedItem(STATUS_OK, (100, 100), IMAGE_FORMAT_JPEG, originalItem)
session.save(item)
session.commit()
session.close()

session = sm()
foundItem = session.query(DerivedItem)
.filter_by(width=100)
.filter_by(height=100)
.filter_by(format=IMAGE_FORMAT_JPEG)
.join('originalItem')
.filter_by(id='MYID12435')
.first()

assert foundItem is not None
assert foundItem.status == STATUS_OK
assert foundItem.width == 100
assert foundItem.height == 100
assert foundItem.format == IMAGE_FORMAT_JPEG
assert foundItem.originalItem.id == 'MYID12435'
assert foundItem.originalItem.status == STATUS_OK
assert foundItem.originalItem.width == 800
assert foundItem.originalItem.height == 600
assert foundItem.originalItem.format == IMAGE_FORMAT_JPEG

session.commit()
session.close()

=>The generated SQL is wrong, and what I get is :
sqlalchemy.exceptions.OperationalError: (OperationalError) no such column: original_item.id u'SELECT abstract_item.id AS abstract_item_id, derived_item.id AS derived_item_id, abstract_item.status AS abstract_item_status, abstract_item.width AS abstract_item_width, abstract_item.height AS abstract_item_height, abstract_item.format AS abstract_item_format, abstract_item.type AS abstract_item_type, derived_item.original_item_id AS derived_item_original_item_id \nFROM abstract_item JOIN derived_item ON abstract_item.id = derived_item.id JOIN (abstract_item JOIN original_item ON abstract_item.id = original_item.id) ON derived_item.original_item_id = original_item.id \nWHERE abstract_item.width = ? AND abstract_item.height = ? AND abstract_item.format = ? AND abstract_item.id = ? ORDER BY abstract_item.oid \n LIMIT 1 OFFSET 0' 100, 'JPEG', 'MYID12435'


Attachments: bug.py | bug.2.py

Activity

sqlalchemy-bot

sqlalchemy-bot commented on Jun 22, 2008

@sqlalchemy-bot
CollaboratorAuthor

Michael Bayer (@zzzeek) wrote:

the join is self referential between abstract_item->abstract_item and must be aliased:

foundItem = session.query(DerivedItem)\
    .filter_by(width=100)\
    .filter_by(height=100)\
    .filter_by(format=IMAGE_FORMAT_JPEG)\
    .join('originalItem', aliased=True)\
    .filter_by(id='MYID12435')\
    .first()

Upgrade to 0.4.6 and your original statement is disallowed, since later versions of 0.4 detect the self-referential join for you. But works in 0.4.2p3 as well (but please upgrade to 0.4.6 and only report bugs against the most recently released versions of SQLA).

sqlalchemy-bot

sqlalchemy-bot commented on Jun 22, 2008

@sqlalchemy-bot
CollaboratorAuthor

Changes by Michael Bayer (@zzzeek):

  • added labels: wontfix
  • changed status to closed
sqlalchemy-bot

sqlalchemy-bot commented on Jun 22, 2008

@sqlalchemy-bot
CollaboratorAuthor

Anonymous wrote:

OK, so I upgraded to 0.4.6, and I still have an issue with the SQL that is generated when using lazy=False :
mapper(DerivedItem, derived_item,
properties={
'originalItem' : relation(OriginalItem, primaryjoin=derived_item.c.original_item_id==original_item.c.id,lazy=False, join_depth=2)
}, inherits=AbstractItem , polymorphic_identity='DERIVED_ITEM')

(same with join_depth=1 and join_depth=3

What I get is :

sqlalchemy.exceptions.OperationalError: (OperationalError) no such column: anon_2.abstract_item_id u'SELECT anon_1.derived_item_original_item_id AS anon_1_derived_item_original_item_id, anon_1.abstract_item_id AS anon_1_abstract_item_id, anon_1.derived_item_id AS anon_1_derived_item_id, anon_1.abstract_item_status AS anon_1_abstract_item_status, anon_1.abstract_item_width AS anon_1_abstract_item_width, anon_1.abstract_item_height AS anon_1_abstract_item_height, anon_1.abstract_item_format AS anon_1_abstract_item_format, anon_1.abstract_item_type AS anon_1_abstract_item_type, anon_2.abstract_item_id AS anon_2_abstract_item_id, anon_2.original_item_id AS anon_2_original_item_id, anon_2.abstract_item_status AS anon_2_abstract_item_status, anon_2.abstract_item_width AS anon_2_abstract_item_width, anon_2.abstract_item_height AS anon_2_abstract_item_height, anon_2.abstract_item_format AS anon_2_abstract_item_format, anon_2.abstract_item_type AS anon_2_abstract_item_type \nFROM (SELECT derived_item.original_item_id AS derived_item_original_item_id, abstract_item.id AS abstract_item_id, derived_item.id AS derived_item_id, abstract_item.status AS abstract_item_status, abstract_item.width AS abstract_item_width, abstract_item.height AS abstract_item_height, abstract_item.format AS abstract_item_format, abstract_item.type AS abstract_item_type, abstract_item.oid AS abstract_item_oid \nFROM abstract_item JOIN derived_item ON abstract_item.id = derived_item.id JOIN (SELECT abstract_item.id AS abstract_item_id, abstract_item.status AS abstract_item_status, abstract_item.width AS abstract_item_width, abstract_item.height AS abstract_item_height, abstract_item.format AS abstract_item_format, abstract_item.type AS abstract_item_type, original_item.id AS original_item_id \nFROM abstract_item JOIN original_item ON abstract_item.id = original_item.id) AS anon_3 ON derived_item.original_item_id = anon_3.original_item_id \nWHERE abstract_item.width = ? AND abstract_item.height = ? AND abstract_item.format = ? AND anon_3.abstract_item_id = ? ORDER BY abstract_item.oid \n LIMIT 1 OFFSET 0) AS anon_1 LEFT OUTER JOIN (SELECT anon_1.derived_item_original_item_id AS anon_1_derived_item_original_item_id, anon_1.abstract_item_id AS anon_1_abstract_item_id, anon_1.derived_item_id AS anon_1_derived_item_id, anon_1.abstract_item_status AS anon_1_abstract_item_status, anon_1.abstract_item_width AS anon_1_abstract_item_width, anon_1.abstract_item_height AS anon_1_abstract_item_height, anon_1.abstract_item_format AS anon_1_abstract_item_format, anon_1.abstract_item_type AS anon_1_abstract_item_type \nFROM (SELECT derived_item.original_item_id AS derived_item_original_item_id, abstract_item.id AS abstract_item_id, derived_item.id AS derived_item_id, abstract_item.status AS abstract_item_status, abstract_item.width AS abstract_item_width, abstract_item.height AS abstract_item_height, abstract_item.format AS abstract_item_format, abstract_item.type AS abstract_item_type, abstract_item.oid AS abstract_item_oid \nFROM abstract_item JOIN derived_item ON abstract_item.id = derived_item.id JOIN (SELECT abstract_item.id AS abstract_item_id, abstract_item.status AS abstract_item_status, abstract_item.width AS abstract_item_width, abstract_item.height AS abstract_item_height, abstract_item.format AS abstract_item_format, abstract_item.type AS abstract_item_type, original_item.id AS original_item_id \nFROM abstract_item JOIN original_item ON abstract_item.id = original_item.id) AS anon_3 ON derived_item.original_item_id = anon_3.original_item_id \nWHERE abstract_item.width = ? AND abstract_item.height = ? AND abstract_item.format = ? AND anon_3.abstract_item_id = ? ORDER BY abstract_item.oid \n LIMIT 1 OFFSET 0) AS anon_1) AS anon_2 ON anon_1.derived_item_original_item_id = anon_2.original_item_id ORDER BY anon_1.oid, anon_2.oid' 100, 'JPEG', 'MYID12435', 100, 100, 'JPEG', 'MYID12435'

However, SA 0.5 beta does not complain, but does not perform the eager loading either, which leads to the following exception:
sqlalchemy/orm/mapper.py", line 1580, in _load_scalar_attributes
raise sa_exc.UnboundExecutionError("Instance %s is not bound to a Session; attribute refresh operation cannot proceed" % (state_str(state)))
sqlalchemy.exc.UnboundExecutionError: Instance OriginalItem@0xbb4450 is not bound to a Session; attribute refresh operation cannot proceed

I attach the new bug.py

sqlalchemy-bot

sqlalchemy-bot commented on Jun 22, 2008

@sqlalchemy-bot
CollaboratorAuthor

Changes by Anonymous:

  • changed status to reopened
sqlalchemy-bot

sqlalchemy-bot commented on Jun 22, 2008

@sqlalchemy-bot
CollaboratorAuthor

Anonymous wrote:

bug.py with lazy loading turned off

sqlalchemy-bot

sqlalchemy-bot commented on Jun 22, 2008

@sqlalchemy-bot
CollaboratorAuthor

Changes by Anonymous:

  • attached file bug.2.py
sqlalchemy-bot

sqlalchemy-bot commented on Jun 22, 2008

@sqlalchemy-bot
CollaboratorAuthor

Michael Bayer (@zzzeek) wrote:

version 0.5 had a slight issue with both tests regarding query.join() from a joined-table mapper to an aliased joined-table mapper on the same base table, this is fixed in 3e8b095. The eager loading you're looking for is supported in version 0.5.

sqlalchemy-bot

sqlalchemy-bot commented on Jun 22, 2008

@sqlalchemy-bot
CollaboratorAuthor

Changes by Michael Bayer (@zzzeek):

  • changed milestone from "0.4.xx" to "0.5.0"
  • changed status to closed
sqlalchemy-bot

sqlalchemy-bot commented on Jun 22, 2008

@sqlalchemy-bot
CollaboratorAuthor

Michael Bayer (@zzzeek) wrote:

Replying to guest:

sqlalchemy/orm/mapper.py", line 1580, in _load_scalar_attributes
raise sa_exc.UnboundExecutionError("Instance %s is not bound to a Session; attribute refresh operation cannot proceed" % (state_str(state)))
sqlalchemy.exc.UnboundExecutionError: Instance OriginalItem@0xbb4450 is not bound to a Session; attribute refresh operation cannot proceed

this error is because the Session has expired its contents after the commit(). You have to leave the session opened in order for the objects committed to be used, since they need to refresh their contents. Read over http://www.sqlalchemy.org/docs/05/session.html#unitofwork_using_committing for info on this.

added this to the 0.5.0 milestone on Nov 27, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @sqlalchemy-bot

        Issue actions

          Joined tables inheritance and Many To One lead to incorrect SQL · Issue #1082 · sqlalchemy/sqlalchemy