Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Queries passed to db fns #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,29 @@

App for illustrating and reporting Datomic bugs. Active bug reports may be in
different branches. See the open PRs.

## Report 1 2018-11-22

Running against latest Datomic Free transactor 0.9.5703 (but same issue shows
when running against lastest Datomic Pro version 0.9.5786 too).

### Details

Two issues show up, but they might be somewhat related.

Suppose that a database function is used to ensure correct atomic updates. Such
a generic function could, for instance, take a query and arguments for a query
as function arguments. Based on the query results, the function may allow the
transaction or throw.

Using such a function, we discover two issues:

1. Queries containing `not` clause behave unexpectedly.
2. Queries that use rules that have `or` clause break.

See [src/datomic_bug_report/core.clj](src/datomic_bug_report/core.clj) for
example.

Since neither issue manifests when using the Datomic in-memory database, it
seems that the underlying cause might be something related to serialization on
the wire.
95 changes: 93 additions & 2 deletions src/datomic_bug_report/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,65 @@

;; Set correct datomic storage pwd
(def datomic-uri "datomic:free://localhost:4334/datomic-bug-report?password=foobar")

(def user-1-id #uuid "5bf6b524-7d05-4078-9df3-a93c4a7007c5")
(def user-2-id #uuid "5bf6b53d-efcf-4d25-a05a-bdeeeb972cab")
(def user-3-id #uuid "5bf6b796-d05e-4079-8fea-95f7ce2f6376")

(def schema-tx
[])
[{:db/ident :user/id
:db/valueType :db.type/uuid
:db/cardinality :db.cardinality/one
:db/unique :db.unique/identity
:db/doc "User id"}
{:db/ident :user/type
:db/valueType :db.type/keyword
:db/cardinality :db.cardinality/one
:db/doc "User type"}
;; A db function that runs a query that is passed as argument and throws if
;; the query matches anything.
{:db/ident :tx/ensure-none
:db/doc "Throws if given query returns any result(s)."
:db/fn (d/function
{:lang "clojure"
:params '[db query args]
:code '(let [r (apply datomic.api/q query db args)]
(if (or (not (seqable? r))
(seq r))
(throw (ex-info ":tx/ensure-none constraint-violation"
{:query-result r}))
[]))})}])

(defn create-users!
[conn]
@(d/transact conn [{:user/id user-1-id
:user/type :admin}
{:user/id user-2-id
:user/type :user}
{:user/id user-3-id
:user/type :user}]))

(def query-ok-users-in-set '[:find [?e ...]
:in $ ?ids
:where [?e :user/id ?id]
[(contains? ?ids ?id)]])

;; Same as query-ok, but using a `not` clause
(def query-1-users-not-in-set '[:find [?e ...]
:in $ ?ids
:where [?e :user/id ?id]
(not [(contains? ?ids ?id)])])

(def rules
'[[(users-and-admins ?u)
(or [?e :user/type :user]
[?e :user/type :admin])]])

;; A query using `or` clause
(def query-2-users '[:find [?e ...]
:in $ %
:where [?e :user/id]
(users-and-admins ?e)])

(comment
;; 1. Start local transactor
Expand All @@ -24,8 +81,42 @@
;; 4. Create schema
@(d/transact conn schema-tx)

;; Bug examples here:
;; 5. Create users
(create-users! conn)

;; 6. Test queries
;; This will return 2 matching entity ids
(d/q query-ok-users-in-set (d/db conn) #{user-1-id user-2-id})

;; This finds 2 matching entity
(d/q query-1-users-not-in-set (d/db conn) #{user-1-id})

;; This returns all 3 users
(d/q query-2-users (d/db conn) rules)

;; 7. Transact with db fn :tx/ensure-none This uses the query-ok, which
;; behaves as expected, and the :tx/ensure-none throws constraint-violation
;; error, as expected.
@(d/transact conn
[[:tx/ensure-none
query-ok-users-in-set
[#{user-1-id user-2-id}]]])

;; BUG HERE:
;; 8. Transact with db fn :tx/ensure-none
;; This should throw, since the query matches
;; Instead, it transacts successfully
@(d/transact conn
[[:tx/ensure-none
query-1-users-not-in-set
[#{user-1-id}]]])

;; BUG 2 here: 9. `or` query inside a rule passed to db fn does not work and
;; throws error about the `or` clause.
@(d/transact conn
[[:tx/ensure-none
query-2-users
[rules]]])


)