SlideShare a Scribd company logo
Object-Relational Mapper
Alexey Malashkevich @ponyorm
What makes Pony ORM different?
Fast object-relational mapper
which uses Python generators for
writing database queries
A Python generator
(p.name for p in product_list if p.price > 100)
A Python generator vs a SQL query
SELECT p.name
FROM Products p
WHERE p.price > 100
(p.name for p in product_list if p.price > 100)
(p.name for p in product_list if p.price > 100)
SELECT p.name
FROM Products p
WHERE p.price > 100
A Python generator vs a SQL query
(p.name for p in product_list if p.price > 100)
SELECT p.name
FROM Products p
WHERE p.price > 100
A Python generator vs a SQL query
(p.name for p in product_list if p.price > 100)
SELECT p.name
FROM Products p
WHERE p.price > 100
A Python generator vs a SQL query
The same query in Pony
SELECT p.name
FROM Products p
WHERE p.price > 100
select(p.name for p in Product if p.price > 100)
• Pony ORM
• Django
• SQL Alchemy
Query syntax comparison
Pony ORM:
select(p for p in Product
if p.name.startswith('A') and p.image is None
or p.added.year < 2014)
Query syntax comparison
Django:
Product.objects.filter(
Q(name__startswith='A', image__isnull=True)
| Q(added__year__lt=2014))
Query syntax comparison
SQLAlchemy:
session.query(Product).filter(
(Product.name.startswith('A')
& (Product.image == None))
| (extract('year', Product.added) < 2014))
Query syntax comparison
session.query(Product).filter(
(Product.name.startswith('A') & (Product.image == None))
| (extract('year', Product.added) < 2014))
Query syntax comparison
Product.objects.filter(
Q(name__startswith='A', image__isnull=True)
| Q(added__year__lt=2014))
select(p for p in Product
if p.name.startswith('A') and p.image is None
or p.added.year < 2014)
Pony
Django
SQLAlchemy
Query translation
select(p for p in Product
if p.name.startswith('A') and p.image is None
or p.added.year < 2014)
• Translation from the bytecode is fast
• The bytecode translation result is cached
• The Python generator object is used as a
cache key
Python generator object
Building a query step by step
q = select(o for o in Order if o.customer.id == some_id)
q = q.filter(lambda o: o.state != 'DELIVERED')
q = q.filter(lambda o: len(o.items) > 2)
q = q.order_by(Order.date_created)
q = q[10:20]
SELECT "o"."id"
FROM "Order" "o"
LEFT JOIN "OrderItem" "orderitem-1"
ON "o"."id" = "orderitem-1"."order"
WHERE "o"."customer" = ?
AND "o"."state" <> 'DELIVERED'
GROUP BY "o"."id"
HAVING COUNT("orderitem-1"."ROWID") > 2
ORDER BY "o"."date_created"
LIMIT 10 OFFSET 10
How Pony translates generator
expressions to SQL?
Python generator to SQL translation
1. Decompile bytecode and restore AST
2. Translate AST to ‘abstract SQL’
3. Translate ‘abstract SQL’ to a specific SQL
dialect
Python generator to SQL translation
1. Decompile bytecode and restore AST
2. Translate AST to ‘abstract SQL’
3. Translate ‘abstract SQL’ to a concrete
SQL dialect
Bytecode decompilation
• Using the Visitor pattern
• Methods of the Visitor object correspond
the byte code commands
• Pony keeps fragments of AST at the stack
• Each method either adds a new part of AST
or combines existing parts
(a + b.c) in x.y
Bytecode decompilation
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Bytecode decompilation
Bytecode decompilation
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Stack
Bytecode decompilation
(a + b.c) in x.y
> LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Stack
Name('a')
Bytecode decompilation
(a + b.c) in x.y
LOAD_GLOBAL a
> LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Stack
Name('b')
Name('a')
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
> LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Stack
Getattr(Name('b'), 'c')
Name('a')
Bytecode decompilation
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
> BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Stack
Add(Name('a'),
Getattr(Name('b'), 'c'))
Bytecode decompilation
Bytecode decompilation
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
> LOAD_FAST x
LOAD_ATTR y
COMPARE_OP in
Stack
Name('x')
Add(Name('a'),
Getattr(Name('b'), 'c'))
Bytecode decompilation
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
> LOAD_ATTR y
COMPARE_OP in
Stack
Getattr(Name('x'), 'y')
Add(Name('a'),
Getattr(Name('b'), 'c'))
Bytecode decompilation
(a + b.c) in x.y
LOAD_GLOBAL a
LOAD_FAST b
LOAD_ATTR c
BINARY_ADD
LOAD_FAST x
LOAD_ATTR y
> COMPARE_OP in
Stack
Compare('in',
Add(…), Getattr(…))
Abstract Syntax Tree (AST)
a
in
+
.c
b
.y
x
(a + b.c) in x.y
Python generator to SQL translation
1. Decompile bytecode and restore AST
2. Translate AST to ‘abstract SQL’
3. Translate ‘abstract SQL’ to a concrete
SQL dialect
What SQL it should be translated to?
a
in
+
.c
b
.y
x
(a + b.c) in x.y
It depends on variables types!
What SQL it should be translated to?
(a + b.c) in x.y
• If a and c are numbers, y is a collection
(? + "b"."c") IN (SELECT …)
• If a and c are strings, y is a collection
CONCAT(?, "b"."c") IN (SELECT …)
• If a, c and y are strings
“x"."y" LIKE CONCAT('%', ?, "b"."c", '%')
What SQL it should be translated to?
(a + b.c) in x.y
• The result of translation depends on types
• If the translator analyzes node types by
itself, the logic becomes too complex
• Pony uses Monads to keep it simple
(a + b.c) in x.y
AST to SQL Translation
• Encapsulates the node translation logic
• Generates the result of translation - ‘the
abstract SQL’
• Can combine itself with other monads
The translator delegates the logic of translation
to monads
A Monad
• StringAttrMonad
• StringParamMonad
• StringExprMonad
• StringConstMonad
• DatetimeAttrMonad
• DatetimeParamMonad
• ObjectAttrMonad
• CmpMonad
• etc…
Each monad defines a set of allowed operations and can
translate itself into a part of resulting SQL query
Monad types
AST Translation
• Using the Visitor pattern
• Walk the tree in depth-first order
• Create monads when leaving each node
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
StringParam
Monad
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
StringParam
Monad
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
StringParam
Monad
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
StringParam
Monad
ObjectIter
Monad
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
StringParam
Monad
ObjectIter
Monad
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
StringParam
Monad
ObjectIter
Monad
StringAttr
Monad
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
StringParam
Monad
ObjectIter
Monad
StringAttr
Monad
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
StringParam
Monad
ObjectIter
Monad
StringAttr
Monad
StringExpr
Monad
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
StringParam
Monad
ObjectIter
Monad
StringAttr
Monad
StringExpr
Monad
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
StringParam
Monad
ObjectIter
Monad
StringAttr
Monad
StringExpr
Monad
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
StringParam
Monad
ObjectIter
Monad
StringAttr
Monad
StringExpr
Monad
ObjectIter
Monad
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
StringParam
Monad
ObjectIter
Monad
StringAttr
Monad
StringExpr
Monad
ObjectIter
Monad
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
StringParam
Monad
ObjectIter
Monad
StringAttr
Monad
StringExpr
Monad
ObjectIter
Monad
StringAttr
Monad
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
StringParam
Monad
ObjectIter
Monad
StringAttr
Monad
StringExpr
Monad
ObjectIter
Monad
StringAttr
Monad
(a + b.c) in x.y
AST to SQL Translation
in
.y
x
+
a .c
b
StringParam
Monad
ObjectIter
Monad
StringAttr
Monad
StringExpr
Monad
ObjectIter
Monad
StringAttr
Monad
Cmp
Monad
Abstract SQL
(a + b.c) in x.y
['LIKE', ['COLUMN', 't1', 'y'],
['CONCAT',
['VALUE', '%'], ['PARAM', 'p1'],
['COLUMN', 't2', 'c'], ['VALUE', '%']
]
]
Allows to put aside the SQL dialect differences
Python generator to SQL translation
1. Decompile bytecode and restore AST
2. Translate AST to ‘abstract SQL’
3. Translate ‘abstract SQL’ to a specific SQL
dialect
Specific SQL dialects
['LIKE', ['COLUMN', 't1', 'y'],
['CONCAT',
['VALUE', '%'], ['PARAM', 'p1'],
['COLUMN', 't2', 'c'], ['VALUE', '%']
]
]
MySQL:
`t1`.`y` LIKE CONCAT('%', ?, `t2`.`c`, '%')
SQLite:
"t1"."y" LIKE '%' || ? || "t2"."c" || '%'
Other Pony ORM features
• Identity Map
• Automatic query optimization
• N+1 Query Problem solution
• Optimistic transactions
• Online ER Diagram Editor
Django ORM
s1 = Student.objects.get(pk=123)
print s1.name, s1.group.id
s2 = Student.objects.get(pk=456)
print s2.name, s2.group.id
• How many SQL queries will be executed?
• How many objects will be created?
Django ORM
s1 = Student.objects.get(pk=123)
print s1.name, s1.group.id
s2 = Student.objects.get(pk=456)
print s2.name, s2.group.id
Student 123
Django ORM
s1 = Student.objects.get(pk=123)
print s1.name, s1.group.id
s2 = Student.objects.get(pk=456)
print s2.name, s2.group.id
Student 123 Group 1
Django ORM
s1 = Student.objects.get(pk=123)
print s1.name, s1.group.id
s2 = Student.objects.get(pk=456)
print s2.name, s2.group.id
Student 123
Student 456
Group 1
Django ORM
s1 = Student.objects.get(pk=123)
print s1.name, s1.group.id
s2 = Student.objects.get(pk=456)
print s2.name, s2.group.id
Student 123
Student 456
Group 1
Group 1
Pony ORM
s1 = Student[123]
print s1.name, s1.group.id
s2 = Student[456]
print s2.name, s2.group.id
Pony ORM – seeds, IdentityMap
s1 = Student[123]
print s1.name, s1.group.id
s2 = Student[456]
print s2.name, s2.group.id
Student 123
Group 1
Pony ORM – seeds, IdentityMap
s1 = Student[123]
print s1.name, s1.group.id
s2 = Student[456]
print s2.name, s2.group.id
Student 123
Group 1
seed
Pony ORM – seeds, IdentityMap
s1 = Student[123]
print s1.name, s1.group.id
s2 = Student[456]
print s2.name, s2.group.id
Student 123
Group 1
seed
Pony ORM – seeds, IdentityMap
s1 = Student[123]
print s1.name, s1.group.id
s2 = Student[456]
print s2.name, s2.group.id
Student 123
Student 456
Group 1
seed
Pony ORM – seeds, IdentityMap
s1 = Student[123]
print s1.name, s1.group.id
s2 = Student[456]
print s2.name, s2.group.id
Student 123
Student 456
Group 1
seed
Solution for the N+1 Query Problem
orders = select(o for o in Order if o.total_price > 1000) 
.order_by(desc(Order.id)).page(1, pagesize=5)
for o in orders:
print o.total_price, o.customer.name
1
SELECT o.id, o.total_price, o.customer_id,...
FROM "Order" o
WHERE o.total_price > 1000
ORDER BY o.id DESC
LIMIT 5
Order 1
Order 3
Order 4
Order 7
Order 9
Customer 1
Customer 4
Customer 7
Solution for the N+1 Query Problem
Order 1
Order 3
Order 4
Order 7
Order 9
Customer 1
Customer 4
Customer 7
Solution for the N+1 Query Problem
One SQL query
Solution for the N+1 Query Problem
1
1
SELECT c.id, c.name, …
FROM “Customer” c
WHERE c.id IN (?, ?, ?)
orders = select(o for o in Order if o.total_price > 1000) 
.order_by(desc(Order.id)).page(1, pagesize=5)
for o in orders:
print o.total_price, o.customer.name
SELECT o.id, o.total_price, o.customer_id,...
FROM "Order" o
WHERE o.total_price > 1000
ORDER BY o.id DESC
LIMIT 5
Automatic query optimization
select(c for c in Customer
if sum(c.orders.total_price) > 1000)
SELECT "c"."id", "c"."email", "c"."password", "c"."name",
"c"."country", "c"."address"
FROM "Customer" "c"
WHERE (
SELECT coalesce(SUM("order-1"."total_price"), 0)
FROM "Order" "order-1"
WHERE "c"."id" = "order-1"."customer"
) > 1000
SELECT "c"."id"
FROM "Customer" "c"
LEFT JOIN "Order" "order-1"
ON "c"."id" = "order-1"."customer"
GROUP BY "c"."id"
HAVING coalesce(SUM("order-1"."total_price"), 0) > 1000
Transactions
def transfer_money(id1, id2, amount):
account1 = Account.objects.get(pk=id1)
if account1.amount < amount:
raise ValueError('Not enough funds!')
account2 = Account.object.get(pk=id2)
account1.amount -= amount
account1.save()
account2.amount += amount
account2.save()
Django ORM
@transaction.atomic
def transfer_money(id1, id2, amount):
account1 = Account.objects.get(pk=id1)
if account1.amount < amount:
raise ValueError('Not enough funds!')
account2 = Account.object.get(pk=id2)
account1.amount -= amount
account1.save()
account2.amount += amount
account2.save()
Transactions
Django ORM
@transaction.atomic
def transfer_money(id1, id2, amount):
account1 = Account.objects 
.select_for_update.get(pk=id1)
if account1.amount < amount:
raise ValueError('Not enough funds!')
account2 = Account.objects 
.select_for_update.get(pk=id2)
account1.amount -= amount
account1.save()
account2.amount += amount
account2.save()
Transactions
Django ORM
@db_session
def transfer_money(id1, id2, amount):
account1 = Account[id1]
if account1.amount < amount:
raise ValueError('Not enough funds!')
account1.amount -= amount
Account[id2].amount += amount
Transactions
Pony ORM
db_session
• Pony tracks which objects where changed
• No need to call save()
• Pony saves all updated objects in a single
transaction automatically on leaving the
db_session scope
Transactions
UPDATE Account
SET amount = :new_value
WHERE id = :id
AND amount = :old_value
Optimistic Locking
Optimistic Locking
• Pony tracks attributes which were read and
updated
• If object wasn’t locked using the for_update
method, Pony uses the optimistic locking
automatically
Entity-Relationship Diagram Editor
https://editor.ponyorm.com
Entity-Relationship Diagram Editor
https://editor.ponyorm.com
Entity-Relationship Diagram Editor
https://editor.ponyorm.com
Main Pony ORM features:
• Using generators for database queries
• Identity Map
• Solution for N+1 Query Problem
• Automatic query optimization
• Optimistic transactions
• Online ER-diagram editor
Wrapping up
• Python 3
• Microsoft SQL Server support
• Improved documentation
• Migrations
• Ansync queries
Pony roadmap
• Site ponyorm.com
• Twitter @ponyorm
• Github github.com/ponyorm/pony
• ER-Diagram editor editor.ponyorm.com
• Installation: pip install pony
Thank you!
Pony ORM

More Related Content

PDF
Scalaz Stream: Rebirth
John De Goes
 
PDF
Parallel-Ready Java Code: Managing Mutation in an Imperative Language
Maurice Naftalin
 
PDF
Let's Get to the Rapids
Maurice Naftalin
 
PDF
Good and Wicked Fairies, and the Tragedy of the Commons: Understanding the Pe...
Maurice Naftalin
 
PDF
Shooting the Rapids
Maurice Naftalin
 
PDF
Halogen: Past, Present, and Future
John De Goes
 
PDF
Shooting the Rapids: Getting the Best from Java 8 Streams
Maurice Naftalin
 
KEY
RIAs Done Right: Grails, Flex, and EXT GWT
Michael Galpin
 
Scalaz Stream: Rebirth
John De Goes
 
Parallel-Ready Java Code: Managing Mutation in an Imperative Language
Maurice Naftalin
 
Let's Get to the Rapids
Maurice Naftalin
 
Good and Wicked Fairies, and the Tragedy of the Commons: Understanding the Pe...
Maurice Naftalin
 
Shooting the Rapids
Maurice Naftalin
 
Halogen: Past, Present, and Future
John De Goes
 
Shooting the Rapids: Getting the Best from Java 8 Streams
Maurice Naftalin
 
RIAs Done Right: Grails, Flex, and EXT GWT
Michael Galpin
 

What's hot (20)

PDF
Ge aviation spark application experience porting analytics into py spark ml p...
Databricks
 
PDF
[QE 2018] Łukasz Gawron – Testing Batch and Streaming Spark Applications
Future Processing
 
PPTX
Scalable and Flexible Machine Learning With Scala @ LinkedIn
Vitaly Gordon
 
PPTX
Kpi driven-java-development-fn conf
Anirban Bhattacharjee
 
PDF
Functional programming in java
John Ferguson Smart Limited
 
PPT
Functional Programming In Java
Andrei Solntsev
 
PDF
Getting the best performance with PySpark - Spark Summit West 2016
Holden Karau
 
PDF
Scala introduction
vito jeng
 
PDF
Advanced Relevancy Ranking
Search Technologies
 
PDF
Python Performance 101
Ankur Gupta
 
PDF
Lambdas and Streams Master Class Part 2
José Paumard
 
PDF
Java 8 Stream API (Valdas Zigas)
Kaunas Java User Group
 
PDF
Improving PySpark Performance - Spark Beyond the JVM @ PyData DC 2016
Holden Karau
 
PPTX
Beyond shuffling - Strata London 2016
Holden Karau
 
PPT
Profiling and optimization
g3_nittala
 
PDF
Functional Programming in Java 8 - Lambdas and Streams
CodeOps Technologies LLP
 
PDF
API first with Swagger and Scala by Slava Schmidt
JavaDayUA
 
PDF
Kotlin Introduction with Android applications
Thao Huynh Quang
 
PPT
NOSQL and Cassandra
rantav
 
PDF
CMU TREC 2007 Blog Track
jelsas
 
Ge aviation spark application experience porting analytics into py spark ml p...
Databricks
 
[QE 2018] Łukasz Gawron – Testing Batch and Streaming Spark Applications
Future Processing
 
Scalable and Flexible Machine Learning With Scala @ LinkedIn
Vitaly Gordon
 
Kpi driven-java-development-fn conf
Anirban Bhattacharjee
 
Functional programming in java
John Ferguson Smart Limited
 
Functional Programming In Java
Andrei Solntsev
 
Getting the best performance with PySpark - Spark Summit West 2016
Holden Karau
 
Scala introduction
vito jeng
 
Advanced Relevancy Ranking
Search Technologies
 
Python Performance 101
Ankur Gupta
 
Lambdas and Streams Master Class Part 2
José Paumard
 
Java 8 Stream API (Valdas Zigas)
Kaunas Java User Group
 
Improving PySpark Performance - Spark Beyond the JVM @ PyData DC 2016
Holden Karau
 
Beyond shuffling - Strata London 2016
Holden Karau
 
Profiling and optimization
g3_nittala
 
Functional Programming in Java 8 - Lambdas and Streams
CodeOps Technologies LLP
 
API first with Swagger and Scala by Slava Schmidt
JavaDayUA
 
Kotlin Introduction with Android applications
Thao Huynh Quang
 
NOSQL and Cassandra
rantav
 
CMU TREC 2007 Blog Track
jelsas
 
Ad

Viewers also liked (14)

PPTX
Coffee. Duta Vladut-group 8213
Vlad Duţă
 
PPTX
Xin (Crossroads)
Xin Gu
 
PPTX
Object-Relational Mapping for Dummies
GlobalLogic Ukraine
 
PPT
Doctrine 2 - Introduction
Diego Lewin
 
PDF
#jd12nl Seblod 2
Herman Peeren
 
PPTX
Cost-based query optimization in Apache Hive 0.14
Julian Hyde
 
PDF
Jooctrine - Doctrine ORM in Joomla!
Herman Peeren
 
PPTX
My little pony powerpoint
leason2
 
PDF
Coffee Project Report S Mo Goltz
Mo Goltz
 
PDF
ORM: Object-relational mapping
Abhilash M A
 
PPT
Object Relational model for SQLIite in android
yugandhar vadlamudi
 
PDF
MySQL 5.7 + JSON
Morgan Tocker
 
PPTX
Python PPT
Edureka!
 
PPT
Introduction to Python
Nowell Strite
 
Coffee. Duta Vladut-group 8213
Vlad Duţă
 
Xin (Crossroads)
Xin Gu
 
Object-Relational Mapping for Dummies
GlobalLogic Ukraine
 
Doctrine 2 - Introduction
Diego Lewin
 
#jd12nl Seblod 2
Herman Peeren
 
Cost-based query optimization in Apache Hive 0.14
Julian Hyde
 
Jooctrine - Doctrine ORM in Joomla!
Herman Peeren
 
My little pony powerpoint
leason2
 
Coffee Project Report S Mo Goltz
Mo Goltz
 
ORM: Object-relational mapping
Abhilash M A
 
Object Relational model for SQLIite in android
yugandhar vadlamudi
 
MySQL 5.7 + JSON
Morgan Tocker
 
Python PPT
Edureka!
 
Introduction to Python
Nowell Strite
 
Ad

Similar to How Pony ORM translates Python generators to SQL queries (20)

PPTX
Compose Code Camp (1).pptx
MadheswarKonidela
 
PPTX
app4.pptx
sg4795
 
PPTX
OPEN SOURCE WEB APPLICATION DEVELOPMENT question
vishal choudhary
 
PDF
Creating the PromQL Transpiler for Flux by Julius Volz, Co-Founder | Prometheus
InfluxData
 
PDF
How do you create a programming language for the JVM?
Federico Tomassetti
 
PPTX
PHP (Hypertext Preprocessor) is a popular open-source server-side scripting l...
jinijames109
 
PPTX
cppt-170218053903 (1).pptx
WatchDog13
 
PPTX
008 FNDAMENTALS OF PYTHON FOR CLASS 11 cs-converted.pptx
KrithikaThimma1
 
PPTX
CPP-overviews notes variable data types notes
SukhpreetSingh519414
 
PDF
Clojure: Simple By Design
All Things Open
 
PDF
A Lifecycle Of Code Under Test by Robert Fornal
QA or the Highway
 
PDF
015. Interface Python with MySQL.pdf
SuneetaSingh28
 
PDF
Orthogonal Functional Architecture
John De Goes
 
PPTX
Apache HAWQ Architecture
Alexey Grishchenko
 
PDF
Hive Functions Cheat Sheet
Hortonworks
 
PDF
Real time, streaming advanced analytics, approximations, and recommendations ...
DataWorks Summit/Hadoop Summit
 
PDF
Boston Spark Meetup May 24, 2016
Chris Fregly
 
PPTX
Presentaion
CBRIARCSC
 
PPTX
07 php
CBRIARCSC
 
PDF
Writing good std::future&lt;c++>
corehard_by
 
Compose Code Camp (1).pptx
MadheswarKonidela
 
app4.pptx
sg4795
 
OPEN SOURCE WEB APPLICATION DEVELOPMENT question
vishal choudhary
 
Creating the PromQL Transpiler for Flux by Julius Volz, Co-Founder | Prometheus
InfluxData
 
How do you create a programming language for the JVM?
Federico Tomassetti
 
PHP (Hypertext Preprocessor) is a popular open-source server-side scripting l...
jinijames109
 
cppt-170218053903 (1).pptx
WatchDog13
 
008 FNDAMENTALS OF PYTHON FOR CLASS 11 cs-converted.pptx
KrithikaThimma1
 
CPP-overviews notes variable data types notes
SukhpreetSingh519414
 
Clojure: Simple By Design
All Things Open
 
A Lifecycle Of Code Under Test by Robert Fornal
QA or the Highway
 
015. Interface Python with MySQL.pdf
SuneetaSingh28
 
Orthogonal Functional Architecture
John De Goes
 
Apache HAWQ Architecture
Alexey Grishchenko
 
Hive Functions Cheat Sheet
Hortonworks
 
Real time, streaming advanced analytics, approximations, and recommendations ...
DataWorks Summit/Hadoop Summit
 
Boston Spark Meetup May 24, 2016
Chris Fregly
 
Presentaion
CBRIARCSC
 
07 php
CBRIARCSC
 
Writing good std::future&lt;c++>
corehard_by
 

Recently uploaded (20)

PDF
vAdobe Premiere Pro 2025 (v25.2.3.004) Crack Pre-Activated Latest
imang66g
 
PPTX
classification of computer and basic part of digital computer
ravisinghrajpurohit3
 
PDF
Protecting the Digital World Cyber Securit
dnthakkar16
 
PPTX
Odoo Integration Services by Candidroot Solutions
CandidRoot Solutions Private Limited
 
PDF
Summary Of Odoo 18.1 to 18.4 : The Way For Odoo 19
CandidRoot Solutions Private Limited
 
PDF
WatchTraderHub - Watch Dealer software with inventory management and multi-ch...
WatchDealer Pavel
 
PDF
ChatPharo: an Open Architecture for Understanding How to Talk Live to LLMs
ESUG
 
PPT
Why Reliable Server Maintenance Service in New York is Crucial for Your Business
Sam Vohra
 
PDF
Applitools Platform Pulse: What's New and What's Coming - July 2025
Applitools
 
PDF
An Experience-Based Look at AI Lead Generation Pricing, Features & B2B Results
Thomas albart
 
PPTX
AI-Ready Handoff: Auto-Summaries & Draft Emails from MQL to Slack in One Flow
bbedford2
 
PPTX
Presentation about variables and constant.pptx
kr2589474
 
PPTX
TRAVEL APIs | WHITE LABEL TRAVEL API | TOP TRAVEL APIs
philipnathen82
 
PDF
lesson-2-rules-of-netiquette.pdf.bshhsjdj
jasmenrojas249
 
PPTX
Role Of Python In Programing Language.pptx
jaykoshti048
 
PPTX
Presentation about variables and constant.pptx
safalsingh810
 
PDF
New Download MiniTool Partition Wizard Crack Latest Version 2025
imang66g
 
PPTX
ASSIGNMENT_1[1][1][1][1][1] (1) variables.pptx
kr2589474
 
PPTX
Presentation about Database and Database Administrator
abhishekchauhan86963
 
PPTX
GALILEO CRS SYSTEM | GALILEO TRAVEL SOFTWARE
philipnathen82
 
vAdobe Premiere Pro 2025 (v25.2.3.004) Crack Pre-Activated Latest
imang66g
 
classification of computer and basic part of digital computer
ravisinghrajpurohit3
 
Protecting the Digital World Cyber Securit
dnthakkar16
 
Odoo Integration Services by Candidroot Solutions
CandidRoot Solutions Private Limited
 
Summary Of Odoo 18.1 to 18.4 : The Way For Odoo 19
CandidRoot Solutions Private Limited
 
WatchTraderHub - Watch Dealer software with inventory management and multi-ch...
WatchDealer Pavel
 
ChatPharo: an Open Architecture for Understanding How to Talk Live to LLMs
ESUG
 
Why Reliable Server Maintenance Service in New York is Crucial for Your Business
Sam Vohra
 
Applitools Platform Pulse: What's New and What's Coming - July 2025
Applitools
 
An Experience-Based Look at AI Lead Generation Pricing, Features & B2B Results
Thomas albart
 
AI-Ready Handoff: Auto-Summaries & Draft Emails from MQL to Slack in One Flow
bbedford2
 
Presentation about variables and constant.pptx
kr2589474
 
TRAVEL APIs | WHITE LABEL TRAVEL API | TOP TRAVEL APIs
philipnathen82
 
lesson-2-rules-of-netiquette.pdf.bshhsjdj
jasmenrojas249
 
Role Of Python In Programing Language.pptx
jaykoshti048
 
Presentation about variables and constant.pptx
safalsingh810
 
New Download MiniTool Partition Wizard Crack Latest Version 2025
imang66g
 
ASSIGNMENT_1[1][1][1][1][1] (1) variables.pptx
kr2589474
 
Presentation about Database and Database Administrator
abhishekchauhan86963
 
GALILEO CRS SYSTEM | GALILEO TRAVEL SOFTWARE
philipnathen82
 

How Pony ORM translates Python generators to SQL queries

  • 2. What makes Pony ORM different? Fast object-relational mapper which uses Python generators for writing database queries
  • 3. A Python generator (p.name for p in product_list if p.price > 100)
  • 4. A Python generator vs a SQL query SELECT p.name FROM Products p WHERE p.price > 100 (p.name for p in product_list if p.price > 100)
  • 5. (p.name for p in product_list if p.price > 100) SELECT p.name FROM Products p WHERE p.price > 100 A Python generator vs a SQL query
  • 6. (p.name for p in product_list if p.price > 100) SELECT p.name FROM Products p WHERE p.price > 100 A Python generator vs a SQL query
  • 7. (p.name for p in product_list if p.price > 100) SELECT p.name FROM Products p WHERE p.price > 100 A Python generator vs a SQL query
  • 8. The same query in Pony SELECT p.name FROM Products p WHERE p.price > 100 select(p.name for p in Product if p.price > 100)
  • 9. • Pony ORM • Django • SQL Alchemy Query syntax comparison
  • 10. Pony ORM: select(p for p in Product if p.name.startswith('A') and p.image is None or p.added.year < 2014) Query syntax comparison
  • 12. SQLAlchemy: session.query(Product).filter( (Product.name.startswith('A') & (Product.image == None)) | (extract('year', Product.added) < 2014)) Query syntax comparison
  • 13. session.query(Product).filter( (Product.name.startswith('A') & (Product.image == None)) | (extract('year', Product.added) < 2014)) Query syntax comparison Product.objects.filter( Q(name__startswith='A', image__isnull=True) | Q(added__year__lt=2014)) select(p for p in Product if p.name.startswith('A') and p.image is None or p.added.year < 2014) Pony Django SQLAlchemy
  • 14. Query translation select(p for p in Product if p.name.startswith('A') and p.image is None or p.added.year < 2014) • Translation from the bytecode is fast • The bytecode translation result is cached • The Python generator object is used as a cache key Python generator object
  • 15. Building a query step by step q = select(o for o in Order if o.customer.id == some_id) q = q.filter(lambda o: o.state != 'DELIVERED') q = q.filter(lambda o: len(o.items) > 2) q = q.order_by(Order.date_created) q = q[10:20] SELECT "o"."id" FROM "Order" "o" LEFT JOIN "OrderItem" "orderitem-1" ON "o"."id" = "orderitem-1"."order" WHERE "o"."customer" = ? AND "o"."state" <> 'DELIVERED' GROUP BY "o"."id" HAVING COUNT("orderitem-1"."ROWID") > 2 ORDER BY "o"."date_created" LIMIT 10 OFFSET 10
  • 16. How Pony translates generator expressions to SQL?
  • 17. Python generator to SQL translation 1. Decompile bytecode and restore AST 2. Translate AST to ‘abstract SQL’ 3. Translate ‘abstract SQL’ to a specific SQL dialect
  • 18. Python generator to SQL translation 1. Decompile bytecode and restore AST 2. Translate AST to ‘abstract SQL’ 3. Translate ‘abstract SQL’ to a concrete SQL dialect
  • 19. Bytecode decompilation • Using the Visitor pattern • Methods of the Visitor object correspond the byte code commands • Pony keeps fragments of AST at the stack • Each method either adds a new part of AST or combines existing parts
  • 20. (a + b.c) in x.y Bytecode decompilation
  • 21. (a + b.c) in x.y LOAD_GLOBAL a LOAD_FAST b LOAD_ATTR c BINARY_ADD LOAD_FAST x LOAD_ATTR y COMPARE_OP in Bytecode decompilation
  • 22. Bytecode decompilation (a + b.c) in x.y LOAD_GLOBAL a LOAD_FAST b LOAD_ATTR c BINARY_ADD LOAD_FAST x LOAD_ATTR y COMPARE_OP in Stack
  • 23. Bytecode decompilation (a + b.c) in x.y > LOAD_GLOBAL a LOAD_FAST b LOAD_ATTR c BINARY_ADD LOAD_FAST x LOAD_ATTR y COMPARE_OP in Stack Name('a')
  • 24. Bytecode decompilation (a + b.c) in x.y LOAD_GLOBAL a > LOAD_FAST b LOAD_ATTR c BINARY_ADD LOAD_FAST x LOAD_ATTR y COMPARE_OP in Stack Name('b') Name('a')
  • 25. (a + b.c) in x.y LOAD_GLOBAL a LOAD_FAST b > LOAD_ATTR c BINARY_ADD LOAD_FAST x LOAD_ATTR y COMPARE_OP in Stack Getattr(Name('b'), 'c') Name('a') Bytecode decompilation
  • 26. (a + b.c) in x.y LOAD_GLOBAL a LOAD_FAST b LOAD_ATTR c > BINARY_ADD LOAD_FAST x LOAD_ATTR y COMPARE_OP in Stack Add(Name('a'), Getattr(Name('b'), 'c')) Bytecode decompilation
  • 27. Bytecode decompilation (a + b.c) in x.y LOAD_GLOBAL a LOAD_FAST b LOAD_ATTR c BINARY_ADD > LOAD_FAST x LOAD_ATTR y COMPARE_OP in Stack Name('x') Add(Name('a'), Getattr(Name('b'), 'c'))
  • 28. Bytecode decompilation (a + b.c) in x.y LOAD_GLOBAL a LOAD_FAST b LOAD_ATTR c BINARY_ADD LOAD_FAST x > LOAD_ATTR y COMPARE_OP in Stack Getattr(Name('x'), 'y') Add(Name('a'), Getattr(Name('b'), 'c'))
  • 29. Bytecode decompilation (a + b.c) in x.y LOAD_GLOBAL a LOAD_FAST b LOAD_ATTR c BINARY_ADD LOAD_FAST x LOAD_ATTR y > COMPARE_OP in Stack Compare('in', Add(…), Getattr(…))
  • 30. Abstract Syntax Tree (AST) a in + .c b .y x (a + b.c) in x.y
  • 31. Python generator to SQL translation 1. Decompile bytecode and restore AST 2. Translate AST to ‘abstract SQL’ 3. Translate ‘abstract SQL’ to a concrete SQL dialect
  • 32. What SQL it should be translated to? a in + .c b .y x (a + b.c) in x.y
  • 33. It depends on variables types! What SQL it should be translated to? (a + b.c) in x.y
  • 34. • If a and c are numbers, y is a collection (? + "b"."c") IN (SELECT …) • If a and c are strings, y is a collection CONCAT(?, "b"."c") IN (SELECT …) • If a, c and y are strings “x"."y" LIKE CONCAT('%', ?, "b"."c", '%') What SQL it should be translated to? (a + b.c) in x.y
  • 35. • The result of translation depends on types • If the translator analyzes node types by itself, the logic becomes too complex • Pony uses Monads to keep it simple (a + b.c) in x.y AST to SQL Translation
  • 36. • Encapsulates the node translation logic • Generates the result of translation - ‘the abstract SQL’ • Can combine itself with other monads The translator delegates the logic of translation to monads A Monad
  • 37. • StringAttrMonad • StringParamMonad • StringExprMonad • StringConstMonad • DatetimeAttrMonad • DatetimeParamMonad • ObjectAttrMonad • CmpMonad • etc… Each monad defines a set of allowed operations and can translate itself into a part of resulting SQL query Monad types
  • 38. AST Translation • Using the Visitor pattern • Walk the tree in depth-first order • Create monads when leaving each node
  • 39. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b
  • 40. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b
  • 41. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b StringParam Monad
  • 42. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b StringParam Monad
  • 43. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b StringParam Monad
  • 44. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b StringParam Monad ObjectIter Monad
  • 45. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b StringParam Monad ObjectIter Monad
  • 46. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b StringParam Monad ObjectIter Monad StringAttr Monad
  • 47. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b StringParam Monad ObjectIter Monad StringAttr Monad
  • 48. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b StringParam Monad ObjectIter Monad StringAttr Monad StringExpr Monad
  • 49. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b StringParam Monad ObjectIter Monad StringAttr Monad StringExpr Monad
  • 50. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b StringParam Monad ObjectIter Monad StringAttr Monad StringExpr Monad
  • 51. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b StringParam Monad ObjectIter Monad StringAttr Monad StringExpr Monad ObjectIter Monad
  • 52. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b StringParam Monad ObjectIter Monad StringAttr Monad StringExpr Monad ObjectIter Monad
  • 53. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b StringParam Monad ObjectIter Monad StringAttr Monad StringExpr Monad ObjectIter Monad StringAttr Monad
  • 54. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b StringParam Monad ObjectIter Monad StringAttr Monad StringExpr Monad ObjectIter Monad StringAttr Monad
  • 55. (a + b.c) in x.y AST to SQL Translation in .y x + a .c b StringParam Monad ObjectIter Monad StringAttr Monad StringExpr Monad ObjectIter Monad StringAttr Monad Cmp Monad
  • 56. Abstract SQL (a + b.c) in x.y ['LIKE', ['COLUMN', 't1', 'y'], ['CONCAT', ['VALUE', '%'], ['PARAM', 'p1'], ['COLUMN', 't2', 'c'], ['VALUE', '%'] ] ] Allows to put aside the SQL dialect differences
  • 57. Python generator to SQL translation 1. Decompile bytecode and restore AST 2. Translate AST to ‘abstract SQL’ 3. Translate ‘abstract SQL’ to a specific SQL dialect
  • 58. Specific SQL dialects ['LIKE', ['COLUMN', 't1', 'y'], ['CONCAT', ['VALUE', '%'], ['PARAM', 'p1'], ['COLUMN', 't2', 'c'], ['VALUE', '%'] ] ] MySQL: `t1`.`y` LIKE CONCAT('%', ?, `t2`.`c`, '%') SQLite: "t1"."y" LIKE '%' || ? || "t2"."c" || '%'
  • 59. Other Pony ORM features • Identity Map • Automatic query optimization • N+1 Query Problem solution • Optimistic transactions • Online ER Diagram Editor
  • 60. Django ORM s1 = Student.objects.get(pk=123) print s1.name, s1.group.id s2 = Student.objects.get(pk=456) print s2.name, s2.group.id • How many SQL queries will be executed? • How many objects will be created?
  • 61. Django ORM s1 = Student.objects.get(pk=123) print s1.name, s1.group.id s2 = Student.objects.get(pk=456) print s2.name, s2.group.id Student 123
  • 62. Django ORM s1 = Student.objects.get(pk=123) print s1.name, s1.group.id s2 = Student.objects.get(pk=456) print s2.name, s2.group.id Student 123 Group 1
  • 63. Django ORM s1 = Student.objects.get(pk=123) print s1.name, s1.group.id s2 = Student.objects.get(pk=456) print s2.name, s2.group.id Student 123 Student 456 Group 1
  • 64. Django ORM s1 = Student.objects.get(pk=123) print s1.name, s1.group.id s2 = Student.objects.get(pk=456) print s2.name, s2.group.id Student 123 Student 456 Group 1 Group 1
  • 65. Pony ORM s1 = Student[123] print s1.name, s1.group.id s2 = Student[456] print s2.name, s2.group.id
  • 66. Pony ORM – seeds, IdentityMap s1 = Student[123] print s1.name, s1.group.id s2 = Student[456] print s2.name, s2.group.id Student 123 Group 1
  • 67. Pony ORM – seeds, IdentityMap s1 = Student[123] print s1.name, s1.group.id s2 = Student[456] print s2.name, s2.group.id Student 123 Group 1 seed
  • 68. Pony ORM – seeds, IdentityMap s1 = Student[123] print s1.name, s1.group.id s2 = Student[456] print s2.name, s2.group.id Student 123 Group 1 seed
  • 69. Pony ORM – seeds, IdentityMap s1 = Student[123] print s1.name, s1.group.id s2 = Student[456] print s2.name, s2.group.id Student 123 Student 456 Group 1 seed
  • 70. Pony ORM – seeds, IdentityMap s1 = Student[123] print s1.name, s1.group.id s2 = Student[456] print s2.name, s2.group.id Student 123 Student 456 Group 1 seed
  • 71. Solution for the N+1 Query Problem orders = select(o for o in Order if o.total_price > 1000) .order_by(desc(Order.id)).page(1, pagesize=5) for o in orders: print o.total_price, o.customer.name 1 SELECT o.id, o.total_price, o.customer_id,... FROM "Order" o WHERE o.total_price > 1000 ORDER BY o.id DESC LIMIT 5
  • 72. Order 1 Order 3 Order 4 Order 7 Order 9 Customer 1 Customer 4 Customer 7 Solution for the N+1 Query Problem
  • 73. Order 1 Order 3 Order 4 Order 7 Order 9 Customer 1 Customer 4 Customer 7 Solution for the N+1 Query Problem One SQL query
  • 74. Solution for the N+1 Query Problem 1 1 SELECT c.id, c.name, … FROM “Customer” c WHERE c.id IN (?, ?, ?) orders = select(o for o in Order if o.total_price > 1000) .order_by(desc(Order.id)).page(1, pagesize=5) for o in orders: print o.total_price, o.customer.name SELECT o.id, o.total_price, o.customer_id,... FROM "Order" o WHERE o.total_price > 1000 ORDER BY o.id DESC LIMIT 5
  • 75. Automatic query optimization select(c for c in Customer if sum(c.orders.total_price) > 1000) SELECT "c"."id", "c"."email", "c"."password", "c"."name", "c"."country", "c"."address" FROM "Customer" "c" WHERE ( SELECT coalesce(SUM("order-1"."total_price"), 0) FROM "Order" "order-1" WHERE "c"."id" = "order-1"."customer" ) > 1000 SELECT "c"."id" FROM "Customer" "c" LEFT JOIN "Order" "order-1" ON "c"."id" = "order-1"."customer" GROUP BY "c"."id" HAVING coalesce(SUM("order-1"."total_price"), 0) > 1000
  • 76. Transactions def transfer_money(id1, id2, amount): account1 = Account.objects.get(pk=id1) if account1.amount < amount: raise ValueError('Not enough funds!') account2 = Account.object.get(pk=id2) account1.amount -= amount account1.save() account2.amount += amount account2.save() Django ORM
  • 77. @transaction.atomic def transfer_money(id1, id2, amount): account1 = Account.objects.get(pk=id1) if account1.amount < amount: raise ValueError('Not enough funds!') account2 = Account.object.get(pk=id2) account1.amount -= amount account1.save() account2.amount += amount account2.save() Transactions Django ORM
  • 78. @transaction.atomic def transfer_money(id1, id2, amount): account1 = Account.objects .select_for_update.get(pk=id1) if account1.amount < amount: raise ValueError('Not enough funds!') account2 = Account.objects .select_for_update.get(pk=id2) account1.amount -= amount account1.save() account2.amount += amount account2.save() Transactions Django ORM
  • 79. @db_session def transfer_money(id1, id2, amount): account1 = Account[id1] if account1.amount < amount: raise ValueError('Not enough funds!') account1.amount -= amount Account[id2].amount += amount Transactions Pony ORM
  • 80. db_session • Pony tracks which objects where changed • No need to call save() • Pony saves all updated objects in a single transaction automatically on leaving the db_session scope Transactions
  • 81. UPDATE Account SET amount = :new_value WHERE id = :id AND amount = :old_value Optimistic Locking
  • 82. Optimistic Locking • Pony tracks attributes which were read and updated • If object wasn’t locked using the for_update method, Pony uses the optimistic locking automatically
  • 86. Main Pony ORM features: • Using generators for database queries • Identity Map • Solution for N+1 Query Problem • Automatic query optimization • Optimistic transactions • Online ER-diagram editor Wrapping up
  • 87. • Python 3 • Microsoft SQL Server support • Improved documentation • Migrations • Ansync queries Pony roadmap
  • 88. • Site ponyorm.com • Twitter @ponyorm • Github github.com/ponyorm/pony • ER-Diagram editor editor.ponyorm.com • Installation: pip install pony Thank you! Pony ORM