datalog: dl-magic-query falls back on built-in/agg/neg goals (222/222)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s

Bug: dl-magic-query was always trying to seed a magic_<rel>^<adn>
fact for the query goal. For aggregate goals like (count N X (p X))
this produced a non-ground "fact" (magic_count^... N X (p X)) and
dl-add-fact! correctly rejected it, surfacing as an error.

Fix: dl-magic-query now detects built-in / aggregate / negation
goals up front and dispatches to plain dl-query for those cases —
magic-sets only applies to positive non-builtin literals against
rule-defined relations. Other shapes don't benefit from the
rewrite anyway.

1 new test confirms (count N X (p X)) returns the expected
{:N 3} via dl-magic-query.
This commit is contained in:
2026-05-08 10:32:01 +00:00
parent 5a3db1a458
commit a648247ae4
4 changed files with 38 additions and 16 deletions

View File

@@ -376,16 +376,26 @@
dl-magic-query
(fn
(db query-goal)
(let
((query-rel (dl-rel-name query-goal))
(query-adn (dl-adorn-goal query-goal)))
(let
((query-args (dl-bound-args query-goal query-adn))
(rules (dl-rules db)))
;; Magic-sets only applies to positive non-builtin / non-aggregate
;; literals against rule-defined relations. For other goal shapes
;; (built-ins, aggregates, EDB-only relations) the seed is either
;; non-ground or unused; fall back to dl-query.
(cond
((or (dl-builtin? query-goal)
(dl-aggregate? query-goal)
(and (dict? query-goal) (has-key? query-goal :neg)))
(dl-query db query-goal))
(else
(let
((rewritten (dl-magic-rewrite rules query-rel query-adn query-args))
(mdb (dl-make-db))
(rule-heads (dl-magic-rule-heads rules)))
((query-rel (dl-rel-name query-goal))
(query-adn (dl-adorn-goal query-goal)))
(let
((query-args (dl-bound-args query-goal query-adn))
(rules (dl-rules db)))
(let
((rewritten (dl-magic-rewrite rules query-rel query-adn query-args))
(mdb (dl-make-db))
(rule-heads (dl-magic-rule-heads rules)))
(do
;; Copy EDB facts (relations not headed by any caller rule).
(for-each
@@ -400,4 +410,4 @@
;; Seed + rewritten rules.
(dl-add-fact! mdb (get rewritten :seed))
(for-each (fn (r) (dl-add-rule! mdb r)) (get rewritten :rules))
(dl-query mdb query-goal)))))))
(dl-query mdb query-goal)))))))))

View File

@@ -1,8 +1,8 @@
{
"lang": "datalog",
"total_passed": 221,
"total_passed": 222,
"total_failed": 0,
"total": 221,
"total": 222,
"suites": [
{"name":"tokenize","passed":26,"failed":0,"total":26},
{"name":"parse","passed":18,"failed":0,"total":18},
@@ -13,8 +13,8 @@
{"name":"negation","passed":10,"failed":0,"total":10},
{"name":"aggregates","passed":19,"failed":0,"total":19},
{"name":"api","passed":20,"failed":0,"total":20},
{"name":"magic","passed":23,"failed":0,"total":23},
{"name":"magic","passed":24,"failed":0,"total":24},
{"name":"demo","passed":21,"failed":0,"total":21}
],
"generated": "2026-05-08T10:29:04+00:00"
"generated": "2026-05-08T10:31:43+00:00"
}

View File

@@ -1,6 +1,6 @@
# datalog scoreboard
**221 / 221 passing** (0 failure(s)).
**222 / 222 passing** (0 failure(s)).
| Suite | Passed | Total | Status |
|-------|--------|-------|--------|
@@ -13,5 +13,5 @@
| negation | 10 | 10 | ok |
| aggregates | 19 | 19 | ok |
| api | 20 | 20 | ok |
| magic | 23 | 23 | ok |
| magic | 24 | 24 | ok |
| demo | 21 | 21 | ok |

View File

@@ -197,6 +197,18 @@
6)
;; dl-magic-query: end-to-end driver, doesn't mutate caller's db.
;; dl-magic-query falls back to dl-query for built-in,
;; aggregate, and negation goals (the magic seed would
;; otherwise be non-ground).
(dl-mt-test! "magic-query falls back on aggregate"
(let
((r (dl-magic-query
(dl-program "p(1). p(2). p(3).")
(list (quote count) (quote N) (quote X)
(list (quote p) (quote X))))))
(and (= (len r) 1) (= (get (first r) "N") 3)))
true)
(dl-mt-test! "magic-query equivalent to dl-query"
(let
((db (dl-program