datalog: dl-magic-query pre-saturates for aggregate correctness
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 13s

dl-magic-query could silently diverge from dl-query when an
aggregate's inner-goal relation was IDB. The rewriter passes
aggregate body lits through unchanged (no magic propagation
generated for them), so the inner relation was empty in the magic
db and the aggregate returned 0. Repro:

  (dl-eval-magic
    "u(a). u(b). u(c). u(d). banned(b). banned(d).
     active(X) :- u(X), not(banned(X)).
     n(N) :- count(N, X, active(X))."
    "?- n(N).")
  => ({:N 0})   ; should be ({:N 2})

dl-magic-query now pre-saturates the source db before copying facts
into the magic db. This guarantees equivalence with dl-query for
every stratified program; the magic benefit still comes from
goal-directed re-derivation of the query relation under the seed
(which matters for large recursive joins). The existing test cases
happened to dodge this because their aggregate inner-goals were all
EDB.

1 new regression test; conformance 274/274.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-11 08:59:28 +00:00
parent 66aa003461
commit 82dfa20e82
5 changed files with 85 additions and 33 deletions

View File

@@ -1,8 +1,8 @@
{
"lang": "datalog",
"total_passed": 273,
"total_passed": 274,
"total_failed": 0,
"total": 273,
"total": 274,
"suites": [
{"name":"tokenize","passed":31,"failed":0,"total":31},
{"name":"parse","passed":23,"failed":0,"total":23},
@@ -13,8 +13,8 @@
{"name":"negation","passed":12,"failed":0,"total":12},
{"name":"aggregates","passed":23,"failed":0,"total":23},
{"name":"api","passed":22,"failed":0,"total":22},
{"name":"magic","passed":36,"failed":0,"total":36},
{"name":"magic","passed":37,"failed":0,"total":37},
{"name":"demo","passed":21,"failed":0,"total":21}
],
"generated": "2026-05-11T08:49:02+00:00"
"generated": "2026-05-11T08:59:09+00:00"
}