Tác giả ORM Pony ở đây.
Pony dịch phát Python vào truy vấn SQL trong ba bước:
- Decompiling của bytecode máy phát điện và xây dựng lại máy phát điện AST (cây cú pháp trừu tượng)
- dịch của Python AST vào "SQL trừu tượng" - vũ trụ biểu diễn dựa trên danh sách truy vấn SQL
- Chuyển đổi biểu diễn SQL trừu tượng thành cụ thể phương ngữ SQL phụ thuộc vào cơ sở dữ liệu
Phần phức tạp nhất là bước thứ hai, trong đó Pony phải hiểu "ý nghĩa" của các biểu thức Python. Dường như bạn là hầu hết các quan tâm đến bước đầu tiên, vì vậy hãy để tôi giải thích cách giải mã hoạt động.
Hãy xem xét truy vấn này:
>>> from pony.orm.examples.estore import *
>>> select(c for c in Customer if c.country == 'USA').show()
nào sẽ được dịch sang SQL sau đây:
SELECT "c"."id", "c"."email", "c"."password", "c"."name", "c"."country", "c"."address"
FROM "Customer" "c"
WHERE "c"."country" = 'USA'
Và dưới đây là kết quả của truy vấn này sẽ được in ra:
id|email |password|name |country|address
--+-------------------+--------+--------------+-------+---------
1 |[email protected] |*** |John Smith |USA |address 1
2 |[email protected]|*** |Matthew Reed |USA |address 2
4 |[email protected]|*** |Rebecca Lawson|USA |address 4
Chức năng select()
chấp nhận trình tạo python làm đối số và t hen phân tích bytecode của nó. Chúng ta có thể nhận được hướng dẫn bytecode của máy phát điện này sử dụng python chuẩn dis
mô-đun:
>>> gen = (c for c in Customer if c.country == 'USA')
>>> import dis
>>> dis.dis(gen.gi_frame.f_code)
1 0 LOAD_FAST 0 (.0)
>> 3 FOR_ITER 26 (to 32)
6 STORE_FAST 1 (c)
9 LOAD_FAST 1 (c)
12 LOAD_ATTR 0 (country)
15 LOAD_CONST 0 ('USA')
18 COMPARE_OP 2 (==)
21 POP_JUMP_IF_FALSE 3
24 LOAD_FAST 1 (c)
27 YIELD_VALUE
28 POP_TOP
29 JUMP_ABSOLUTE 3
>> 32 LOAD_CONST 1 (None)
35 RETURN_VALUE
Pony ORM có chức năng decompile()
trong mô-đun pony.orm.decompiling
có thể khôi phục lại một AST từ bytecode:
>>> from pony.orm.decompiling import decompile
>>> ast, external_names = decompile(gen)
Ở đây, chúng ta có thể thấy biểu diễn văn bản của các nút AST:
>>> ast
GenExpr(GenExprInner(Name('c'), [GenExprFor(AssName('c', 'OP_ASSIGN'), Name('.0'),
[GenExprIf(Compare(Getattr(Name('c'), 'country'), [('==', Const('USA'))]))])]))
Bây giờ hãy xem cách hoạt động của hàm decompile()
.
Chức năng decompile()
tạo đối tượng Decompiler
, thực hiện mẫu Khách truy cập. Ví dụ về trình dịch ngược được hướng dẫn bytecode từng cái một. Đối với mỗi lệnh, đối tượng decompiler gọi phương thức riêng của nó. Tên của phương thức này bằng với tên của lệnh bytecode hiện tại.
Khi Python tính toán một biểu thức, nó sử dụng ngăn xếp, lưu trữ kết quả trung gian của phép tính. Đối tượng decompiler cũng có ngăn xếp riêng của nó, nhưng ngăn xếp này lưu trữ không phải là kết quả của phép tính biểu thức, nhưng nút AST cho biểu thức.
Khi phương pháp biên dịch ngược cho lệnh bytecode tiếp theo được gọi, cần có các nút AST từ ngăn xếp, kết hợp chúng vào nút AST mới, sau đó đặt nút này lên đầu ngăn xếp.
Ví dụ: hãy xem cách biểu diễn con số c.country == 'USA'
. Các bytecode tương ứng đoạn là:
9 LOAD_FAST 1 (c)
12 LOAD_ATTR 0 (country)
15 LOAD_CONST 0 ('USA')
18 COMPARE_OP 2 (==)
Vì vậy, đối tượng decompiler nào sau đây:
- cuộc gọi
decompiler.LOAD_FAST('c')
. Phương pháp này đặt nút Name('c')
trên đầu ngăn xếp bộ giải mã.
- Gọi
decompiler.LOAD_ATTR('country')
. Phương pháp này có nút Name('c')
từ ngăn xếp, tạo nút Geattr(Name('c'), 'country')
và đặt nó lên đầu ngăn xếp.
- Gọi
decompiler.LOAD_CONST('USA')
. Phương pháp này đặt nút Const('USA')
lên trên chồng.
- Gọi
decompiler.COMPARE_OP('==')
. Phương pháp này có hai nút (Getattr và Const) từ ngăn xếp, và sau đó đặt Compare(Getattr(Name('c'), 'country'), [('==', Const('USA'))])
trên đầu ngăn xếp.
Sau khi tất cả hướng dẫn bytecode được xử lý, ngăn chứa bộ giải mã có chứa một nút AST tương ứng với biểu thức toàn bộ trình tạo.
Kể từ Pony ORM cần phải biên soạn lại phát và lambdas chỉ, đây không phải là phức tạp, bởi vì dòng hướng dẫn cho một máy phát điện là tương đối đơn giản - nó chỉ là một loạt các vòng lặp lồng nhau.
Hiện nay Pony ORM bao gồm các hướng dẫn toàn bộ máy phát điện thiết lập ngoại trừ hai điều:
- Inline nếu biểu thức:
a if b else c
- so sánh Compound:
a < b < c
Nếu Pony gặp biểu hiện như vậy nó làm tăng NotImplementedError
ngoại lệ. Nhưng ngay cả trong trường hợp này bạn có thể làm cho nó hoạt động bằng cách chuyển biểu thức trình tạo dưới dạng chuỗi. Khi bạn vượt qua một máy phát điện như một chuỗi Pony không sử dụng mô-đun bộ giải mã. Thay vào đó, nó được AST sử dụng hàm Python chuẩn compiler.parse
.
Hy vọng điều này sẽ trả lời câu hỏi của bạn.
Có lẽ đối tượng 'p' là đối tượng của loại được Pony thực hiện, xem xét phương thức/thuộc tính nào đang được truy cập trên nó (ví dụ:' name', 'startswith') và chuyển đổi chúng thành SQL. – BrenBarn
[Ở đây] (https://github.com/ponyorm/pony/blob/orm/pony/orm/decompiling.py#L52) là tệp mà bạn đang theo dõi. Nó dường như tái tạo lại máy phát bằng cách sử dụng một số thuật sĩ nội tâm. Tôi không chắc liệu nó có hỗ trợ 100% cú pháp Python hay không, nhưng điều này khá hay. – Blender
@Blender: Tôi đã nhìn thấy loại lừa này trong LISP - kéo diễn viên đóng thế này bằng Python chỉ đơn giản là bị bệnh! –