Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
412 commits
Select commit Hold shift + click to select a range
f08deea
fix: remove redundant testCacheReconnect, already covered by utopia-p…
abnegate Mar 25, 2026
617cf69
fix: replace docker-based Redis test with mock, remove Redis-Destruct…
abnegate Mar 25, 2026
9107f2f
Selective startup
abnegate Mar 25, 2026
ed945f0
fix: use docker-compose profiles to only start needed services per CI…
abnegate Mar 25, 2026
08c10f2
fix: keep xdebug.so in image, restore volume mount
abnegate Mar 25, 2026
2150f9b
fix: disable xdebug in CI with XDEBUG_MODE=off
abnegate Mar 25, 2026
4767099
refactor: break SQLite inheritance from MariaDB, use Feature interfaces
abnegate Mar 25, 2026
25b1fe2
fix: add relationship guards for SQLite, fix lint issues
abnegate Mar 25, 2026
9ccb3e1
fix: add remaining relationship guards, make Pool implement Features
abnegate Mar 25, 2026
13a60f0
fix: update PHPStan baseline, remove redundant instanceof checks
abnegate Mar 25, 2026
1f65132
fix: correct malformed datetime test value
abnegate Mar 25, 2026
f952c28
fix: use ISO datetime format in test, MySQL rejects text format
abnegate Mar 25, 2026
5a53446
refactor: promote shared methods from concrete adapters to SQL.php
abnegate Mar 25, 2026
476aef4
fix: align spatial method signatures, fix import ordering
abnegate Mar 25, 2026
7be2b74
feat: expose schema builder, rename BuildResult to Plan, add permissi…
abnegate Mar 26, 2026
141df88
(chore): update lock
abnegate Mar 26, 2026
a99e777
(feat): unified addHook API, Decorator/Interceptor/Permissions/Tenanc…
abnegate Mar 26, 2026
ab86d6f
(fix): remove parse error from dangling statement and inline assignme…
abnegate Mar 26, 2026
a51656c
(fix): accept any PDO-compatible object in SQL adapter constructor
abnegate Mar 26, 2026
3006ceb
(fix): use object type for PDO parameter
abnegate Mar 26, 2026
57b4d17
(fix): remove DatabasePDO type constraints from SQL adapter
abnegate Mar 26, 2026
0946667
(fix): pass PermissionType enum to getPermissionsByType
abnegate Mar 26, 2026
6a256be
(fix): pass PermissionType enum to Input constructor
abnegate Mar 26, 2026
e6d2504
(fix): handle ColumnType::Float in row size calculation
abnegate Mar 26, 2026
f51f4a5
(fix): handle ColumnType::Float everywhere alongside Double
abnegate Mar 26, 2026
5efffc0
(fix): restore syncWriteHooks auto-registration for Permissions and T…
abnegate Mar 26, 2026
c0f0c20
(fix): pass PermissionType enum to getPermissionsByType in Permission…
abnegate Mar 26, 2026
3f2e0ef
(fix): revert syncWriteHooks - manual registration only for user data…
abnegate Mar 26, 2026
d55beb5
(fix): auto-register Permissions hook in syncWriteHooks for all DB in…
abnegate Mar 27, 2026
9ad4baf
(revert): remove auto-registration, back to empty syncWriteHooks
abnegate Mar 27, 2026
be29e8e
(fix): sync write hooks from Pool adapter to inner adapter on delegate
abnegate Mar 27, 2026
ec959e3
(fix): sync write hooks from Pool only for DML operations, empty sync…
abnegate Mar 27, 2026
4f96135
(fix): normalize order case in Mongo tryFrom calls, add integer seque…
abnegate Mar 31, 2026
03f2ba5
(chore): trigger CI
abnegate Mar 31, 2026
902826b
(fix): update tests to use enum types and new hook API after refactoring
abnegate Mar 31, 2026
68dd174
(fix): pass ColumnType enum to addFormat in AttributeTests
abnegate Mar 31, 2026
7e3f9bc
(fix): restore Permissions write hook in syncWriteHooks, remove globa…
abnegate Mar 31, 2026
e1a5cea
(fix): register Permissions hook in test setUp for explicit permissio…
abnegate Mar 31, 2026
516ed26
(fix): restore Tenancy write hook registration in syncWriteHooks for …
abnegate Mar 31, 2026
5fb45e5
(fix): use direct sharedTables/tenant check in newBuilder instead of …
abnegate Mar 31, 2026
49f98ee
(fix): address PR review comments - return type safety, read hook ini…
abnegate Mar 31, 2026
076038e
Merge remote-tracking branch 'origin/main' into feat-query-lib
abnegate Mar 31, 2026
0570e1d
(fix): resolve PHPStan errors and regenerate baseline
abnegate Mar 31, 2026
3a4ded6
(refactor): collapse Hook\Relationship interface into Relationships c…
abnegate Mar 31, 2026
26f0c82
(fix): accept PermissionType or string in Input for custom permission…
abnegate Mar 31, 2026
9074dfa
(fix): remove auto-registration of hooks, let callers register explic…
abnegate Mar 31, 2026
843dce3
(fix): propagate write hooks for singular document operations in Pool…
abnegate Mar 31, 2026
146221a
(fix): propagate write hooks unconditionally in Pool, forward to Mirr…
abnegate Mar 31, 2026
2444fe6
(fix): handle millisecond timestamp strings in Mongo castingBefore
abnegate Mar 31, 2026
89e7afc
(fix): resolve Structure validator missing Float, TenantFilter alias …
abnegate Mar 31, 2026
2c3cc09
(fix): skip structure validation for internal metadata document updates
abnegate Mar 31, 2026
a32778b
(fix): include null tenant in MongoDB metadata collection queries
abnegate Mar 31, 2026
87a32dd
(fix): always call castingAfter in createDocument regardless of relat…
abnegate Mar 31, 2026
93b7f4d
(fix): add ColumnType::Float case to Attribute validator
abnegate Mar 31, 2026
84c0c74
(fix): skip decorators during silenced (internal) operations
abnegate Mar 31, 2026
c481610
(fix): convert associative arrays to Documents in relationship hooks
abnegate Mar 31, 2026
49e2dea
(fix): use loose comparison for tenant checks and fix PHPStan return …
abnegate Mar 31, 2026
ed0b48a
(fix): add PHPStan type assertions for Document constructor calls
abnegate Mar 31, 2026
a07ec0e
(fix): restore Document tenant int cast, validate relationship $id, f…
abnegate Mar 31, 2026
999c209
(fix): convert associative arrays to Documents in relationship hooks
abnegate Mar 31, 2026
c9a9c7c
(chore): trigger CI
abnegate Mar 31, 2026
fa461e5
(fix): normalize tenant types at boundary, restore strict comparison
abnegate Apr 1, 2026
cae1e99
(chore): add agent files
abnegate Apr 1, 2026
7140252
(feat): add metadata parameter to createCollection for custom attributes
abnegate Apr 2, 2026
2c49e16
(feat): add externalId attribute to collection metadata schema
abnegate Apr 2, 2026
3fade2e
Merge remote-tracking branch 'origin/main' into feat-query-lib
abnegate Apr 8, 2026
7ab1b83
(chore): resolve merge conflicts with main, keep query-lib adapter ar…
abnegate Apr 23, 2026
f720a2b
(chore): pin claude-pr-owner to v0.2.0
abnegate Apr 23, 2026
af527e1
Revert "(chore): pin claude-pr-owner to v0.2.0"
abnegate Apr 23, 2026
15e6aa7
Merge branch 'main' into feat-query-lib
abnegate Apr 23, 2026
c2b792e
(fix): address review findings — 1 HIGH, 0 MEDIUM
github-actions[bot] Apr 23, 2026
eb7fef3
Merge remote-tracking branch 'origin/main' into feat-query-lib
abnegate Apr 23, 2026
2378ab9
Merge branch 'main' into feat-query-lib
abnegate Apr 23, 2026
b6198a6
Merge remote-tracking branch 'origin/main' into feat-query-lib
abnegate Apr 24, 2026
997093a
(fix): address review findings — 1 HIGH, 0 MEDIUM
github-actions[bot] Apr 24, 2026
ca4e2a7
(fix): CI — define Query::LOGICAL_TYPES/TYPE_ELEM_MATCH, drop Databas…
github-actions[bot] Apr 24, 2026
56e3342
Merge remote-tracking branch 'origin/main' into feat-query-lib
abnegate Apr 24, 2026
6f2fc7f
(fix): address review findings — critical & warning
github-actions[bot] Apr 24, 2026
3f277c1
(fix): CI — emit INSERT IGNORE when skipDuplicates is set in SQL::cre…
github-actions[bot] Apr 24, 2026
78529fe
(fix): CI — propagate skipDuplicates through Database → Adapter → Per…
github-actions[bot] Apr 24, 2026
b44c8cd
(fix): CI — bump utopia-php/query pin to include ColumnType::Serial
github-actions[bot] Apr 24, 2026
e95c1f8
fix: rename Plan to Statement and Blueprint to Table after query lib …
abnegate Apr 29, 2026
8bfe500
fix: drop redundant Postgres TIMESTAMP qualifier and isolate join-agg…
abnegate Apr 29, 2026
2e24174
chore: bump utopia-php/query to 0.2.0
abnegate Apr 29, 2026
d8011fa
Merge branch 'main' into feat-query-lib
abnegate Apr 29, 2026
b3206a6
chore: regenerate composer.lock after merge with main
abnegate Apr 29, 2026
f8bbaae
fix: extend join-test data isolation to remaining DataProvider tests
github-actions[bot] Apr 29, 2026
1656c9c
(fix): CI — replace removed getSupportForHostname() with supports(Cap…
github-actions[bot] Apr 29, 2026
874ae86
refactor: scrub stale Blueprint/Plan terminology after query-lib rename
github-actions[bot] Apr 29, 2026
514e7e5
(fix): CI — replace removed getSupportForHostname() mock in CacheKeyTest
github-actions[bot] Apr 29, 2026
c61d31f
fix: clear PHPStan errors after merge from main
abnegate Apr 29, 2026
936b1df
perf: batch permissions read in afterDocumentBatchUpdate
abnegate Apr 29, 2026
e4e2fef
perf: cache DocumentsValidator per collection
abnegate Apr 29, 2026
800dc79
perf: hoist hot-loop guards and cache spatial / map / timeout state o…
abnegate Apr 29, 2026
e1756b5
refactor: consolidate find/count/sum binding loops into bindStatement…
abnegate Apr 29, 2026
ba8d911
perf: trim find/count/sum redundant work and parse queries once in va…
abnegate Apr 29, 2026
9b60851
fix: purge collection cache on index create/delete and revert timeout…
abnegate Apr 29, 2026
1e64e5a
fix: invalidate spatial cache on rename/updateAttribute paths
github-actions[bot] Apr 29, 2026
c87fb80
fix: route Mirror's getDocumentsValidator through source for cache co…
abnegate Apr 29, 2026
7258011
(fix): CI — purge cached DocumentsValidator on index changes
github-actions[bot] Apr 29, 2026
d3dae3b
fix: address spatial-cache scoping & per-row alloc warnings
github-actions[bot] Apr 29, 2026
e8ba8f4
Merge branch 'main' into feat-query-lib
abnegate Apr 29, 2026
d48f975
perf: stop allocating typed Attribute objects in read hot paths
abnegate Apr 29, 2026
4e96d92
style: fix pint no_whitespace_in_blank_line
abnegate Apr 29, 2026
aee3c47
fix: address review findings — cache coherence & memory parity
github-actions[bot] Apr 29, 2026
8868c66
refactor: tighten internal-attribute caches and extract isRelationshi…
github-actions[bot] Apr 29, 2026
2f2fc2f
fix(memory): exclude rows when notEqual candidates contain NULL
github-actions[bot] Apr 29, 2026
51ebe32
(fix): CI — apply MySQL session timeout eagerly to stop pool leaks
github-actions[bot] Apr 29, 2026
cac5a9a
fix(memory): reconcile adapter signatures with typed Attribute/Index/…
abnegate Apr 29, 2026
1d92a85
fix: null-guard cache key documentId and mongo object filter empty key
abnegate Apr 29, 2026
db03f79
ci: add Memory adapter to test matrix
abnegate Apr 29, 2026
72acd76
fix(memory): declare adapter capabilities and per-process test database
abnegate Apr 29, 2026
ebe9e64
test(memory): skip no-change update permission optimisation test
abnegate Apr 29, 2026
c2bda30
perf(relationships): batch N+1 single-row calls in Relationships hook
abnegate Apr 30, 2026
b663a28
perf(database): cache getInternalAttributes() and use Attribute::isRe…
abnegate Apr 30, 2026
e8cd9da
perf(adapter): cache Adapter::filter() preg_replace results
abnegate Apr 30, 2026
fc1e05e
perf(relationships): pre-index processQueries relationship lookups
abnegate Apr 30, 2026
24710a2
test: structural cache-key tenant assertion in testSetGlobalCollection
abnegate Apr 30, 2026
4ea97af
perf(adapter): cache MariaDB session timeout and add hot-path bypasses
abnegate Apr 30, 2026
b662ef9
perf(adapter): skip MariaDB timeout-set branch entirely when never armed
abnegate Apr 30, 2026
ef6b8b5
perf(adapter): defer per-query clone in SQL find to join-only path
abnegate Apr 30, 2026
775dd33
perf(relationships): fast-exit hooks when collection has no relations…
abnegate Apr 30, 2026
20c9041
perf(database): tighten decode hot path
abnegate Apr 30, 2026
18c0ca4
perf(database): skip casting for already-canonical types
abnegate Apr 30, 2026
13e4edb
perf(database): inline authorization toggle in find to avoid per-find…
abnegate Apr 30, 2026
01b0c6a
perf(database): inline events-silence toggle in find collection lookup
abnegate Apr 30, 2026
c94a5cc
fix(adapter): use per-adapter quote() in fast-path SQL strings
abnegate Apr 30, 2026
1631ae6
perf(document): fast path for the common Assign case in setAttribute
abnegate Apr 30, 2026
af6ee56
perf(collections): inline silent() in getCollection
abnegate Apr 30, 2026
d0367c6
perf(database): inline silent() in getDocument collection lookup
abnegate Apr 30, 2026
39851ac
perf(collections): per-instance memo cache for getCollection
abnegate Apr 30, 2026
7622f59
perf(relationships): drop Attribute::fromDocument allocation in hot l…
abnegate Apr 30, 2026
79f7d1b
perf(database): hoist invariants out of find post-processing loop
abnegate Apr 30, 2026
96855b5
Merge branch 'main' into feat-query-lib
abnegate Apr 30, 2026
89f3136
Merge branch 'main' into feat-query-lib
abnegate Apr 30, 2026
a982139
fix(test): persist updateDocument writes in schemaless mock
abnegate Apr 30, 2026
0be4eea
fix(sqlite): route Search/NotSearch through FTS5 in the Builder pipeline
abnegate Apr 30, 2026
eb08be0
fix(mirror): delegate getCollection to source so metadata stays fresh
abnegate Apr 30, 2026
79e5e80
perf(document): fromRow factory bypasses nested-document detection on…
abnegate Apr 30, 2026
3dd6d45
perf(database): O(1) attribute lookup in convertQuery via prebuilt id…
abnegate Apr 30, 2026
11b444d
perf(structure): hoist validator allocation in createDocuments and me…
abnegate Apr 30, 2026
3d58732
perf(adapter): skip op_search relevance projection when caller suppli…
abnegate Apr 30, 2026
03f44b0
fix: phpstan follow-ups for Document::fromRow and Structure memo
abnegate Apr 30, 2026
0462567
Merge remote-tracking branch 'origin/main' into feat-query-lib
abnegate May 1, 2026
92a30ed
(fix): CI — regenerate composer.lock after merge dropped deps
github-actions[bot] May 1, 2026
9946e3e
refactor: address review feedback on recent push
claude May 1, 2026
4f03607
fix: address review findings — auth parity, MariaDB timeout, relation…
github-actions[bot] May 1, 2026
4f16422
(fix): CI — remove duplicate old-API test methods from merge
github-actions[bot] May 1, 2026
1b783f7
fix(relationships): respect lowered maxQueryValues in chunked fan-out
github-actions[bot] May 1, 2026
3a5ec95
(refactor): rewrite Redis adapter for feat-query-lib typed API
abnegate May 1, 2026
6b3604b
(fix): narrow phpstan types in Redis adapter
abnegate May 1, 2026
df74f66
(fix): drop redundant is_array check in RedisTest scrubKeys
abnegate May 1, 2026
31e9fad
(fix): redis test host fallback and remaining phpstan errors
abnegate May 1, 2026
39aa026
(fix): drop tearDownAfterClass scrub that broke paratest functional mode
abnegate May 1, 2026
37b2642
Merge remote-tracking branch 'origin/main' into feat-query-lib
abnegate Aug 13, 2026
d1cb7ef
(fix): preserve core behavior through typed integration
abnegate Aug 13, 2026
11c824a
(fix): preserve SQL behavior after query migration
abnegate Aug 13, 2026
83ffb00
(fix): preserve non-SQL adapter behavior
abnegate Aug 13, 2026
c93d9c0
(fix): reconcile typed adapter integration
abnegate Aug 13, 2026
2573933
(fix): guard query cache consistency
abnegate Aug 13, 2026
7c81007
(fix): preserve scoped SQL query behavior
abnegate Aug 13, 2026
b490ebd
(fix): normalize bigint attribute handling
abnegate Aug 13, 2026
8d91c44
(fix): harden cache invalidation boundaries
abnegate Aug 13, 2026
6f147ed
(fix): scope SQL execution events
abnegate Aug 13, 2026
97878e9
(fix): preserve exact unsigned bigint arithmetic
abnegate Aug 13, 2026
232aa5e
(test): use current PHPUnit string constraint
abnegate Aug 13, 2026
b572c3a
(fix): order cache invalidation before transaction commit
abnegate Aug 13, 2026
65fbb8b
(fix): hold query cache epochs through commit
abnegate Aug 13, 2026
80569c7
(fix): keep query cache blocked for concurrent writers
abnegate Aug 13, 2026
1d22609
(style): remove README trailing whitespace
abnegate Aug 13, 2026
5b7b7d4
(fix): address current-head review findings
abnegate Aug 13, 2026
0bd5c67
(fix): preserve SQL transform bindings
abnegate Aug 13, 2026
e23f8ae
(fix): preserve operator and cache semantics
abnegate Aug 13, 2026
d67ac97
(fix): cast batch updates before mutation
abnegate Aug 13, 2026
19d5c97
(fix): discover Swoole stubs during analysis
abnegate Aug 13, 2026
1332b36
(fix): preserve postgres query boundaries
abnegate Aug 13, 2026
16b1444
(fix): preserve adapter runtime semantics
abnegate Aug 13, 2026
d9d6573
(fix): type lifecycle event assertions
abnegate Aug 13, 2026
520f1f9
(test): correct adapter capability fixtures
abnegate Aug 13, 2026
96da30b
(fix): synchronize pooled hooks and migration database
abnegate Aug 13, 2026
2b8fa93
(test): isolate mutable document fixtures
abnegate Aug 13, 2026
e593b78
feat: upgrade utopia-php/query to 0.4
abnegate Aug 14, 2026
5145a39
(refactor): extract reserved document and storage name constants
abnegate Aug 14, 2026
3aeed90
(refactor): use Document constants in query validators
abnegate Aug 14, 2026
65c4dbb
(refactor): use reserved-name constants in Database traits
abnegate Aug 14, 2026
f973cdf
(refactor): map SQL reserved columns through Storage
abnegate Aug 14, 2026
f648e6a
(refactor): use Storage constants in MariaDB and MySQL adapters
abnegate Aug 14, 2026
234fdf3
(refactor): use Storage constants in the Postgres adapter
abnegate Aug 14, 2026
679c0d1
(refactor): use Storage constants in the SQLite adapter
abnegate Aug 14, 2026
bb49282
(refactor): use Storage constants in the Mongo adapter
abnegate Aug 14, 2026
433f376
(refactor): use reserved-name constants in Memory and Redis adapters
abnegate Aug 14, 2026
1fe50a2
(refactor): use reserved-name constants in database hooks
abnegate Aug 14, 2026
6749710
(refactor): use Document constants in models and remaining library files
abnegate Aug 14, 2026
9f06b6c
(test): assert reserved columns via Storage constants
abnegate Aug 14, 2026
296054a
(refactor): finish reserved-name wiring after worktree merge
abnegate Aug 14, 2026
7e72535
(refactor): derive leftover bind names and messages from reserved con…
abnegate Aug 14, 2026
a4147de
fix: do not fail writes when cache epoch purge returns false
abnegate Aug 14, 2026
b212752
(feat): add optional adapter Feature interfaces for raw query and schema
abnegate Aug 14, 2026
f49b1e0
(refactor): declare SQL adapter Features and move spatial decode to M…
abnegate Aug 14, 2026
4693009
(refactor): remove empty spatial decode stubs from Mongo
abnegate Aug 14, 2026
f496674
(refactor): advertise Feature\\Relationships on the Memory adapter
abnegate Aug 14, 2026
3330da5
(refactor): mark Redis Feature methods with Override
abnegate Aug 14, 2026
a0df5e2
(refactor): remove optional Feature stubs from Adapter
abnegate Aug 14, 2026
3e78d9a
(refactor): call adapter Features only after instanceof
abnegate Aug 14, 2026
6065284
(refactor): gate Pool Feature delegation and timeout sync
abnegate Aug 14, 2026
5cdcbce
(refactor): drop leftover Feature stubs and getSupportFor helpers
abnegate Aug 14, 2026
660dcab
(refactor): gate leftover relationship Feature calls with instanceof
abnegate Aug 14, 2026
5cde067
(test): align Feature contract tests with adapter instanceof
abnegate Aug 14, 2026
2646e14
(fix): resolve Pool Feature support from the inner adapter
abnegate Aug 14, 2026
bbf5093
fix: serialize having children in Query::toArray
abnegate Aug 14, 2026
178eced
(fix): stop Pool advertising Features the inner adapter lacks
abnegate Aug 14, 2026
fd10d09
(refactor): nest grouped types under their prefix namespace
abnegate Aug 14, 2026
15b951e
fix: serialize having children without treating them as filters
abnegate Aug 14, 2026
a8d99d4
fix: allow json-filter attributes to use array defaults
abnegate Aug 14, 2026
d9b0710
fix: skip default type checks on json-filter attributes
abnegate Aug 14, 2026
f434588
fix: only reject unsupported spatial types in createCollection
abnegate Aug 14, 2026
d671702
Merge remote-tracking branch 'origin/main' into feat-query-lib
abnegate Aug 14, 2026
f399c5c
fix: detect spatial attributes from cached array metadata
abnegate Aug 14, 2026
6dfa4af
chore: keep validators 0.4 until Appwrite can take 0.5
abnegate Aug 14, 2026
e6f6c82
fix: keep document security on find and stop distinct from ordering b…
abnegate Aug 14, 2026
d43d4e8
fix: keep auth on joined finds and omit internals from distinct selects
abnegate Aug 14, 2026
2f92025
fix: wrap spatial writes after schema changes on long-lived adapters
abnegate Aug 14, 2026
f723325
fix: honour Mongo attribute support when checking DefinedAttributes
abnegate Aug 14, 2026
c78f490
fix: only wrap known spatial columns or WKT strings
abnegate Aug 14, 2026
2c8836b
fix: hash long Postgres table names on create and exists
abnegate Aug 14, 2026
9bb988a
(fix): restore document-level find auth without collapsing LEFT JOINs
abnegate Aug 14, 2026
1bfdcd1
(fix): do not skip collection-level find auth on joins
abnegate Aug 14, 2026
a1b6f8d
fix: keep Mongo ObjectId sequences as strings
abnegate Aug 14, 2026
6befdc5
fix: keep createDocument structure error for missing collections
abnegate Aug 14, 2026
0a056df
fix: leave Document::getSequence unchanged
abnegate Aug 14, 2026
b0db34e
fix: keep authorization enabled for joined finds
abnegate Aug 14, 2026
32b20e3
test: cover join aliases, unmatched outer rows, and extra aggregates
abnegate Aug 14, 2026
91c8109
fix: coerce null join ids and honor user join aliases
abnegate Aug 14, 2026
2f168d6
test: assert outer-join unmatched rows and self-join ACL
abnegate Aug 14, 2026
35730d4
fix: keep unmatched outer-join rows under document security
abnegate Aug 14, 2026
4e3d4b3
test: reject unauthorized outer-join rows and keep shared-table unmat…
abnegate Aug 14, 2026
6bfabcc
fix: place right and full-outer join ACL in WHERE
abnegate Aug 14, 2026
7966452
test: hydrate Attribute factories to typed subclasses
abnegate Aug 14, 2026
a0fe460
test: createCollection accepts Collection model
abnegate Aug 14, 2026
0ea408d
test: build Collection fixtures with typed attributes
abnegate Aug 14, 2026
3024e05
test: cover attribute type subclasses and factories
abnegate Aug 14, 2026
d1aca8d
feat: add typed Attribute subclasses and factories
abnegate Aug 14, 2026
890b9c5
feat: accept Collection in createCollection
abnegate Aug 14, 2026
c409d46
fix: satisfy phpstan on attribute subclasses
abnegate Aug 14, 2026
e6a5267
test: assert _metadata schema has no externalId
abnegate Aug 14, 2026
df2b4fa
refactor: drop externalId from _metadata and build schema from Collec…
abnegate Aug 14, 2026
e6dbb7a
test: cover Index type factories
abnegate Aug 15, 2026
9f85a7c
feat: add Index type factories
abnegate Aug 15, 2026
9c4d9a4
fix: keep dotted attribute selects off the join remapper
abnegate Aug 15, 2026
f4c3372
fix: satisfy phpstan after collectionDefinition and join remapper
abnegate Aug 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 7 additions & 3 deletions .github/workflows/codeql-analysis.yml
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
name: "CodeQL"

on: [ pull_request ]
on: [ pull_request, workflow_dispatch ]
jobs:
lint:
name: CodeQL
Expand All@@ -13,8 +13,12 @@ jobs:
fetch-depth: 2

- run: git checkout HEAD^2
if: github.event_name == 'pull_request'

- name: Run CodeQL
run: |
docker run --rm -v $PWD:/app -w /app phpswoole/swoole:5.1.8-php8.3-alpine sh -c \
"composer install --profile --ignore-platform-reqs && composer check"
docker run --rm -v $PWD:/app -w /app php:8.4-cli-alpine sh -c \
"php -r \"copy('https://getcomposer.org/installer', '/tmp/composer-setup.php');\" && \
php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer && \
composer install --profile --ignore-platform-reqs && \
composer check"
6 changes: 4 additions & 2 deletions .github/workflows/linter.yml
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
name: "Linter"

on: [ pull_request ]
on: [ pull_request, workflow_dispatch ]
jobs:
lint:
name: Linter
Expand All@@ -13,8 +13,10 @@ jobs:
fetch-depth: 2

- run: git checkout HEAD^2
if: github.event_name == 'pull_request'

- name: Run Linter
run: |
docker run --rm -v $PWD:/app -w /app phpswoole/swoole:5.1.8-php8.3-alpine sh -c \
"composer install --profile --ignore-platform-reqs && composer lint"
"composer install --profile --ignore-platform-reqs && \
composer lint"
65 changes: 40 additions & 25 deletions .github/workflows/tests.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,9 +6,9 @@ concurrency:

env:
IMAGE: databases-dev
CACHE_KEY: databases-dev-${{ github.event.pull_request.head.sha }}
CACHE_KEY: databases-dev-${{ github.event.pull_request.head.sha || github.sha }}

on: [pull_request]
on: [pull_request, workflow_dispatch]

jobs:
setup:
Expand All@@ -25,6 +25,7 @@ jobs:
uses: docker/build-push-action@1104d471370f9806843c095c1db02b5a90c5f8b6 # v3.3.1
with:
context: .
file: Dockerfile
push: false
tags: ${{ env.IMAGE }}
load: true
Expand DownExpand Up@@ -60,34 +61,48 @@ jobs:
docker compose up -d --wait

- name: Run Unit Tests
run: docker compose exec tests vendor/bin/phpunit /usr/src/code/tests/unit
run: docker compose exec -e XDEBUG_MODE=off tests vendor/bin/paratest --configuration phpunit.xml --functional --processes 4 /usr/src/code/tests/unit

adapter_test:
name: Adapter Tests
name: "Adapter Tests (${{ matrix.adapter }})"
runs-on: ubuntu-latest
needs: setup
strategy:
fail-fast: false
matrix:
adapter:
[
MongoDB,
MariaDB,
MySQL,
Postgres,
SQLite,
Memory,
Mirror,
Pool,
Redis,
SharedTables/MongoDB,
SharedTables/MariaDB,
SharedTables/MySQL,
SharedTables/Postgres,
SharedTables/SQLite,
SharedTables/Redis,
Schemaless/MongoDB,
]
include:
- adapter: MongoDB
profiles: "--profile mongo"
- adapter: MariaDB
profiles: "--profile mariadb"
- adapter: MySQL
profiles: "--profile mysql"
- adapter: Postgres
profiles: "--profile postgres"
- adapter: SQLite
profiles: ""
- adapter: Memory
profiles: ""
- adapter: Mirror
profiles: "--profile mariadb --profile mariadb-mirror --profile redis-mirror"
- adapter: Pool
profiles: "--profile mysql"
- adapter: Redis
profiles: ""
- adapter: SharedTables/MongoDB
profiles: "--profile mongo"
- adapter: SharedTables/MariaDB
profiles: "--profile mariadb"
- adapter: SharedTables/MySQL
profiles: "--profile mysql"
- adapter: SharedTables/Postgres
profiles: "--profile postgres"
- adapter: SharedTables/SQLite
profiles: ""
- adapter: SharedTables/Redis
profiles: ""
- adapter: Schemaless/MongoDB
profiles: "--profile mongo"

steps:
- name: checkout
Expand All@@ -103,7 +118,7 @@ jobs:
- name: Load and Start Services
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose up -d --wait
docker compose ${{ matrix.profiles }} up -d --wait

- name: Run Tests
run: docker compose exec -T tests vendor/bin/phpunit/usr/src/code/tests/e2e/Adapter/${{matrix.adapter}}Test.php --debug
run: docker compose exec -T -e XDEBUG_MODE=off tests vendor/bin/paratest --configuration phpunit.xml --functional --processes 4 /usr/src/code/tests/e2e/Adapter/${{matrix.adapter}}Test.php
1 change: 1 addition & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,3 +12,4 @@ Makefile
.envrc
.vscode
tmp
*.sql
118 changes: 118 additions & 0 deletions AGENTS.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
# Utopia Database

PHP database abstraction library with a unified API across MariaDB 10.5, MySQL 8.0, PostgreSQL 13+, SQLite 3.38+, and MongoDB.

## Commands

| Command | Purpose |
|---------|---------|
| `composer build` | Build Docker containers |
| `composer start` | Start all database containers in background |
| `composer test` | Run tests in Docker (ParaTest, 4 parallel processes) |
| `composer lint` | Check formatting (Pint, PSR-12) |
| `composer format` | Auto-format code |
| `composer check` | Static analysis (PHPStan, max level, 2GB) |
| `composer coverage` | Check test coverage (90% minimum required) |

Run a single test:
```bash
docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml tests/e2e/Adapter/MariaDBTest.php
docker compose exec tests vendor/bin/phpunit --configuration phpunit.xml tests/unit/Validator/SomeTest.php
```

## Stack

- PHP 8.4+, Docker Compose for test databases
- ParaTest (parallel PHPUnit), Pint (PSR-12), PHPStan (max level)
- Test databases: MariaDB 10.11, MySQL 8.0.43, PostgreSQL 16, SQLite, MongoDB 8.0.14
- Redis 8.2.1 for caching tests

## Project layout

- **src/Database/** -- core library (PSR-4 namespace `Utopia\Database\`)
- `Database.php` -- main API class (uses trait composition for organization)
- `Adapter.php` -- base adapter class all engines extend
- `Adapter/` -- engine implementations: MariaDB, MySQL, Postgres, SQLite, Mongo, Pool, ReadWritePool
- `Adapter/SQL.php` -- shared SQL adapter base (MariaDB, MySQL, Postgres, SQLite extend this)
- `Adapter/Feature/` -- capability interfaces for adapter features
- `Document.php` -- JSON document model (extends ArrayObject)
- `Mirror.php` -- database mirroring/replication
- `Query.php` -- query builder extension
- `Attribute.php` -- attribute type definitions
- `Index.php` -- index management
- `Relationship.php` -- relationship definitions
- `Traits/` -- Database.php composition: Async, Attributes, Collections, Databases, Documents, Entities, Indexes, Relationships, Transactions
- `Hook/` -- event hooks and interceptors: Lifecycle, Permissions, Relationships, TenantFilter, Transform, Read, Write, WriteContext, Interceptor, Decorator, PermissionFilter, Mongo/PermissionFilter, Mongo/TenantFilter, Tenancy
- `Event/` -- DomainEvent, DispatcherHook, plus Collection/{Created,Deleted} and Document/{Created,Deleted,Updated}
- `ORM/` -- EntityManager, EntityMapper, EntityMetadata, EntityState, IdentityMap, MetadataFactory, UnitOfWork, ColumnMapping, EmbeddableMapping, RelationshipMapping, plus `Mapping/` (Entity, Column, Id, HasMany, HasOne, BelongsTo, Embedded, Permissions, Tenant, Pre/{Persist,Remove,Update}, Post/{Persist,Remove,Update}, etc.)
- `Schema/` -- Introspector, Diff, Change, ChangeType, DiffResult
- `Validator/` -- input validators (19 top-level + subdirectories)
- `Helpers/` -- ID, Permission, Role utilities
- `Exception/` -- 18 exception types (Authorization, Duplicate, Limit, Query, Timeout, etc.)

- **tests/unit/** -- unit tests for validators, helpers, etc.
- **tests/e2e/Adapter/** -- E2E tests against real databases
- `Base.php` -- abstract test class all adapter tests extend
- `Scopes/` -- test trait mixins (DocumentTests, AttributeTests, CollectionTests, PermissionTests, RelationshipTests, SpatialTests, VectorTests, etc.)
- Each adapter test (MariaDBTest, PostgresTest, etc.) extends Base and gets all scope traits

## Key patterns

**Multi-adapter:** Single `Database` class with engine-specific `Adapter` implementations. SQL adapters share `SQL.php` base; MongoDB has its own.

**Document model:** Documents are ArrayObject subclasses with reserved attributes: `$id`, `$sequence`, `$createdAt`, `$updatedAt`, `$collection`, `$permissions`.

**Hook system:** Pluggable hooks for permissions, relationships, tenancy filtering, and lifecycle events. Hooks registered on the Database instance.

**Custom document types:**
```php
$database->setDocumentType('users', User::class);
$user = $database->getDocument('users', 'id123'); // Returns User instance
```

**Trait composition:** `Database.php` splits its API across 9 traits in `Traits/` for organization. Each trait groups related operations (documents, attributes, indexes, entities, etc.).

**Connection pooling:** `Pool` adapter wraps multiple connections. `ReadWritePool` distributes reads and writes to separate pools.

**Query builder:** Integrates with `utopia-php/query`. Queries grouped by type: filters, selections, aggregations, ordering, pagination.

## Testing patterns

- E2E tests extend `Base.php` which provides setUp/tearDown for real database connections
- Test functionality split into trait mixins in `Scopes/` -- each adapter test includes all relevant traits
- Unit tests in `tests/unit/` for validators and helpers
- Tests check for `ext-swoole` and skip if missing

## Docker services

```bash
composer build && composer start # Start all databases
```

Services (activated via Docker Compose profiles):
- `mariadb` (port 3306), `mysql` (port 3307), `postgres` (port 5432), `mongo` (port 27017)
- `redis` (port 6379) for caching
- Mirror variants for replication tests
- `adminer` (port 8700, debug profile) for database UI

## Load testing

```bash
bin/load --adapter=mariadb # Populate test data
bin/index --adapter=mariadb # Create indexes
bin/query --adapter=mariadb # Run queries
bin/compare # Visualize at localhost:8708
```

## Conventions

- PSR-12 via Pint, PSR-4 autoloading
- One class per file, filename matches class name
- Full type hints on all parameters and returns, readonly properties for immutable data
- Imports: alphabetical, single per statement, grouped by const/class/function
- Constants: UPPER_SNAKE_CASE
- Methods: camelCase with verb prefixes (get*, set*, create*, update*, delete*)

## Cross-repo context

Changes to the Query builder or Adapter interface may break appwrite. Run `composer test` in both repos after adapter changes.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
@AGENTS.md
4 changes: 1 addition & 3 deletions Dockerfile
Original file line numberDiff line numberDiff line change
Expand Up@@ -115,8 +115,6 @@ RUN EXT_DIR=$(php-config --extension-dir) \
RUN echo extension=redis.so >> /usr/local/etc/php/conf.d/redis.ini
RUN echo extension=swoole.so >> /usr/local/etc/php/conf.d/swoole.ini
RUN echo extension=pcov.so >> /usr/local/etc/php/conf.d/pcov.ini
RUN echo extension=xdebug.so >> /usr/local/etc/php/conf.d/xdebug.ini

RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"

RUN echo "opcache.enable_cli=1" >> $PHP_INI_DIR/php.ini
Expand All@@ -131,6 +129,6 @@ COPY ./dev /usr/src/code/dev
RUN if [ "$DEBUG" = "true" ]; then cp /usr/src/code/dev/xdebug.ini /usr/local/etc/php/conf.d/xdebug.ini; fi
RUN if [ "$DEBUG" = "true" ]; then mkdir -p /tmp/xdebug; fi
RUN if [ "$DEBUG" = "false" ]; then rm -rf /usr/src/code/dev; fi
RUN if [ "$DEBUG" = "false" ]; then rm -f $(php-config --extension-dir)/xdebug.so; fi
RUN if [ "$DEBUG" = "false" ]; then rm -f /usr/local/etc/php/conf.d/xdebug.ini; fi

CMD [ "tail", "-f", "/dev/null" ]
32 changes: 16 additions & 16 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -633,22 +633,22 @@ $database->createRelationship(
);

// Relationship onDelete types
Database::RELATION_MUTATE_CASCADE,
Database::RELATION_MUTATE_SET_NULL,
Database::RELATION_MUTATE_RESTRICT,
ForeignKeyAction::Cascade->value,
ForeignKeyAction::SetNull->value,
ForeignKeyAction::Restrict->value,

// Update the relationship with the default reference attributes
$database->updateRelationship(
collection: 'movies',
id: 'users',
onDelete: Database::RELATION_MUTATE_CASCADE
onDelete: ForeignKeyAction::Cascade->value
);

// Update the relationship with custom reference attributes
$database->updateRelationship(
collection: 'movies',
id: 'users',
onDelete: Database::RELATION_MUTATE_CASCADE,
onDelete: ForeignKeyAction::Cascade->value,
newKey: 'movies_id',
newTwoWayKey: 'users_id',
twoWay: true
Expand DownExpand Up@@ -755,25 +755,25 @@ $database->decreaseDocumentAttribute(
// Update the value of an attribute in a document

// Set types
Document::SET_TYPE_ASSIGN, // Assign the new value directly
Document::SET_TYPE_APPEND, // Append the new value to end of the array
Document::SET_TYPE_PREPEND // Prepend the new value to start of the array
SetType::Assign, // Assign the new value directly
SetType::Append, // Append the new value to end of the array
SetType::Prepend // Prepend the new value to start of the array
Note: Using append/prepend with an attribute which is not an array, it will be set to an array containing the new value.

$document->setAttribute(key: 'name', 'Chris Smoove')
->setAttribute(key: 'age', 33, Document::SET_TYPE_ASSIGN);
->setAttribute(key: 'age', 33, SetType::Assign);

$database->updateDocument(
collection: 'users',
id: $document->getId(),
collection: 'users',
id: $document->getId(),
document: $document
);
);

// Update the permissions of a document
$document->setAttribute('$permissions', Permission::read(Role::any()), Document::SET_TYPE_APPEND)
->setAttribute('$permissions', Permission::create(Role::any()), Document::SET_TYPE_APPEND)
->setAttribute('$permissions', Permission::update(Role::any()), Document::SET_TYPE_APPEND)
->setAttribute('$permissions', Permission::delete(Role::any()), Document::SET_TYPE_APPEND)
$document->setAttribute('$permissions', Permission::read(Role::any()), SetType::Append)
->setAttribute('$permissions', Permission::create(Role::any()), SetType::Append)
->setAttribute('$permissions', Permission::update(Role::any()), SetType::Append)
->setAttribute('$permissions', Permission::delete(Role::any()), SetType::Append)

$database->updateDocument(
collection: 'users',
Expand Down
Loading
Loading