Skip to content

HAVING, OFFSET, and post-GROUP BY ORDER BY are parsed then silently discarded #32

Description

@mp-grind

Summary

Three of the SQL features listed in one README bullet do not take effect. None of
them raise an error — the query succeeds and returns a wrong result, which makes
them easy to build on without noticing.

README (line 49) claims:

GROUP BY ... HAVING, LIMIT ... OFFSET, UNION/UNION ALL/INTERSECT/EXCEPT: standard aggregate filtering, pagination, and set operations across SELECTs

Observed on mq-db 0.2.1: HAVING is ignored, OFFSET is ignored, ORDER BY
after a GROUP BY is ignored, and the set operators error out.

Reproduction

mkdir -p /tmp/mqbug/docs && cd /tmp/mqbug
printf '# Alpha\n\nIntro paragraph.\n\n## Install\n\nRun the installer.\n' > docs/a.md
printf '# Beta\n\nAnother paragraph.\n\n## Usage\n\nUse it well.\n'       > docs/b.md
mq-db index docs -o s.mq-db

That yields 2 documents and 8 blocks, 4 per document (blocks.id = 0..7).

1. HAVING is ignored

$ mq-db sql -d s.mq-db -F json \
  "SELECT document_id, COUNT(*) AS n FROM blocks GROUP BY document_id HAVING COUNT(*) > 100"
[{"document_id":0,"n":4},{"document_id":1,"n":4}]

Expected: zero rows (no group has more than 100 blocks). Actual: every group,
identical to the same query without the HAVING.

2. OFFSET is ignored

$ mq-db sql -d s.mq-db -F json "SELECT id FROM blocks ORDER BY id LIMIT 2 OFFSET 4"
[{"id":0},{"id":1}]

Expected: [{"id":4},{"id":5}]. Actual: the same rows as LIMIT 2 with no
offset.

3. ORDER BY after GROUP BY is ignored

$ mq-db sql -d s.mq-db -F json \
  "SELECT document_id, COUNT(*) AS n FROM blocks GROUP BY document_id ORDER BY document_id DESC"
[{"document_id":0,"n":4},{"document_id":1,"n":4}]

Expected document_id 1 then 0. ORDER BY works correctly on an ungrouped
query, so this is specific to the aggregate path.

4. Set operators error (loud, but contradicts the README)

$ mq-db sql -d s.mq-db "SELECT path FROM documents UNION ALL SELECT path FROM documents"
Error: SQL execution error: unsupported query type
$ mq-db sql -d s.mq-db "SELECT path FROM documents EXCEPT SELECT path FROM documents"
Error: SQL execution error: unsupported query type

This one fails safely, so it's much less of a problem than 1–3 — but since it is
in the same README bullet, either the implementation or the bullet needs a
change.

EXPLAIN confirms the clauses are dropped, not mis-evaluated

$ mq-db sql -d s.mq-db "EXPLAIN SELECT document_id, COUNT(*) AS n FROM blocks GROUP BY document_id HAVING COUNT(*) > 100"
│ query:from     │ blocks (blocks)  │
│ query:where    │ none — full scan │
│ query:group-by │ 1 key(s)         │

$ mq-db sql -d s.mq-db "EXPLAIN SELECT id FROM blocks ORDER BY id LIMIT 2 OFFSET 4"
│ query:from     │ blocks (blocks)  │
│ query:where    │ none — full scan │
│ query:order-by │ id ASC           │
│ query:limit    │ 2                │

There is no having step and no offset step. The parser accepts the clauses;
the planner never emits them.

The underlying machinery already works

Doing the same filtering and sorting one level out, over a CTE, gives the right
answers — including the negative case:

$ mq-db sql -d s.mq-db -F json "WITH c AS (SELECT document_id, COUNT(*) AS n FROM blocks GROUP BY document_id) SELECT * FROM c WHERE n > 2"
[{"document_id":0,"n":4},{"document_id":1,"n":4}]

$ mq-db sql -d s.mq-db -F json "WITH c AS (...same...) SELECT * FROM c WHERE n > 4"
[]

$ mq-db sql -d s.mq-db -F json "WITH c AS (...same...) SELECT * FROM c ORDER BY document_id DESC"
[{"document_id":1,"n":4},{"document_id":0,"n":4}]

So aggregation, post-aggregate filtering, and post-aggregate sorting are all
correct. Only the inline HAVING / post-GROUP BY ORDER BY clauses are not
routed into them. That also gives users a workaround in the meantime.

Suggested resolution

Whatever the fix timeline, rejecting an unsupported clause is much better than
ignoring it.
A HAVING that returns unfiltered rows is a wrong answer a caller
will act on; Error: HAVING is not supported is one they can see. The set
operators already behave this way, and that's the right behavior.

Environment

  • mq-db 0.2.1, installed via cargo install
  • macOS 26.6.2 (build 25G83), arm64

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions