2025-02-21 01:03:44 +00:00
|
|
|
;; copyright (c) 2020-2025 sean corfield, all rights reserved
|
2024-11-12 23:58:32 +00:00
|
|
|
|
|
|
|
|
(ns honey.sql.xtdb-test
|
2024-11-24 02:26:53 +00:00
|
|
|
(:require [clojure.test :refer [deftest is testing]]
|
2024-11-12 23:58:32 +00:00
|
|
|
[honey.sql :as sql]
|
|
|
|
|
[honey.sql.helpers :as h
|
2025-01-21 23:21:59 +00:00
|
|
|
:refer [select exclude rename from]]))
|
2024-11-12 23:58:32 +00:00
|
|
|
|
|
|
|
|
(deftest select-tests
|
|
|
|
|
(testing "select, exclude, rename"
|
|
|
|
|
(is (= ["SELECT * EXCLUDE _id RENAME value AS foo_value FROM foo"]
|
|
|
|
|
(sql/format (-> (select :*) (exclude :_id) (rename [:value :foo_value])
|
|
|
|
|
(from :foo)))))
|
|
|
|
|
(is (= ["SELECT * EXCLUDE (_id, a) RENAME value AS foo_value FROM foo"]
|
|
|
|
|
(sql/format (-> (select :*) (exclude :_id :a) (rename [:value :foo_value])
|
|
|
|
|
(from :foo)))))
|
|
|
|
|
(is (= ["SELECT * EXCLUDE _id RENAME (value AS foo_value, a AS b) FROM foo"]
|
|
|
|
|
(sql/format (-> (select :*) (exclude :_id)
|
|
|
|
|
(rename [:value :foo_value]
|
|
|
|
|
[:a :b])
|
2024-11-23 06:54:42 +00:00
|
|
|
(from :foo)))))
|
|
|
|
|
(is (= ["SELECT * EXCLUDE _id RENAME value AS foo_value, c.x FROM foo"]
|
|
|
|
|
(sql/format (-> (select [:* (-> (exclude :_id) (rename [:value :foo_value]))]
|
|
|
|
|
:c.x)
|
|
|
|
|
(from :foo)))))
|
|
|
|
|
(is (= ["SELECT * EXCLUDE (_id, a) RENAME value AS foo_value, c.x FROM foo"]
|
|
|
|
|
(sql/format (-> (select [:* (-> (exclude :_id :a) (rename [:value :foo_value]))]
|
|
|
|
|
:c.x)
|
|
|
|
|
(from :foo)))))
|
|
|
|
|
(is (= ["SELECT * EXCLUDE _id RENAME (value AS foo_value, a AS b), c.x FROM foo"]
|
|
|
|
|
(sql/format (-> (select [:* (-> (exclude :_id)
|
|
|
|
|
(rename [:value :foo_value]
|
|
|
|
|
[:a :b]))]
|
|
|
|
|
:c.x)
|
2024-11-12 23:58:32 +00:00
|
|
|
(from :foo))))))
|
|
|
|
|
(testing "select, nest_one, nest_many"
|
|
|
|
|
(is (= ["SELECT a._id, NEST_ONE (SELECT * FROM foo AS b WHERE b_id = a._id) FROM bar AS a"]
|
|
|
|
|
(sql/format '{select (a._id,
|
|
|
|
|
((nest_one {select * from ((foo b)) where (= b_id a._id)})))
|
|
|
|
|
from ((bar a))})))
|
|
|
|
|
(is (= ["SELECT a._id, NEST_MANY (SELECT * FROM foo AS b) FROM bar AS a"]
|
|
|
|
|
(sql/format '{select (a._id,
|
|
|
|
|
((nest_many {select * from ((foo b))})))
|
|
|
|
|
from ((bar a))})))))
|
|
|
|
|
|
|
|
|
|
(deftest dotted-array-access-tests
|
2024-11-30 05:49:44 +00:00
|
|
|
(is (= ["SELECT (a.b).c"] ; old, partial support:
|
|
|
|
|
(sql/format '{select (((. (nest :a.b) :c)))})))
|
|
|
|
|
(is (= ["SELECT (a.b).c"] ; new, complete support:
|
2024-12-11 23:35:26 +00:00
|
|
|
(sql/format '{select (((:get-in :a.b :c)))})))
|
|
|
|
|
(is (= ["SELECT (a).b.c"] ; the first expression is always parenthesized:
|
|
|
|
|
(sql/format '{select (((:get-in :a :b :c)))}))))
|
2024-11-23 07:14:02 +00:00
|
|
|
|
|
|
|
|
(deftest erase-from-test
|
|
|
|
|
(is (= ["ERASE FROM foo WHERE foo.id = ?" 42]
|
|
|
|
|
(-> {:erase-from :foo
|
|
|
|
|
:where [:= :foo.id 42]}
|
2024-12-11 19:50:47 +00:00
|
|
|
(sql/format))))
|
|
|
|
|
(is (= ["ERASE FROM foo WHERE foo.id = ?" 42]
|
|
|
|
|
(-> (h/erase-from :foo)
|
|
|
|
|
(h/where [:= :foo.id 42])
|
2024-11-23 07:14:02 +00:00
|
|
|
(sql/format)))))
|
2024-11-23 07:46:56 +00:00
|
|
|
|
|
|
|
|
(deftest inline-record-body
|
|
|
|
|
(is (= ["{_id: 1, name: 'foo', info: {contact: [{loc: 'home', tel: '123'}, {loc: 'work', tel: '456'}]}}"]
|
|
|
|
|
(sql/format [:inline {:_id 1 :name "foo"
|
|
|
|
|
:info {:contact [{:loc "home" :tel "123"}
|
|
|
|
|
{:loc "work" :tel "456"}]}}]))))
|
2024-11-23 19:59:12 +00:00
|
|
|
|
|
|
|
|
(deftest records-statement
|
|
|
|
|
(testing "auto-lift maps"
|
|
|
|
|
(is (= ["RECORDS ?, ?" {:_id 1 :name "cat"} {:_id 2 :name "dog"}]
|
|
|
|
|
(sql/format {:records [{:_id 1 :name "cat"}
|
|
|
|
|
{:_id 2 :name "dog"}]}))))
|
|
|
|
|
(testing "explicit inline"
|
|
|
|
|
(is (= ["RECORDS {_id: 1, name: 'cat'}, {_id: 2, name: 'dog'}"]
|
|
|
|
|
(sql/format {:records [[:inline {:_id 1 :name "cat"}]
|
|
|
|
|
[:inline {:_id 2 :name "dog"}]]}))))
|
|
|
|
|
(testing "insert with records"
|
|
|
|
|
(is (= ["INSERT INTO foo RECORDS {_id: 1, name: 'cat'}, {_id: 2, name: 'dog'}"]
|
2024-12-11 23:35:26 +00:00
|
|
|
(sql/format {:insert-into :foo
|
|
|
|
|
:records [[:inline {:_id 1 :name "cat"}]
|
|
|
|
|
[:inline {:_id 2 :name "dog"}]]})))
|
|
|
|
|
(is (= ["INSERT INTO foo RECORDS {_id: 1, name: 'cat'}, {_id: 2, name: 'dog'}"]
|
|
|
|
|
(sql/format {:insert-into :foo
|
|
|
|
|
:records [[:inline {:_id 1 :name "cat"}]
|
|
|
|
|
[:inline {:_id 2 :name "dog"}]]})))
|
2024-11-23 19:59:12 +00:00
|
|
|
(is (= ["INSERT INTO foo RECORDS ?, ?" {:_id 1 :name "cat"} {:_id 2 :name "dog"}]
|
2024-12-11 23:35:26 +00:00
|
|
|
(sql/format {:insert-into [:foo ; as a sub-clause
|
2024-11-23 19:59:12 +00:00
|
|
|
{:records [{:_id 1 :name "cat"}
|
|
|
|
|
{:_id 2 :name "dog"}]}]})))))
|
2024-11-23 20:17:43 +00:00
|
|
|
|
2024-12-11 19:50:47 +00:00
|
|
|
(deftest patch-statement
|
|
|
|
|
(testing "patch with records"
|
|
|
|
|
(is (= ["PATCH INTO foo RECORDS {_id: 1, name: 'cat'}, {_id: 2, name: 'dog'}"]
|
2024-12-11 23:35:26 +00:00
|
|
|
(sql/format {:patch-into [:foo]
|
|
|
|
|
:records [[:inline {:_id 1 :name "cat"}]
|
|
|
|
|
[:inline {:_id 2 :name "dog"}]]})))
|
2024-12-11 19:50:47 +00:00
|
|
|
(is (= ["PATCH INTO foo RECORDS ?, ?" {:_id 1 :name "cat"} {:_id 2 :name "dog"}]
|
2024-12-11 23:35:26 +00:00
|
|
|
(sql/format {:patch-into [:foo ; as a sub-clause
|
2024-12-11 19:50:47 +00:00
|
|
|
{:records [{:_id 1 :name "cat"}
|
|
|
|
|
{:_id 2 :name "dog"}]}]})))
|
|
|
|
|
(is (= ["PATCH INTO foo RECORDS ?, ?" {:_id 1 :name "cat"} {:_id 2 :name "dog"}]
|
|
|
|
|
(sql/format (h/patch-into :foo
|
|
|
|
|
(h/records [{:_id 1 :name "cat"}
|
|
|
|
|
{:_id 2 :name "dog"}])))))))
|
|
|
|
|
|
2024-11-23 20:17:43 +00:00
|
|
|
(deftest object-record-expr
|
|
|
|
|
(testing "object literal"
|
|
|
|
|
(is (= ["SELECT OBJECT (_id: 1, name: 'foo')"]
|
|
|
|
|
(sql/format {:select [[[:object {:_id 1 :name "foo"}]]]})))
|
|
|
|
|
(is (= ["SELECT OBJECT (_id: 1, name: 'foo')"]
|
|
|
|
|
(sql/format '{select (((:object {:_id 1 :name "foo"})))}))))
|
|
|
|
|
(testing "record literal"
|
|
|
|
|
(is (= ["SELECT RECORD (_id: 1, name: 'foo')"]
|
|
|
|
|
(sql/format {:select [[[:record {:_id 1 :name "foo"}]]]})))
|
|
|
|
|
(is (= ["SELECT RECORD (_id: 1, name: 'foo')"]
|
|
|
|
|
(sql/format '{select (((:record {:_id 1 :name "foo"})))}))))
|
|
|
|
|
(testing "inline map literal"
|
|
|
|
|
(is (= ["SELECT {_id: 1, name: 'foo'}"]
|
|
|
|
|
(sql/format {:select [[[:inline {:_id 1 :name "foo"}]]]})))))
|
2024-11-23 21:16:23 +00:00
|
|
|
|
|
|
|
|
(deftest navigation-dot-index
|
|
|
|
|
(is (= ["SELECT (a.b).c[1].d"]
|
|
|
|
|
(sql/format '{select (((get-in a.b c 1 d)))})))
|
|
|
|
|
(is (= ["SELECT (a.b).c[?].d" 1]
|
|
|
|
|
(sql/format '{select (((get-in a.b c (lift 1) d)))})))
|
|
|
|
|
(is (= ["SELECT (a.b).c[?].d" 1]
|
|
|
|
|
(sql/format '{select (((get-in (. a b) c (lift 1) d)))})))
|
|
|
|
|
(is (= ["SELECT (OBJECT (_id: 1, b: 'thing').b).c[?].d" 1]
|
|
|
|
|
(sql/format '{select (((get-in (. (object {_id 1 b "thing"}) b) c (lift 1) d)))}))))
|
2025-02-21 01:03:44 +00:00
|
|
|
|
|
|
|
|
(deftest assert-statement
|
|
|
|
|
(is (= ["ASSERT NOT EXISTS (SELECT 1 FROM users WHERE email = 'james @example.com')"]
|
|
|
|
|
(sql/format '{assert (not-exists {select 1 from users where (= email "james @example.com")})}
|
|
|
|
|
:inline true)))
|
|
|
|
|
(is (= ["ASSERT TRUE"]
|
|
|
|
|
(sql/format '{assert true}
|
|
|
|
|
:inline true))))
|