Skip to content

chore: absorb query into packages/query - #13896

Merged
ChiragAgg5k merged 327 commits into
mainfrom
chore/absorb-query
Sep 26, 2026
Merged

ChiragAgg5k merged 327 commits into
mainfrom
chore/absorb-query

Conversation

@ChiragAgg5k

@ChiragAgg5k ChiragAgg5k commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Summary

This moves utopia-php/query into packages/query, so Appwrite loads it directly. It follows the playbook in rfc/monorepo.md (wave 1 of #13828) and comes in three commits, plus a follow-up and merges from main:

  1. Add 'packages/query/' from commit '67f89d99…': subtree import from the standalone repo with full history. The mirror head is the 0.6.1 tag, so this also upgrades Appwrite from 0.6.0 to 0.6.1. The only change between them is feat(postgres): alter a column's nullability utopia-php/query#22, which adds Schema\PostgreSQL::alterColumnNullable() (plus its test). It's additive, and no Appwrite call site needs a change.
  2. chore(query): mirror plumbing: absorb replaces the repo's CI, linter, static-analysis and coverage-baseline workflows with mirror.yml, adds the README banner, and removes pint.json and composer.lock.
  3. refactor: load query from packages/, plus refactor(query): keep the library source as released:
    • Adds the root autoload and replace entries, drops the utopia-php/query: 0.6.* require line, and removes the package from the lock. utopia-php/audit and utopia-php/usage still require utopia-php/query from Packagist, and replace satisfies them.
    • Step A of the standard shape:
      • src/Query/* moves up to src/.
      • tests/Query/* moves up to tests/, and tests/Integration becomes tests/E2E, since it runs against real MySQL, MariaDB, PostgreSQL, ClickHouse and MongoDB.
      • Test namespaces move from Tests\Query and Tests\Integration to Utopia\Query\Tests and Utopia\Query\Tests\E2E, with a matching autoload-dev entry.
      • phpunit.xml now has unit (with tests/E2E excluded) and e2e suites. The scripts are test, test:performance and test:e2e. The coverage scripts, paratest and phpcov only fed the deleted coverage workflow, so they're gone.
    • Library source is unchanged apart from the path move. src/ is byte-identical to the 0.6.1 tag except for one line. The standalone phpstan.neon (level max over src and tests) passes with no baseline. Its only finding was a dead $col->table !== null check in Builder::applyAstColumns(): an earlier continue already handles a Star with a null table, and Star is readonly. Removing that check is the one change to src/.
    • Adds a rector.php (PHP sets and type declarations, with a longer parallel timeout for the ~100K Builder.php). Like balancer and detector, it skips the rules that would rewrite src/ (skips scoped to src/), so the library code stays as released. Rector and Pint cleanups in tests/ do apply, and they're mechanical: typed arrow functions, new Foo()->bar(), imported names, ordered_imports and function_declaration.
    • docker-compose.test.yml becomes docker-compose.yml with healthchecks, so docker compose up --wait in bin/monorepo test waits for the databases to be ready. MongoDB moves from host port 27017 to 17017 so it can't collide with a local Appwrite stack.
    • Drops a stray .claude/plans/ file (the package's own .gitignore already ignored .claude/) and the README badges for the deleted workflows. The README's Contributing section now points to bin/monorepo check/test query and the new compose file.
    • One test literal (CorrectnessRegressionTest) had significant trailing whitespace in a multi-line string, and git diff --check flagged it. It now uses \n instead, with the same value.
    • The root phpunit.xml excludes the performance group. Five query tests assert microsecond timings (for example, classifySQL < 2.0 us). Upstream kept them out of composer test for the same reason, and they'd be flaky in the shared packages suite.

There's nothing to hoist, since the package requires only PHP. mongodb/mongodb stays in its require-dev for the e2e tier. Appwrite uses the package in Usage/{Connection,Concurrency,Policy}.php, Execution/Store.php and the Usage module's Http/Action.php, Events/XList.php and Gauges/XList.php. Root PHPStan passes on all of them.

Validation

  • bin/monorepo validate: all packages valid
  • bin/monorepo check query: Rector, Pint, and PHPStan level max (no baseline) pass
  • bin/monorepo test query: unit 5,352 tests / 12,479 assertions; e2e 286 tests / 926 assertions against MySQL 8.4, MariaDB 11, PostgreSQL 16 (pgvector), ClickHouse 24 and MongoDB 7 from docker-compose.yml
  • vendor/bin/phpunit --testsuite packages --filter 'Utopia\\Query\\Tests': 5,352 tests pass under the root autoloader, with E2E and performance excluded
  • vendor/bin/phpunit tests/unit/Usage: 10 tests pass
  • Root PHPStan on the 8 Appwrite files that use Utopia\Query: no errors
  • composer validate --no-check-publish: valid
  • composer.lock: after merging main, the only change is removing utopia-php/query 0.6.0
  • bin/monorepo split query --dry-run: fast-forwards from the mirror head 67f89d99
  • git diff --check

Merge and follow-up

Warning

Merge with a merge commit, not a squash. A squash drops the git-subtree-* annotation, and the split then gets rejected by the mirror.

absorb normalised the utopia-php/query mirror's existing main ruleset (id 15759184) to the canonical config. Before, its bypass actor was team 6789480 and it required 1 approval. Now the split app (4016286) is the only always-bypass actor and 0 approvals are required, matching the other mirrors.

After merge: confirm Split pushes to utopia-php/query. The mirror has no open issues or PRs to triage.

🤖 Generated with Claude Code

abnegate and others added 30 commits April 21, 2026 20:57
# Conflicts:
#	src/Query/Query.php
#	tests/Query/QueryTest.php
Reintroduces Query::LOGICAL_TYPES (now a list of Method cases), uses
Method->value for shape() string concat, migrates lingering
Query::TYPE_AND/OR/ELEM_MATCH references in tests to Method cases, and
resolves PHPStan level-max issues across Builder/MongoDB, Parser/MongoDB,
and assorted test files (tighter array shapes, explicit instanceof
guards, extracted CollectingVisitor, widened mergeIntoCollection pipeline
types, removed dead helpers).

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
CI runners are too variable to meet the <1-5us/op targets these tests
assert against; only run them on dedicated hardware.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…ggregate pipelines

update() built update operators before filters, but binding replacement
walks the serialized op JSON in key order (filter first, then update),
so the wrong bindings ended up in each slot — filters silently matched
nothing and updates set the wrong values.

Also, empty stdClass instances (BSON "{}") were being lost when the
integration client round-tripped queries through json_decode(assoc:true),
degrading operators like $documentNumber into invalid BSON arrays.
Decode as objects and preserve empty stdClass so the MongoDB driver
still encodes them as documents.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Window function aliases were produced by \$setWindowFields but then
dropped by the subsequent \$project stage because they weren't in the
select list, so callers never saw the generated column.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…entifier escaping

- Serializer: pass a stricter precedence to the right child for
  non-commutative left-associative operators (-, /, %) so trees like
  a - (b - c) serialise with parens and are not silently rewritten as
  a - b - c.
- Serializer: always separate a prefix unary operator from its operand
  so '-' followed by a negative numeric literal or nested unary can no
  longer collide into '--' and become a MySQL line comment.
- Serializer: escape backslashes in string literals before escaping
  single quotes, so a value ending in '\\' cannot break out of the
  quoted literal.
- Parser: un-double escaped delimiters in backtick, double-quoted and
  bracket-quoted identifiers (e.g. `foo``bar` -> foo`bar).
- MySQL tokenizer: mirror the single-quote branch's backslash-skip
  inside "..." so a # after an escaped quote stays inside the string
  literal and is not rewritten to --.
- MySQL tokenizer: replace # with -- (not '-- ') so the resulting line
  comment text matches the original.
- buildJoinStages now throws UnsupportedException when a non-equality join
  operator is passed — $lookup localField/foreignField only supports equality.
- update() rejects setRaw/setCase/conflictRaw sets instead of silently dropping
  them; these have no clean MongoDB translation.
- buildWindowFunctions rejects multi-argument window functions (COVAR(a, b))
  and requires ORDER BY for RANK/DENSE_RANK/ROW_NUMBER at build time instead
  of failing at runtime inside MongoDB.
- buildFieldExists now emits {$type: 10} for IS NULL and
  {$exists: true, $ne: null} for IS NOT NULL so the result mirrors SQL's
  IS NULL semantics (present and explicitly null) instead of conflating
  missing and null documents.
- insertOrIgnore builds its operation descriptor directly instead of
  round-tripping through json_decode, avoiding empty-stdClass corruption.
- buildDistinct resolves each attribute once per field instead of twice,
  halving attribute-hook invocations.
- Imports stdClass at the top and drops leading backslashes at call sites.
- Adds regression tests for every fix above, including the window-function
  projection preservation from commit 06ceaca.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
MySQL's default SQL mode treats backslash as an escape character, so values
ending in `\` can escape the closing single quote and break out of quoted
strings. The previous `str_replace("'", "''", $value)` only doubled single
quotes, leaving backslashes unescaped.

- Escape backslash before single quote in every DDL string-literal path:
  column comments, default values, ENUM values, table comments, column
  comments, partition names, collation option values, sequence names.
- ClickHouse enum escape now also doubles the backslash before applying
  `\'` quote escape so a value like `\'` cannot terminate the literal.
- `PostgreSQL::createCollation()` now validates option keys against
  `/^[A-Za-z_][A-Za-z0-9_]*$/` and throws `ValidationException` for keys
  that would allow SQL injection (e.g. `"provider = 'x', danger"`).
- `PostgreSQL::tablesample()` validates method against `BERNOULLI|SYSTEM`.
- `PostgreSQL::explain()` validates format against `TEXT|XML|JSON|YAML|''`.

Adds regression tests covering backslash-escaped enum/default/comment in
MySQL and ClickHouse, invalid collation option keys, and invalid
TABLESAMPLE method / EXPLAIN format on PostgreSQL.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Per project rule, prefer `array_push($items, ...$new)` over `array_merge`
in loops (merge copies the entire array every iteration) and over
per-element `foreach`-forwarding when a bulk append suffices.

- Add `Builder::addBindings(array)` that does one `array_push` spread
  instead of N method calls. Replace every
  `foreach ($x->bindings as $b) { \$this->addBinding(\$b); }` pattern
  across Builder.php and the MySQL, PostgreSQL, ClickHouse, MongoDB,
  SQL sub-builders.
- Replace `array_merge` in the recursive AST walks
  (`astWhereToQueries`, `astExpressionToSingleQuery`) and in
  `Query::validate()`'s nested-query recursion with `array_push` spreads.
- Import `stdClass` in `Builder/MongoDB.php` and `Schema/MongoDB.php`
  instead of referencing `\stdClass` with a leading backslash.
- Import `ValidationException` at the top of `Query.php` so the two
  `throw new \Utopia\Query\Exception\ValidationException(...)` sites in
  `Query::page()` use the short name.

No behaviour change. All unit tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…b-builder-guards

# Conflicts:
#	src/Query/Builder/MongoDB.php
…mes, parser depth, and tokenizer bounds

- Builder::selectCast() now rejects cast types that contain characters
  outside [A-Za-z0-9_(), ] to prevent raw-SQL injection through the
  CAST target.
- Builder::selectWindow() now rejects function strings that do not
  match the shape identifier(...), blocking trailing clauses, comment
  markers, and statement terminators.
- AST Parser enforces a recursion depth limit (256) on parseExpression
  to prevent stack overflow on deeply-nested parenthesised input.
- Tokenizer::readString() now throws ValidationException when a
  backslash appears at EOF, replacing a silent pos-past-length bug.
- MongoDB builder rejects field names that are empty or start with
  '$' across set, push, pull, pullAll, addToSet, increment, multiply,
  rename, unsetFields, currentDate, popFirst, popLast, updateMin,
  updateMax, and pushEach.
- Added unit tests for fromNone() and selectCast() across MySQL,
  PostgreSQL, SQLite, and ClickHouse dialects.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…cit opt-in

Raw queries bypass the binding/escaping pipeline, so accepting them from
JSON lets an attacker smuggle arbitrary SQL through any endpoint that calls
Query::parse on untrusted input. parse, parseQuery, and parseQueries now
throw ValidationException when they encounter Method::Raw unless the caller
explicitly passes allowRaw: true (for trusted round-trips such as in-memory
caches). The flag propagates into nested logical queries so Raw cannot be
hidden inside an or/and/elemMatch either.

Also rewrite groupByType() from an IIFE-per-arm match (true) pattern to
plain switch/case with direct mutations: one Closure allocation per call
per arm disappears and the control flow reads in source order. While there,
fix the cursor fallback that previously coerced a null cursor value to the
row-count limit; it now stays null.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Replace stringly-typed $direction ('ASC'/'DESC') and $nulls
('FIRST'/'LAST'/null) on OrderByItem with OrderDirection and
NullsPosition enum types. Producers (Parser, Builder) and consumers
(Serializer, Builder order-by emission) updated to use enum cases
and enum->value for SQL emission. Emitted SQL shape is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
… introduce MongoDB enums

Replace 13 parallel update-operation fields (pushOps, pullOps, addToSetOps,
incOps, unsetOps, renameOps, mulOps, popOps, pullAllOps, minOps, maxOps,
currentDateOps, pushEachOps) with a single updateOperations table keyed by
UpdateOperator enum value. Each operator's payload shape is constructed in
its setter, and buildUpdate() iterates once over the table to emit the final
update document.

Introduce three enums to eliminate magic strings throughout the builder:
Operation (find/insertMany/updateMany/deleteMany/updateOne/aggregate),
UpdateOperator (\$set/\$push/\$pull/etc.), and PipelineStage (\$match/\$group/
etc.). Also add an UpdateOperation readonly value object for typed consumers.

JSON output shape and all public method signatures are preserved — this is a
pure internal restructuring with compile-time safety guarantees.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…patialDistanceFilter DTO

QuotesIdentifiers::quote() now skips explode/array_map/implode for dotless
identifiers (the vast majority of inputs), directly wrapping and escaping
in one pass. In the dotted path, only the final segment may be bare '*' —
intermediate '*' segments are now quoted as literal identifiers instead of
silently passed through.

Method::sqlFunction() provides a single authoritative mapping from
aggregate/statistical/bitwise Method cases to standard SQL function names,
returning null for non-aggregation methods. Callers can migrate in later
cycles.

SpatialDistanceFilter is a typed readonly DTO replacing the opaque
[geometry, distance, meters] 3-tuple at the read sites in MariaDB, MySQL,
and PostgreSQL builders. Write sites in Query.php are untouched; the DTO
normalizes the tuple via fromTuple() so callers get named fields and
compile-time safety.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…ckHouse regex-based afterBuild with structured slot hooks

Builder::build() was a ~466-line monolith handling SELECT, CTE, UNION,
15+ clause sections, and alias qualification inline. It now orchestrates
named private helpers (buildCtePrefix, buildSelectClause, buildFromClause,
buildJoinsClause, buildWhereClause, buildGroupByClause, buildHavingClause,
buildWindowClause, buildOrderByClause, buildLimitClause, buildLockingClause,
buildUnionSuffix) plus a prepareAliasQualification step and a
compileWindowSelect / compileOrderByList / buildAggregationAliasMap trio.
Each helper has a single responsibility and emits its bindings in document
order, preserving byte-identical SQL output.

ClickHouse used to compile via parent::build() then post-hoc regex-rewrite
the SQL to splice in ARRAY JOIN, raw ASOF joins, GROUP BY modifier,
LIMIT BY, and SETTINGS. That approach could match keywords inside string
literals or identifiers and required a hand-rolled placeholder counter
(preg_match_all on '?') to reposition the LIMIT BY binding.

Builder now exposes four no-op protected hooks that subclasses override
to emit dialect-specific fragments at the correct position during build():

  buildAfterJoinsClause()    after JOINs, before WHERE
  buildAfterGroupByClause()  after GROUP BY, before HAVING
  buildAfterOrderByClause()  after ORDER BY, before LIMIT
  buildSettingsClause()      trailing settings, before UNION suffix

ClickHouse overrides all four. Its ARRAY JOIN / raw ASOF joins / PREWHERE
collapse into buildAfterJoinsClause; groupByModifier into
buildAfterGroupByClause; LIMIT BY (with its count binding added at the
moment of emission) into buildAfterOrderByClause; SETTINGS into
buildSettingsClause. The ClickHouse::build() override,
injectBeforeFirstKeyword, findKeywordPosition, and the preg_match_all
placeholder-counting hack are all deleted -- bindings ordering is now
naturally correct because emission happens in document order.

A new regression test asserts that identifiers (settings_table,
array_join_col, limit_by_col, order_by_col) and bound string literals
containing SQL clause keywords ('LIMIT 1 SETTINGS foo', 'ARRAY JOIN tags',
'PREWHERE condition') round-trip through the builder untouched -- a
guarantee the regex-based version could not make.

Full unit suite (4164 tests, 10714 assertions) passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…tate machine

- PostgreSQL.alterColumnType: validate $type allowlist and reject ;/--/block
  comments plus 1024-char cap on USING expression
- PostgreSQL.createPartition: same semicolon/comment/length rejection on the
  partition expression
- Schema.compileIndexColumns: validate collation against identifier allowlist
  and require orders to be exactly ASC/DESC (OrderDirection enum)
- Schema/Index: widen collation allowlist to permit quoted identifiers
- SQL.createProcedure / PostgreSQL.createTrigger+createProcedure: document
  trust requirement and reject \$\$ inside dollar-quoted PL/pgSQL bodies
- Parser/SQL: replace naive byte scans in extractKeyword and classifyCTE
  with a shared state machine that skips single/double/backtick quoted
  strings, dollar-quoted bodies, line comments and block comments — keeps
  parenthesis depth honest and prevents keyword misclassification when
  payloads hide DML inside string literals or comments
- Parser/MongoDB: validate skipBsonString and skipBsonBinary length fields
  against the remaining buffer (and reject negative lengths on 32-bit PHP)
  so a crafted strLen=0xFFFFFFFF cannot drive reads past the wire buffer

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…kenizer coverage

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
… SpatialDistanceFilter DTO from worktree-agent-a25589ce
…ree-agent-add57390

# Conflicts:
#	tests/Query/Builder/MongoDBTest.php
…s from worktree-agent-a4a0da49

# Conflicts:
#	tests/Query/Builder/ClickHouseTest.php
- MongoDBTest: assertIsArray($op['projection'|'filter']) before assertArrayHasKey
  on mixed-typed map access (reserved-word/unicode tests from af4b444b).
- ClickHouseClient: fall back to original query when preg_replace_callback
  returns null.
- QueryParseTest: assertInstanceOf(Query::class) before calling getMethod()
  on array<mixed> element.
…e Case\Builder to Case\Expression; README BuildResult->Plan

- README no longer references the non-existent BuildResult class; every build()/insert()/update()/delete() returns Plan
- #[\Override] attributes on every overriding method across Builder, SQL, MySQL, MariaDB, PostgreSQL, SQLite, ClickHouse, MongoDB, AST serializers and AST visitors (343 attributes). PHP verifies correctness at load-time
- Case namespace: Builder->Expression (fluent), Expression->Result (DTO). Resolves naming collision with top-level Builder. All callers and tests updated
- README CASE example rewritten to the real API (no Builder::case() method)
Adds tests exercising the exact attack vectors closed by:

- d203ed7 (DDL validation + wire-parser state machine):
  alterColumnType/createPartition input rejection, extractKeyword
  ignores keywords inside quoted strings and block comments, classifyCTE
  ignores INSERT hidden in a string literal.
- 5662d27 (cast/window/mongo/parser/tokenizer bounds):
  MongoDB builder rejects dollar-prefixed and empty field names.
- c5a4ed3 (DDL backslash escaping):
  MySQL enum with trailing backslash, createCollation rejects invalid
  option keys, PostgreSQL tablesample rejects injected method token.
- 4eb2996 (AST tightening): DDL default value with backslash is
  doubled so a trailing backslash cannot escape the closing quote.
- ff64121 (Method::Raw): parse/parseQuery reject Raw by default, accept
  when allowRaw=true, and still reject Raw nested inside Or.
Covers Features that previously had no dedicated unit test file. Each
test file exercises the happy path, NULL/empty-input edge cases,
dialect-specific quoting, and binding-order properties using assertSame.

Added:

- Feature/SpatialTest
- Feature/LateralJoinsTest
- Feature/BitwiseAggregatesTest
- Feature/StatisticalAggregatesTest
- Feature/PostgreSQL/VectorSearchTest
- Feature/PostgreSQL/MergeTest
- Feature/PostgreSQL/ReturningTest
- Feature/PostgreSQL/OrderedSetAggregatesTest
- Feature/ClickHouse/ArrayJoinsTest
- Feature/ClickHouse/AsofJoinsTest
- Feature/MongoDB/AtlasSearchTest
- Feature/MongoDB/PipelineStagesTest
- Feature/MongoDB/FieldUpdatesTest
- Feature/MongoDB/ArrayPushModifiersTest
…0 methods)

Brings ClickHouseIntegrationTest.php from 15 to 20 test methods, covering
the gaps against MySQLIntegrationTest and PostgreSQLIntegrationTest:

- testSelectWithBetween
- testSelectWithStartsWithAndContains
- testSelectWithCaseExpression
- testSelectWithArrayJoin
- testSelectWithExistsSubquery

All new tests exercise real ClickHouse behaviour via the existing
ClickHouseClient harness and pass against clickhouse-server 24.
abnegate and others added 12 commits August 21, 2026 17:05
Keep the simple leftJoin(table, left, right, op, alias) triple, and accept
an array of Query::on() / filter queries so extra ON predicates stay on the
join instead of falling through to WHERE.
SQL compileFilter accepted search/regex/exists on nested JOIN ON while
toAst() silently rewrote them to Raw, so build() and toAst() disagreed.
SQL compilation prefixes unqualified Query::on operands with the base-table alias when joins are present; toAst() now applies the same qualification so AST consumers resolve the same JOIN columns.
feat: nest join ON conditions as Query objects
Index types already use IndexType; column order was still a raw ASC/DESC
string. Schema\Order is the matching enum so call sites do not keep
magic strings next to typed index types.
(feat): add Schema\Order for index column directions
Postgres carries NOT NULL through an ALTER COLUMN ... TYPE, so a column
that moves between required and optional keeps its old constraint. MySQL
resets it as part of MODIFY COLUMN, which is why only Postgres needs the
constraint altered on its own.

Co-Authored-By: Claude Opus 5 <[email protected]>
feat(postgres): alter a column's nullability
…684093'

git-subtree-dir: packages/query
git-subtree-mainline: b866958
git-subtree-split: 67f89d9
@ChiragAgg5k ChiragAgg5k added the absorb History-preserving package absorption; merge commit required label Sep 24, 2026
@greptile-apps

greptile-apps Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

[High risk] Absorbs external query library into monorepo packages.

The PR does not yet appear safe to merge because the public Column constructor remains incompatible with its former named argument, and the regex directive remains unsatisfied.

Fix All in Claude CodeFindings

  1. P1 Named constructor argument breaks ▶
  2. P2 Regex use violates repository directive ▶
Fix with agent prompt
### Issue 1
packages/query/src/Schema/Column.php:undefined-82
The public `Column` constructor parameter changed from `isAutoIncrement` to `autoIncrement`. Consumers using the previously valid `isAutoIncrement:` named argument now get an unknown named parameter error when constructing a column, even though in-repository callers were updated.

### Issue 2
packages/query/src/Builder/JoinBuilder.php:undefined-25
The imported identifier validation uses `preg_match`, and the new detector framework adapters also use regexes. The repository directive says not to add regexes unless no string operation or existing validator works and the PR explains why. This requirement must be satisfied before merging.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

The PR moves Utopia Query into packages/query, loads it through the root autoloader, and adds package-local test, database, and mirror tooling. The changes since the previous review bring in an already-merged DSN package from main; they do not change Query code or introduce a new finding.

Reviews (9) · Last reviewed commit: "Merge remote-tracking branch 'origin/mai..."

Comment thread packages/query/composer.json
Comment thread packages/query/tests/Regression/CorrectnessRegressionTest.php
Comment thread packages/query/src/AST/Parser.php
public ?int $scale = null,
?int $srid = null,
?int $dimensions = null,
bool $autoIncrement = false,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Named constructor argument breaks

The public Column constructor parameter changed from isAutoIncrement to autoIncrement. Consumers using the previously valid isAutoIncrement: named argument now get an unknown named parameter error when constructing a column, even though in-repository callers were updated.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/query/src/Schema/Column.php
Line: 82

Comment:
**Named constructor argument breaks**

The public `Column` constructor parameter changed from `isAutoIncrement` to `autoIncrement`. Consumers using the previously valid `isAutoIncrement:` named argument now get an unknown named parameter error when constructing a column, even though in-repository callers were updated.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Codex

*/
public function on(string $left, string $right, string $operator = '='): static
{
if (!\preg_match('/^[a-zA-Z_][a-zA-Z0-9_.]*$/', $left)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Regex use violates repository directive

The imported identifier validation uses preg_match, and the new detector framework adapters also use regexes. The repository directive says not to add regexes unless no string operation or existing validator works and the PR explains why. This requirement must be satisfied before merging.

Context Used: AGENTS.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/query/src/Builder/JoinBuilder.php
Line: 25

Comment:
**Regex use violates repository directive**

The imported identifier validation uses `preg_match`, and the new detector framework adapters also use regexes. The repository directive says not to add regexes unless no string operation or existing validator works and the PR explains why. This requirement must be satisfied before merging.

**Context Used:** AGENTS.md ([source](https://github.com/appwrite/appwrite/blob/main/AGENTS.md))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Claude Code Fix in Codex

@github-actions

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

✨ Benchmark results

Comparing main (before) → chore/absorb-query (after).

Metric Before After Change
🚀 Requests/sec 179.23 186.16 ⚪ +3.9%
⏱️ Latency P50 96.37 ms 93.04 ms ⚪ -3.5%
⏱️ Latency P95 227.66 ms 217.53 ms ⚪ -4.5%
Per-scenario breakdown & investigation details

Metrics below reflect the current branch (after). Δ P95 compares against the base.

Scenario P50 (ms) P95 (ms) Requests RPS Δ P95 (ms)
API total 93.04 217.53 11,457 186.16 -10.14
Account 184.78 347.88 603 10.05 -1.31
TablesDB 89.9 164.7 6,231 102.6 -15.36
Storage 85.5 186.11 3,015 51.73 -3.35
Functions 134.94 261.28 1,608 28.32 -13.59

Top API waits (after)

API request Max wait (ms)
functions.variables.update 542.43
account.name.update 540.04
functions.variables.create 499.9
account.prefs.update 474.69
tablesdb.rows.create 434.95

@ChiragAgg5k
ChiragAgg5k merged commit 6cf4c0c into main Sep 26, 2026
49 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

absorb History-preserving package absorption; merge commit required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants