S-expressions
S-expressions is the way Walkable allow you to express SQL meanings in a Lispy syntax in a highly composable way without compromising security.
Note about the examples in this page:
|
Primitive types
"hello world"
(jdbc/query your-db ["SELECT ? AS q" "hello world"])
[{:q "hello world"}]
Comparison
Walkable comes with some comparison operators: :=
, :<
, :>
,
:<=
, :>=
. They will result in SQL operators with the same name,
but also handle multiple arity mimicking their Clojure equivalents.
[:< 1 2 3 1]
(jdbc/query your-db ["SELECT ((1 < 2) AND (2 < 3) AND (3 < 1)) AS q"])
[{:q false}]
String comparison operators: =
, like
, match
, glob
:
[:= "hello" "hi"]
(jdbc/query your-db ["SELECT (? = ?) AS q" "hello" "hi"])
[{:q false}]
[:like "abcd" "abc%"]
(jdbc/query your-db ["SELECT (? LIKE ?) AS q" "abcd" "abc%"])
[{:q true}]
Use them on some columns, too:
Math
Basic math operators work just like their Clojure equivalents: :+
,
:-
, :*
, :/
:
[:+ 1 2 4 8]
(jdbc/query your-db ["SELECT (1 + 2 + 4 + 8) AS q"])
[{:q 15}]
Feel free to mix them:
String manipulation
[:str "hello " nil "world" 123]
(jdbc/query your-db ["SELECT (CONCAT(?, NULL, ?, 123) AS q" "hello " "world"])
[{:q "hello world123"}]
[:subs "hello world"]
(jdbc/query your-db ["SELECT (CONCAT(?, NULL, ?, 123) AS q" "hello " "world"])
[{:q "hello world123"}]
Logic constructs
:and
and :or
accept many arguments like in Clojure:
[:and true true false]
(jdbc/query your-db ["SELECT (true AND true AND false) AS q"])
[{:q false}]
:not
accepts exactly one argument:
[:not true]
(jdbc/query your-db ["SELECT (NOT true) AS q"])
[{:q false}]
Party time! Mix them as you wish:
Important note
Please note that Walkable S-expressions are translated directly to SQL equivalent. Your DBMS may throw an exception if you ask for this:
[:or 2 true]
(jdbc/query your-db ["SELECT (2 OR true) AS q"])
ERROR: argument of OR must be type boolean, not type integer
Don’t be surprised if you see [:not nil]
is … nil
!
Other constructs
:when
, :if
, :case
and :cond
look like in Clojure…
[:when true "yay"] ;; or [:if true "yay"]
(jdbc/query your-db ["SELECT (CASE WHEN ( true ) THEN ( ? ) END) AS q" "yay"])
[{:q "yay"}]
[:if [:= 1 2] "yep" "nope"]
(jdbc/query your-db ["SELECT (CASE WHEN ((1)=(2)) THEN ( ? ) ELSE ( ? ) END) AS q" "yay" "nope"])
[{:q "nope"}]
[:case [:+ 0 1] 2 3]
(jdbc/query your-db ["SELECT (CASE (0+1) WHEN (2) THEN (3) END) AS q"])
[{:q nil}]
[:case [:+ 0 1] 2 3 4]
(jdbc/query your-db ["SELECT (CASE (0+1) WHEN (2) THEN (3) ELSE (4) END) AS q"])
[{:q 4}]
[:cond [:= 0 1] "wrong" [:< 2 3] "right"]
(jdbc/query your-db ["SELECT (CASE WHEN ((0)=(1)) THEN ( ? ) WHEN ((2)<(3)) THEN ( ? ) END) AS q" "wrong" "right"])
[{:q "right"}]
…except the fact that you must supply real booleans to them, not just some truthy values.
[:cond
[:= 0 1]
"wrong"
[:> 2 3]
"wrong again"
true (1)
"default"]
1 | must be literally true , not :default or anything else |
(jdbc/query your-db ["SELECT (CASE WHEN ((0)=(1)) THEN ( ? ) WHEN ((2)>(3)) THEN ( ? ) WHEN ( true ) THEN ( ? ) END) AS q" "wrong" "wrong again" "default"])
[{:q "default"}]