Skip to content

refactor: rename Utopia\Query\Parser to Classifier - #17

Open
abnegate wants to merge 1 commit into
refactor/drop-unsupported-exceptionfrom
refactor/rename-parser-to-classifier
Open

refactor: rename Utopia\Query\Parser to Classifier#17
abnegate wants to merge 1 commit into
refactor/drop-unsupported-exceptionfrom
refactor/rename-parser-to-classifier

Conversation

@abnegate

Copy link
Copy Markdown
Member

Stacked on #16 (which is stacked on #15). Review those first, or read only the single commit on this branch.

Why

Two unrelated subsystems both called themselves a parser:

Utopia\Query\Parser Utopia\Query\AST\Parser
Input raw wire-protocol bytes Token[] from Tokenizer
Output Type::{Read,Write,Transaction,…} AST\Statement\Select
How reads the leading keyword, looks it up in a table recursive descent, MAX_DEPTH = 256
For proxies, read/write splitting, audit logs inspecting and rewriting SQL

They share no code — I checked both directions. Any file needing both had to alias one.

Only the second one parses. The first never builds a structure: its implementation is extractKeyword() plus keyword lookup tables, its own method is already called classifySQL(), and its return type is a classification enum. The name was the odd part, not the behaviour.

What changed

Utopia\Query\Parser                                  -> Utopia\Query\Classifier
Utopia\Query\Parser\{SQL,MySQL,PostgreSQL,MongoDB}   -> Utopia\Query\Classifier\{…}
Classifier::parse()                                  -> Classifier::classify()

classifySQL() and extractKeyword() keep their names — they take SQL text rather than wire bytes, so they're a genuinely different entry point from classify(), not a redundant one.

Classifier\MongoDB also carried no-op classifySQL() and extractKeyword() stubs, returning Type::Unknown and '' under a "not applicable" comment, despite the interface never requiring them. MongoDB doesn't speak SQL, so following #16 they're gone rather than answering meaninglessly. The two tests that asserted only those no-op returns become one asserting absence.

Both classes now carry a docblock pointing at the other, so the distinction is discoverable from either side, and the README section is renamed with a note plus links each way.

Notes for review

  • Breaking: Classifier is a public interface an external proxy would implement, and parse()classify() changes its one method. Pre-1.0 (0.3.3).
  • No behaviour change beyond dropping the two MongoDB no-op stubs. Classification results are identical.
  • Test namespace moved Tests\Query\ParserTests\Query\Classifier; local $parser variables renamed to $classifier. Two commit subjects quoted in a SecurityRegressionTest history comment still say "parser" — those are historical references and were left alone.
  • This touches the "Wire Protocol Parsers" README section rewritten in docs(readme): sync with code, document AST layer and correct dialect claims #15, so expect that overlap in the stack.

Test plan

  • composer test — 5309 tests, 12345 assertions, all pass
  • composer check — PHPStan level max, no errors
  • composer lint — pass
  • Verified end to end against a real COM_QUERY packet and SQL text:
MySQL       instanceof Classifier=1 classify()=1 classifySQL()=1
PostgreSQL  instanceof Classifier=1 classify()=1 classifySQL()=1
MongoDB     instanceof Classifier=1 classify()=1 classifySQL()=0

MySQL classify(COM_QUERY 'SELECT 1') = Read
PG    classifySQL('INSERT INTO …')   = Write
PG    classifySQL('SAVEPOINT s')     = Transaction

Old names resolve to nothing (Utopia\Query\Parser, Utopia\Query\Parser\MySQL); Utopia\Query\AST\Parser is untouched.

🤖 Generated with Claude Code

Two unrelated subsystems both called themselves a parser, so any file
needing both had to alias one:

  Utopia\Query\Parser       raw wire bytes  -> Type
  Utopia\Query\AST\Parser   Token[]         -> AST\Statement\Select

Only the second one parses. The first reads a message's leading keyword
(or, for OP_MSG, its first command name) and looks it up in a table -- it
never builds a structure. Its own method was already called
classifySQL(), and its return type is a classification enum, so the name
was the odd part, not the behaviour.

  Utopia\Query\Parser                -> Utopia\Query\Classifier
  Utopia\Query\Parser\{SQL,MySQL,
    PostgreSQL,MongoDB}              -> Utopia\Query\Classifier\{...}
  Classifier::parse()                -> Classifier::classify()

classifySQL() and extractKeyword() keep their names: they take SQL text
rather than wire bytes, so they are a genuinely different entry point
from classify().

Classifier\MongoDB carried no-op classifySQL() and extractKeyword()
stubs -- returning Type::Unknown and '' with a "not applicable" comment
-- even though the interface never required them. MongoDB does not speak
SQL, so per the previous commits they are gone rather than answering
meaninglessly. Two tests that asserted only those no-op returns are
replaced with one asserting the methods are absent.

Both classes now carry a docblock pointing at the other, and the README
section is renamed with a note on the distinction plus links each way.

Breaking: Classifier is a public interface an external proxy would
implement. Pre-1.0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR consistently renames the wire-protocol query-classification API from Parser/parse() to Classifier/classify() while leaving the separate AST parser intact.

  • Moves the SQL, MySQL, PostgreSQL, and MongoDB implementations and tests into Classifier namespaces.
  • Removes two intentionally meaningless SQL helper stubs from the MongoDB classifier.
  • Updates documentation and cross-references to distinguish classification from structural SQL parsing.

Confidence Score: 5/5

The PR appears safe to merge, with the public rename applied consistently across implementations, tests, autoloaded paths, and documentation.

No concrete blocking or independently actionable non-blocking defect remains; repository references consistently use the new API, and the intentional breaking changes are clearly documented.

Important Files Changed

Filename Overview
src/Query/Classifier.php Introduces the renamed public interface with a consistent classify(string $data): Type contract.
src/Query/Classifier/SQL.php Moves the shared SQL classifier to its new namespace without changing classification behavior.
src/Query/Classifier/MySQL.php Renames the MySQL classifier namespace and wire-message entry point consistently.
src/Query/Classifier/PostgreSQL.php Renames the PostgreSQL classifier namespace and wire-message entry point consistently.
src/Query/Classifier/MongoDB.php Adopts the new interface and removes SQL-specific no-op helpers that were not part of its contract.
src/Query/AST/Parser.php Adds documentation distinguishing structural AST parsing from wire-protocol classification.
README.md Updates public examples, headings, method names, and cross-links for the renamed API.
tests/Query/Classifier/MongoDBTest.php Migrates MongoDB tests to the renamed API and explicitly verifies that SQL-only helpers are absent.

Fix All in Greploop

Reviews (1): Last reviewed commit: "refactor: rename Utopia\Query\Parser to ..." | Re-trigger Greptile

@github-actions

Copy link
Copy Markdown

📊 Coverage

Metric Covered Ratio
Lines 91.91% 7452 / 8108
Methods 84.22% 1105 / 1312
Classes 65.26% 139 / 213

Full per-file breakdown in the job summary.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant