Ecto.Query เนี่ยเป็น module ที่ช่วยให้เราเขียน SQL Query ด้วย syntax ของ Elixir ความ magic ของมันคือหน้าตามันเหมือนจะเรียก Elixir function โดยตรงเนี่ยแหละ แต่มันไม่ใช้ สิ่งที่เขียนไปมันจะโดน macro แปลงไปอยู่ในรูปของ SQL Query ให้
ตัวอย่างเช่น โค้ดที่เขียน Query แบบนี้
from p in Post, select: sum(p.wrapped_visits)
ซึ่งมันยังคงถูก syntax Elixir นะ อธิบายทีละส่วนคือมันเรียก macro ชื่อ from
ทีนี้เวลาเรียก macro สิ่งที่เราส่งให้มันจะยังไม่ถูก evaluate ใดๆ ดังนั้น p in Post
และ sum(p.wrapped_visits)
ก็จะยังไม่เกิดอะไรขึ้น sum(p.wrapped_visits)
ที่เราเห็นว่าเหมือนเรียกฟังก์ชัน จริงๆแล้วไม่มีโค้ด function หรือ macro ชื่อ sum ใน Ecto.Query ด้วยซ้ำ
ทั้งสองส่วนนี้จะถูกแปลงเป็นรูปแบบ AST (Abstract Syntax Tree) หลังจากนั้นตัว from macro จะค่อยแปลง AST เป็น SQL Query อีกที
ถ้าเราไปลองอ่านๆโค้ดของ Ecto ที่ https://github.com/elixir-ecto/ecto/blob/v3.5.5/lib/ecto/query/builder.ex จะเจอจุดที่คิดว่าเป็น logic ในการแปลงตรง sum(p.wrapped_visits)
เป็น SQL คือ
@static_aggregates [
count: {0, :integer},
count: {1, :integer},
count: {2, :integer},
avg: {1, :any},
sum: {1, :any},
row_number: {0, :integer},
rank: {0, :integer},
dense_rank: {0, :integer},
percent_rank: {0, :any},
cume_dist: {0, :any},
ntile: {1, :integer}
]
และ
for {agg, {arity, return}} <- @static_aggregates do
defp call_type(unquote(agg), unquote(arity)), do: {:any, unquote(return)}
end
นั้นคือเวลาเรียก sum(p.wrapped_visits)
มันจะถูกแปลงเป็น AST แล้วเอาข้อมูลใน AST มา matching กับ call_type
ที่มีจนได้ข้อมูลเพียงพอที่จะแปลงเป็น SQL ต่อไปนั่นเอง
Top comments (0)