Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Client API by
retagged by

Environment

  • com.datomic/client-cloud "1.0.117"
  • com.datomic/dev-local 1.0.238

History

(def client (d/client {:server-type :dev-local
                         :storage-dir :mem
                         :system      "test"}))
=> #'user/client
(d/create-database client {:db-name "a"})
=> true
(def conn (d/connect client {:db-name "a"}))
=> #'user/conn
(d/q '[:find (pull ?e [:db/doc]) (pull ?e [:db/ident])
         :where
         [?e :db/ident :db/cardinality]]
    (d/db conn))
Execution error (ArrayIndexOutOfBoundsException) at datomic.core.datalog/fn$project (datalog.clj:560).
Index 1 out of bounds for length 1

If you bind the same eid to a new var name, the exception will not be thrown.

(d/q '[:find (pull ?e [:db/doc]) (pull ?e2 [:db/ident])
       :where
       [?e :db/ident :db/cardinality]
       [(identity ?e) ?e2]]
  (d/db conn))
=>
[[#:db{:doc "Property of an attribute. Two possible values: :db.cardinality/one for single-valued attributes, and :db.cardinality/many for many-valued attributes. Defaults to :db.cardinality/one."}
  #:db{:ident :db/cardinality}]]

Expectation

Pulling the same entity in multiple locations in :find will pull the relevant data and not thrown an ArrayIndexOutOfBoundsException.

Actual

Pulling the same entity in multiple locations in :find throws an ArrayIndexOutOfBoundsException.

Evidence

Full stacktrace:

#error{:cause "Index 1 out of bounds for length 1",
       :via [{:type clojure.lang.ExceptionInfo,
              :message "processing clause: [?e :db/ident :db/cardinality], message: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1",
              :data #:cognitect.anomalies{:category :cognitect.anomalies/incorrect,
                                          :message "processing clause: [?e :db/ident :db/cardinality], message: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1"},
              :at [datomic.core.datalog$throw_query_ex_BANG_ invokeStatic "datalog.clj" 50]}
             {:type java.lang.ArrayIndexOutOfBoundsException,
              :message "Index 1 out of bounds for length 1",
              :at [clojure.lang.RT aset "RT.java" 2384]}],
       :trace [[clojure.lang.RT aset "RT.java" 2384]
               [datomic.core.datalog$fn__22911$project__22994 invoke "datalog.clj" 560]
               [datomic.core.datalog$fn__22911$join__23012 invoke "datalog.clj" 659]
               [datomic.core.datalog$fn__22911$fn__23015 invoke "datalog.clj" 666]
               [datomic.core.common$pooled_mapv$fn__22667$fn__22668 invoke "common.clj" 490]
               [clojure.lang.AFn applyToHelper "AFn.java" 152]
               [clojure.lang.AFn applyTo "AFn.java" 144]
               [clojure.core$apply invokeStatic "core.clj" 667]
               [clojure.core$with_bindings_STAR_ invokeStatic "core.clj" 1977]
               [clojure.core$with_bindings_STAR_ doInvoke "core.clj" 1977]
               [clojure.lang.RestFn invoke "RestFn.java" 425]
               [clojure.lang.AFn applyToHelper "AFn.java" 156]
               [clojure.lang.RestFn applyTo "RestFn.java" 132]
               [clojure.core$apply invokeStatic "core.clj" 671]
               [clojure.core$bound_fn_STAR_$fn__5767 doInvoke "core.clj" 2007]
               [clojure.lang.RestFn invoke "RestFn.java" 397]
               [clojure.lang.AFn call "AFn.java" 18]
               [java.util.concurrent.FutureTask run "FutureTask.java" 264]
               [java.util.concurrent.ThreadPoolExecutor runWorker "ThreadPoolExecutor.java" 1128]
               [java.util.concurrent.ThreadPoolExecutor$Worker run "ThreadPoolExecutor.java" 628]
               [java.lang.Thread run "Thread.java" 829]]}

Impact

Programmatically built queries that expect two positions in :find must apply a workaround to avoid the exception.

1 Answer

0 votes
by

Hi thank you for the detailed reproduction and problem statement. Each variable can appear in at most one pull expression. This behavior was not documented, but I've now added this to our docs on pull expressions in cloud and on-prem:

https://docs.datomic.com/cloud/query/query-data-reference.html#pull-expressions
https://docs.datomic.com/on-prem/query/query.html#pull-expressions

Cheers,
Jaret

by
Thanks for adding to the docs. That will be a useful reference. I'm curious, why have a constraint like this? This would be a useful feature.
by
I logged a ticket for a feature for allowing a variable to be used in multiple pull patterns.  I think your use case is valid for making a naive query builder.  I can't speak to the reason why the constraint exists, but know that the thought was if you have the entity you can just change/expand the pull pattern to include more information.  That doesn't work for your use case.
by
The thought is valid regardless :) When building queries programmatically, this constraint creates a painful hurdle though. Thanks for logging the feature request ticket.
by
I see what you did there :).  Point taken.
...