I'm going through my application and wrapping all of my queries in a linter function that (in development) sets :query-stats to true, and flags when it warns about unbound variables.
I'm noticing that Datomic will warn about queries where the query matches only on a literal value in-line in the :where clause:
(d/q '[:find [?foo ...] :in $ :where [?foo :foo/tag "some-tag"]] db)
but not if the value is passed as an argument:
(d/q '[:find [?foo ...] :in $ ?tag :where [?foo :foo/tag ?tag]] db "some-tag")
I know the second style is more correct, but my intuition is that the performance should be the same for both queries. But maybe my intuition is wrong?
I'm asking because I'm trying to decide whether to address this by fixing the queries (some are dynamic so it's nontrivial to fix) or the linter. If the more-correct form is genuinely faster, there is a stronger case for fixing the queries.What is the warning you're looking for?
In general, you want to keep the number of distinct query forms in a process as low as possible. query form compilation is cached, and the cache has limited size. In addition, function expression forms generate eval calls during compilation, thus create distinct classes, thus can use a lot of metaspace over time.
What is the warning you're looking for?The :unbound-vars warning (I believe this is the only warning :query-stats currently emits? at least, it's the only one documented at https://docs.datomic.com/reference/query-stats.html#warnings and the only one I have encountered in practice.)
I had not considered the query form compilation aspect, thank you.
but you want this warning, or don't?
It sounds like this warning isn't aware of all binds from :in ?
I think I'm arguing that it should not warn for (d/q '[:find [?foo ...] :in $ :where [?foo :foo/tag "some-tag"]] db) because the "some-tag" literal in that where expression is functionally equivalent to a bound lvar in the same position.
Could you paste the query-stats you are seeing?
I think I see what you mean:
(datomic.api/query {:query '[:find [?foo ...] :in $ :where [?foo :foo/tag "some-tag"]]
:args [[]]
:query-stats true})
=>
{:ret [],
:query-stats {:query [:find [?foo ...] :in $ :where [?foo :foo/tag "some-tag"]],
:phases [{:sched (([?foo :foo/tag "some-tag"])),
:clauses [{:clause [?foo :foo/tag "some-tag"],
:rows-in 0,
:rows-out 0,
:binds-in (),
:binds-out [?foo],
:warnings {:unbound-vars #{?foo}}}]}]}}
(datomic.api/query {:query '[:find [?foo ...] :in $ ?tag :where [?foo :foo/tag ?tag]]
:args [[] "some-tag"]
:query-stats true})
=>
{:ret [],
:query-stats {:query [:find [?foo ...] :in $ ?tag :where [?foo :foo/tag ?tag]],
:phases [{:sched (([(ground $__in__2) ?tag] [?foo :foo/tag ?tag])),
:clauses [{:clause [(ground $__in__2) ?tag],
:rows-in 0,
:rows-out 1,
:binds-in (),
:binds-out [?tag],
:expansion 1}
{:clause [?foo :foo/tag ?tag],
:rows-in 1,
:rows-out 0,
:binds-in [?tag],
:binds-out [?foo]}]}]}}
The warning for "unbound vars" is about unbound vars on input to the row, not output