Tác giả Pony ORM ở đây.
Pony dịch trình tạo Python thành truy vấn SQL trong ba bước:
- Giải mã bytecode của trình tạo và xây dựng lại trình tạo AST (cây cú pháp trừu tượng)
- Bản dịch AST của Python thành "SQL trừu tượng" - đại diện dựa trên danh sách phổ quát của một truy vấn SQL
- Chuyển đổi biểu diễn SQL trừu tượng thành phương ngữ SQL phụ thuộc vào cơ sở dữ liệu cụ thể
Phần phức tạp nhất là bước thứ hai, nơi Pony phải hiểu "ý nghĩa" của các biểu thức Python. Có vẻ như bạn quan tâm nhất đến bước đầu tiên, vì vậy hãy để tôi giải thích cách dịch ngược 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()
Cái nào sẽ được dịch sang SQL sau:
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 |john@example.com |*** |John Smith |USA |address 1
2 |matthew@example.com|*** |Matthew Reed |USA |address 2
4 |rebecca@example.com|*** |Rebecca Lawson|USA |address 4
Các select()
chức năng chấp nhận một máy phát điện python như là đối số, và sau đó phân tích bytecode của nó. Chúng tôi có thể nhận hướng dẫn bytecode của trình tạo này bằng cách sử dụng dis
mô-đun python tiêu chuẩn :
>>> 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 AST từ mã bytecode:
>>> from pony.orm.decompiling import decompile
>>> ast, external_names = decompile(gen)
Ở đây, chúng ta có thể thấy biểu diễn dạng 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ờ chúng ta hãy xem làm thế nào decompile()
chức năng hoạt động .
Các decompile()
chức năng tạo ra mộtDecompiler
đối tượng, mà thực hiện mô hình của khách. Cá thể trình dịch ngược nhận được các hướng dẫn bytecode từng cái một. Đối với mỗi lệnh, đối tượng trình dịch ngược gọi phương thức riêng của nó. Tên của phương thức này giố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 trình dịch ngược cũng có ngăn xếp riêng của nó, nhưng ngăn xếp này không lưu trữ kết quả của phép tính biểu thức, mà là nút AST cho biểu thức.
Khi phương thức dịch ngược cho lệnh bytecode tiếp theo được gọi, nó lấy các nút AST từ ngăn xếp, kết hợp chúng thành một nút AST mới, rồi đặt nút này lên trên cùng của ngăn xếp.
Ví dụ, hãy xem cách tính biểu thức con c.country == 'USA'
. Đoạn mã bytecode tương ứng 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 dịch ngược thực hiện những việc sau:
- Cuộc gọi
decompiler.LOAD_FAST('c')
. Phương pháp này đặtName('c')
nút ở trên cùng của ngăn xếp trình dịch ngược.
- Cuộc gọi
decompiler.LOAD_ATTR('country')
. Phương thức này lấy Name('c')
nút từ ngăn xếp, tạoGeattr(Name('c'), 'country')
nút và đặt nó lên trên cùng của ngăn xếp.
- Cuộc gọi
decompiler.LOAD_CONST('USA')
. Phương pháp này đặtConst('USA')
nút ở trên cùng của ngăn xếp.
- Cuộc gọi
decompiler.COMPARE_OP('==')
. Phương thức này lấy hai nút (Getattr và Const) từ ngăn xếp, sau đó đặt Compare(Getattr(Name('c'), 'country'), [('==', Const('USA'))])
lên trên cùng của ngăn xếp.
Sau khi tất cả các lệnh bytecode được xử lý, ngăn xếp trình dịch ngược chứa một nút AST duy nhất tương ứng với toàn bộ biểu thức trình tạo.
Vì Pony ORM chỉ cần dịch ngược bộ tạo và lambdas, điều này không phức tạp lắm, bởi vì luồng lệnh cho bộ tạo tương đối đơn giản - nó chỉ là một loạt các vòng lặp lồng nhau.
Hiện tại Pony ORM bao gồm toàn bộ bộ hướng dẫn của trình tạo ngoại trừ hai điều:
- Các biểu thức if nội dòng:
a if b else c
- So sánh phức hợp:
a < b < c
Nếu Pony gặp phải biểu hiện như vậy, nó sẽ dẫn đến 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 một chuỗi. Khi bạn truyền một trình tạo dưới dạng chuỗi, Pony không sử dụng mô-đun dịch ngược. Thay vào đó, nó nhận được AST bằng cách sử dụng Python tiêu chuẩncompiler.parse
hàm .
Hy vọng điều này trả lời câu hỏi của bạn.
p
đối tượng là một đối tượng của một kiểu do Pony thực hiện, nó sẽ xem xét những 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 sang SQL.