datalog: aggregates work as top-level query goals (183/183)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 40s

Bug: dl-match-lit (the naive matcher used by dl-find-bindings)
was missing dl-aggregate? dispatch — it was only present in
dl-fbs-aux (semi-naive). Symptom:
  (dl-query db '(count N X (p X)))
silently returned ().

Two fixes:
- Add aggregate branch to dl-match-lit before the positive case.
- dl-query-user-vars now projects only the result var (first arg)
  of an aggregate goal — the aggregated var and inner-goal vars
  are existentials and should not leak into substitutions.

2 new aggregate tests cover count and findall as direct query goals.
This commit is contained in:
2026-05-08 09:45:15 +00:00
parent f33a8d69f5
commit d66ddc614b
5 changed files with 60 additions and 15 deletions

View File

@@ -83,6 +83,7 @@
(cond
((and (dict? lit) (has-key? lit :neg))
(dl-match-negation (get lit :neg) db subst))
((dl-aggregate? lit) (dl-eval-aggregate lit db subst))
((dl-builtin? lit)
(let
((s (dl-eval-builtin lit subst)))
@@ -426,16 +427,35 @@
(for-each
(fn
(g)
(let
((tgt
(if (and (dict? g) (has-key? g :neg)) (get g :neg) g)))
(for-each
(fn
(v)
(cond
((and (dict? g) (has-key? g :neg))
(for-each
(fn
(v)
(when
(and (not (= v "_")) (not (dl-member-string? v seen)))
(append! seen v)))
(dl-vars-of (get g :neg))))
((dl-aggregate? g)
;; Only the result var (first arg of the aggregate
;; literal) is user-facing. The aggregated var and
;; any vars in the inner goal are internal.
(let ((r (nth g 1)))
(when
(and (not (= v "_")) (not (dl-member-string? v seen)))
(append! seen v)))
(dl-vars-of tgt))))
(dl-var? r)
(let ((rn (symbol->string r)))
(when
(and (not (= rn "_"))
(not (dl-member-string? rn seen)))
(append! seen rn))))))
(else
(for-each
(fn
(v)
(when
(and (not (= v "_")) (not (dl-member-string? v seen)))
(append! seen v)))
(dl-vars-of g)))))
goals)
seen))))