Skip to content

Commit

Permalink
- move dialect into a package
Browse files Browse the repository at this point in the history
- add orm package
- implement rudimental nested query rendering
- simple tests
  • Loading branch information
zzzeek committed Oct 4, 2012
1 parent fd3e80c commit 501a462
Show file tree
Hide file tree
Showing 10 changed files with 178 additions and 6 deletions.
4 changes: 2 additions & 2 deletions run_tests.py
@@ -1,7 +1,7 @@
from sqlalchemy.dialects import registry

registry.register("akiban", "sqlalchemy_akiban.psycopg2", "AkibanPsycopg2Dialect")
registry.register("akiban.psycopg2", "sqlalchemy_akiban.psycopg2", "AkibanPsycopg2Dialect")
registry.register("akiban", "sqlalchemy_akiban.dialect.psycopg2", "AkibanPsycopg2Dialect")
registry.register("akiban.psycopg2", "sqlalchemy_akiban.dialect.psycopg2", "AkibanPsycopg2Dialect")

from sqlalchemy.testing import runner

Expand Down
4 changes: 2 additions & 2 deletions setup.py
Expand Up @@ -35,8 +35,8 @@
zip_safe=False,
entry_points={
'sqlalchemy.dialects': [
'akiban = sqlalchemy_akiban.psycopg2:AkibanPsycopg2Dialect',
'akiban.psycopg2 = sqlalchemy_akiban.psycopg2:AkibanPsycopg2Dialect',
'akiban = sqlalchemy_akiban.dialect.psycopg2:AkibanPsycopg2Dialect',
'akiban.psycopg2 = sqlalchemy_akiban.dialect.psycopg2:AkibanPsycopg2Dialect',
]
}
)
4 changes: 2 additions & 2 deletions sqlalchemy_akiban/__init__.py
Expand Up @@ -2,6 +2,6 @@

from sqlalchemy.dialects import registry

registry.register("akiban", "sqlalchemy_akiban.psycopg2", "AkibanPsycopg2Dialect")
registry.register("akiban+psycopg2", "sqlalchemy_akiban.psycopg2", "AkibanPsycopg2Dialect")
registry.register("akiban", "sqlalchemy_akiban.dialect.psycopg2", "AkibanPsycopg2Dialect")
registry.register("akiban+psycopg2", "sqlalchemy_akiban.dialect.psycopg2", "AkibanPsycopg2Dialect")

Empty file.
File renamed without changes.
File renamed without changes.
5 changes: 5 additions & 0 deletions sqlalchemy_akiban/orm/__init__.py
@@ -0,0 +1,5 @@
from . import strategy
from sqlalchemy.orm import strategies as _sqla_strat

def nestedload(*keys, **kw):
return _sqla_strat.EagerLazyOption(keys, lazy='akiban_nested')
84 changes: 84 additions & 0 deletions sqlalchemy_akiban/orm/strategy.py
@@ -0,0 +1,84 @@
from sqlalchemy.orm.strategies import AbstractRelationshipLoader
from sqlalchemy.orm.strategies import LazyLoader
from sqlalchemy.orm.strategies import _factory
from sqlalchemy.orm import interfaces
from sqlalchemy.orm import attributes
from sqlalchemy.orm import loading
from sqlalchemy.orm import util as orm_util
from sqlalchemy.sql import util as sql_util
from sqlalchemy import util
from sqlalchemy import log
from sqlalchemy import select
from sqlalchemy import exc as sa_exc

class NestedLoader(AbstractRelationshipLoader):
def __init__(self, parent):
super(NestedLoader, self).__init__(parent)
self.join_depth = self.parent_property.join_depth

def init_class_attribute(self, mapper):
self.parent_property.\
_get_strategy(LazyLoader).init_class_attribute(mapper)

def setup_query(self, context, entity, path, adapter, \
column_collection=None,
parentmapper=None,
**kwargs):

if not context.query._enable_eagerloads:
return

path = path[self.key]

with_polymorphic = None

# if not via query option, check for
# a cycle
if not path.contains(context, "loaderstrategy"):
if self.join_depth:
if path.length / 2 > self.join_depth:
return
elif path.contains_mapper(self.mapper):
return

#if parentmapper is None:
# localparent = entity.mapper
#else:
# localparent = parentmapper

source_selectable = entity.selectable

with_poly_info = path.get(
context,
"path_with_polymorphic",
None
)
if with_poly_info is not None:
with_polymorphic = with_poly_info.with_polymorphic_mappers
else:
with_polymorphic = None

pj, sj, source, dest, secondary, target_adapter = \
self.parent_property._create_joins(dest_polymorphic=True,
source_selectable=source_selectable)

add_to_collection = []

path = path[self.mapper]
for value in self.mapper._iterate_polymorphic_properties(
mappers=with_polymorphic):
value.setup(
context,
entity,
path,
None,
parentmapper=self.mapper,
column_collection=add_to_collection)

context.secondary_columns.append(
select(add_to_collection).where(pj).as_scalar()
)

log.class_logger(NestedLoader)

_factory["akiban_nested"] = NestedLoader
24 changes: 24 additions & 0 deletions test/fixtures.py
@@ -0,0 +1,24 @@
from sqlalchemy import Table, Column, Integer, String, Numeric, ForeignKey

def cust_order_item(metadata):
Table('customer',
metadata,
Column('id', Integer, primary_key=True),
Column('name', String(20)),
)

Table('order',
metadata,
Column('id', Integer, primary_key=True),
Column('customer_id', Integer, ForeignKey('customer.id')),
Column('order_info', String(20)),
)

Table('item',
metadata,
Column('id', Integer, primary_key=True),
Column('order_id', Integer, ForeignKey('order.id')),
Column('price', Numeric(10, 2)),
Column('quanity', Integer)
)

59 changes: 59 additions & 0 deletions test/test_nested_loading.py
@@ -0,0 +1,59 @@
from sqlalchemy.testing import fixtures
from sqlalchemy.testing.assertions import eq_, is_, AssertsCompiledSQL
from sqlalchemy.orm import relationship, Session, mapper
from .fixtures import cust_order_item

from sqlalchemy_akiban import orm

class RenderTest(fixtures.MappedTest, AssertsCompiledSQL):
__dialect__ = 'akiban'

@classmethod
def define_tables(cls, metadata):
cust_order_item(metadata)

@classmethod
def setup_classes(cls):
class Customer(cls.Comparable):
pass
class Order(cls.Comparable):
pass
class Item(cls.Comparable):
pass

@classmethod
def setup_mappers(cls):
Customer, Order, Item = cls.classes.Customer, \
cls.classes.Order, \
cls.classes.Item
customer, order, item = cls.tables.customer,\
cls.tables.order,\
cls.tables.item
mapper(Customer, customer, properties={
'orders': relationship(Order, backref="customer")
})
mapper(Order, order, properties={
'items': relationship(Item, backref="order")
})
mapper(Item, item)

def test_option_creation(self):
from sqlalchemy.orm.strategies import EagerLazyOption
Customer = self.classes.Customer
opt = orm.nestedload(Customer.orders)
assert isinstance(opt, EagerLazyOption)
is_(opt.key[0], Customer.orders)

def test_render_basic_nested(self):
Customer = self.classes.Customer
s = Session()
q = s.query(Customer).options(orm.nestedload(Customer.orders))
self.assert_compile(
q,
'SELECT customer.id AS customer_id, '
'customer.name AS customer_name, (SELECT "order".id, '
'"order".customer_id, "order".order_info FROM "order" '
'WHERE customer.id = "order".customer_id) AS anon_1 '
'FROM customer'
)

0 comments on commit 501a462

Please sign in to comment.