포니 ORM 작성자는 여기입니다.
Pony는 Python 생성기를 세 단계로 SQL 쿼리로 변환합니다.
- 생성기 바이트 코드 디 컴파일 및 생성기 AST 재 구축 (추상 구문 트리)
- Python AST를 "추상 SQL"로 번역-SQL 쿼리의 범용 목록 기반 표현
- 추상 SQL 표현을 특정 데이터베이스 종속 SQL 언어로 변환
가장 복잡한 부분은 Pony가 파이썬 표현식의 "의미"를 이해해야하는 두 번째 단계입니다. 첫 번째 단계에 가장 관심이있는 것 같으니 디 컴파일이 어떻게 작동하는지 설명하겠습니다.
이 쿼리를 살펴 보겠습니다.
>>> from pony.orm.examples.estore import *
>>> select(c for c in Customer if c.country == 'USA').show()
다음 SQL로 변환됩니다.
SELECT "c"."id", "c"."email", "c"."password", "c"."name", "c"."country", "c"."address"
FROM "Customer" "c"
WHERE "c"."country" = 'USA'
다음은 출력 될이 쿼리의 결과입니다.
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
이 select()
함수는 파이썬 생성기를 인수로받은 다음 해당 바이트 코드를 분석합니다. 표준 파이썬을 사용하여이 생성기의 바이트 코드 명령을 얻을 수 있습니다.dis
모듈을 .
>>> 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은 바이트 코드에서 AST를 복원 할 수 decompile()
있는 모듈 내 기능 을 pony.orm.decompiling
가지고 있습니다.
>>> from pony.orm.decompiling import decompile
>>> ast, external_names = decompile(gen)
여기에서 AST 노드의 텍스트 표현을 볼 수 있습니다.
>>> ast
GenExpr(GenExprInner(Name('c'), [GenExprFor(AssName('c', 'OP_ASSIGN'), Name('.0'),
[GenExprIf(Compare(Getattr(Name('c'), 'country'), [('==', Const('USA'))]))])]))
이제 decompile()
함수가 어떻게 작동 하는지 살펴 보겠습니다 .
이 decompile()
함수 Decompiler
는 방문자 패턴을 구현 하는 개체를 만듭니다 . 디 컴파일러 인스턴스는 바이트 코드 명령어를 하나씩 가져옵니다. 각 명령어에 대해 디 컴파일러 객체는 자체 메서드를 호출합니다. 이 메서드의 이름은 현재 바이트 코드 명령어의 이름과 같습니다.
파이썬은 표현식을 계산할 때 중간 계산 결과를 저장하는 스택을 사용합니다. 디 컴파일러 객체에도 자체 스택이 있지만이 스택에는 표현식 계산 결과가 아니라 표현식에 대한 AST 노드가 저장됩니다.
다음 바이트 코드 명령어에 대한 디 컴파일러 메서드가 호출되면 스택에서 AST 노드를 가져 와서 새 AST 노드로 결합한 다음이 노드를 스택의 맨 위에 놓습니다.
예를 들어 하위 표현식 c.country == 'USA'
이 어떻게 계산 되는지 살펴 보겠습니다 . 해당하는 바이트 코드 조각은 다음과 같습니다.
9 LOAD_FAST 1 (c)
12 LOAD_ATTR 0 (country)
15 LOAD_CONST 0 ('USA')
18 COMPARE_OP 2 (==)
따라서 디 컴파일러 객체는 다음을 수행합니다.
- 를 호출
decompiler.LOAD_FAST('c')
합니다. 이 메서드는 Name('c')
노드를 디 컴파일러 스택의 맨 위에 놓습니다.
- 를 호출
decompiler.LOAD_ATTR('country')
합니다. 이 메서드는 Name('c')
스택 에서 노드를 가져 와서 노드를 만들어 스택 Geattr(Name('c'), 'country')
의 맨 위에 놓습니다.
- 를 호출
decompiler.LOAD_CONST('USA')
합니다. 이 방법은Const('USA')
노드를 스택 맨 위에 .
- 를 호출
decompiler.COMPARE_OP('==')
합니다. 이 메서드는 스택에서 두 개의 노드 (Getattr 및 Const)를 가져온 다음 스택 Compare(Getattr(Name('c'), 'country'), [('==', Const('USA'))])
맨 위에 배치합니다.
모든 바이트 코드 명령어가 처리 된 후 디 컴파일러 스택에는 전체 생성기 표현식에 해당하는 단일 AST 노드가 포함됩니다.
Pony ORM은 제너레이터와 람다 만 디 컴파일해야하기 때문에 제너레이터의 명령 흐름이 비교적 간단하기 때문에 복잡하지 않습니다. 이는 중첩 루프의 무리 일뿐입니다.
현재 Pony ORM은 다음 두 가지를 제외하고 전체 생성기 명령어 세트를 다룹니다.
- 인라인 if 표현식 :
a if b else c
- 화합물 비교 :
a < b < c
Pony가 이러한 표현을 만나면 NotImplementedError
예외가 발생합니다. 그러나이 경우에도 생성기 표현식을 문자열로 전달하여 작동하도록 만들 수 있습니다. 생성기를 문자열로 전달할 때 Pony는 디 컴파일러 모듈을 사용하지 않습니다. 대신 표준 Python compiler.parse
함수를 사용하여 AST를 가져옵니다 .
이것이 귀하의 질문에 답하기를 바랍니다.
p
객체는 어떤 메소드 / 속성이 액세스되고 있는지 (예 :name
,startswith
) 살펴보고 이를 SQL로 변환하는 Pony가 구현 한 유형의 객체 일 것 입니다.