使用 SQL 資料庫時,我們有時候會需要用到參數化查詢,比方說,為了阻擋 SQL injection 的時候。下方是一段 Postgres 的範例:
PREPARE get_users_by_age(int) AS
SELECT id, name FROM users WHERE age = $1;
EXECUTE get_users_by_age(30);
下方的這個查詢是用來查找『演員 "Sylvester Stallone" 演出的所有電影名稱』
[:find ?title
:where
[?p :person/name "Sylvester Stallone"]
[?m :movie/cast ?p]
[?m :movie/title ?title]]
那如果我們需要查詢『其它演員演出的所有電影』呢?這可以利用參數化查詢來處理。Datalog 的參數化查詢主要依賴 :in
開頭的子句 (clause) 來實現。
[:find ?title
:in $ ?name
:where
[?p :person/name ?name]
[?m :movie/cast ?p]
[?m :movie/title ?title]]
仔細看,參數化查詢相對於原本的查詢,有兩個地方做了修改:
:in $ ?name
。此處的 $
對應著資料庫值,而 ?name
則對應著演員姓名的參數值。[?p :person/name "Sylvester Stallone"]
改成 [?p :person/name ?name]
。Datomic 資料庫有提供一個函數 datomic.api/q
,是專門用來處理查詢的函數,它同時也會接收參數的值。
最簡單的查詢,函數的呼叫長成 (q query db)
,其中,query
是查詢,db
是資料庫值。在這種時候,:in
子句其實是長成 :in $
,由於只有一個資料庫值,所以這種情況下,整個 :in
子句也可以省略不寫。
而參數化查詢,以上頭為例,函數的呼叫會長成: (q query db "Sylvester Stallone")
。其中,query
是參數化的查詢;db
是資料庫值,會代入 $
; "Sylvester Stallone"
是演員姓名,會代入 ?name
參數。
$
『咦,對應資料庫值的 $
真有用到嗎?』
其實,完整的資料模式 (data pattern) 有五個元素。第一個元素對應的就是資料庫值,只是說,許多時候也可以不寫、省略。
[<database> <entity-id> <attribute> <value> <transaction-id>]
換言之,剛才那個參數化的查詢,也可以將每個資料模式開頭的 $
都寫出來,寫成
[:find ?title
:in $ ?name
:where
[$ ?p :person/name ?name]
[$ ?m :movie/cast ?p]
[$ ?m :movie/title ?title]]
而它執行出來的結果會是一樣的。