Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
1392 commits
Select commit Hold shift + click to select a range
95c08f6
test: check vector columns through createAttribute in AddTableColumnTest
abnegate Sep 30, 2026
faf3b20
test: reach the documents validator through a test subclass
abnegate Sep 30, 2026
353f95d
test: reach the documents validator through a test subclass in the gr…
abnegate Sep 30, 2026
3a5c6bf
test: check permission row casing through adapter updates
abnegate Sep 30, 2026
9f2db88
test: read an object sequence through getDocument in MongoIdentifierTest
abnegate Sep 30, 2026
69b656a
test: derive hashed postgres table names from adapter calls
abnegate Sep 30, 2026
2e57b4d
test: check postgres spatial writes through createDocument
abnegate Sep 30, 2026
6758da7
test: check the metadata permission subquery through find
abnegate Sep 30, 2026
6154dec
test: spell the raw table name from public getters in QueryBuilderTen…
abnegate Sep 30, 2026
dc4f495
test: bind upsert operators through the database in SQLOperatorBindin…
abnegate Sep 30, 2026
03c31cd
test: check the read timeout scope through find and createDocument
abnegate Sep 30, 2026
3a4d835
test: read the transaction state publicly in TransactionRetryTest
abnegate Sep 30, 2026
1a1cf57
test: swap the native connection through a test subclass in PDOTest
abnegate Sep 30, 2026
5180efd
test: hydrate a relationship through getDocument in RelationshipModel…
abnegate Sep 30, 2026
dd57767
test: build the mongo adapter through its constructor in BigIntegerTest
abnegate Sep 30, 2026
33c663a
test(e2e): reach the pooled connection through getDriver in PoolTest
abnegate Sep 30, 2026
9ade916
test: drop the check that mirrors swoole private message list
abnegate Sep 30, 2026
c2c606d
test: drop permission hook cases that need an input the database rejects
abnegate Sep 30, 2026
25014de
refactor(permissions): drop the unreachable case-insensitive lookup
abnegate Sep 30, 2026
b38b4d3
test: check a failed redis rollback through later transactions
abnegate Sep 30, 2026
0b3f25e
test(e2e): address the stored redis document by its documented key
abnegate Sep 30, 2026
7cd916e
test(e2e): open the killing connection by cloning the driver
abnegate Sep 30, 2026
deab26f
test: type the engine error provider by adapter class
abnegate Sep 30, 2026
5c1e7c4
test: narrow the captured insert statement to a string
abnegate Sep 30, 2026
57c277a
test: read the insert statement past the ones already captured
abnegate Sep 30, 2026
21fea61
test(e2e): drop the driver type assertion the guard already makes
abnegate Sep 30, 2026
aa249c1
test(e2e): let the server end the idle session instead of a second one
abnegate Sep 30, 2026
1e5eea0
test(e2e): idle three times the lowered wait_timeout and restore it
abnegate Sep 30, 2026
b476f82
docs(agents): drop the composer coverage command
abnegate Sep 30, 2026
9062ca5
docs(upgrade): state what each engine does without the permission and…
abnegate Sep 30, 2026
c352ba8
docs(upgrade): warn that a hook name is unique per database
abnegate Sep 30, 2026
f6a5acd
docs(upgrade): describe the cache layout and rolling-upgrade stalenes…
abnegate Sep 30, 2026
f772139
docs: list the adapter-level transaction as a known limitation
abnegate Sep 30, 2026
d0e63a9
docs(upgrade): apply the upgrade deltas of the merged fix branches
abnegate Sep 30, 2026
aefc6eb
docs(changelog): apply the changelog deltas of the merged fix branches
abnegate Sep 30, 2026
7f24538
docs(readme): say which engine checks which permission store
abnegate Sep 30, 2026
4aece28
docs(adapters): describe capabilities and feature interfaces in the a…
abnegate Sep 30, 2026
f4063ea
docs(contributing): point the test and load commands at the current l…
abnegate Sep 30, 2026
8787db8
docs(agents): match the project layout to src
abnegate Sep 30, 2026
7214545
docs(spec): list the adapter methods with their 8.0 signatures
abnegate Sep 30, 2026
bbfa564
docs(changelog): list the schema rollback fixes of j-db-fix-46
abnegate Sep 30, 2026
4d9ae00
fix(attributes): let a php error escape the createattributes rollback
abnegate Sep 30, 2026
de5795b
fix(documents): accept the whole-number bounds operator limits accept
abnegate Sep 30, 2026
dbeba91
fix(adapters): keep the create error when createcollection's cleanup …
abnegate Sep 30, 2026
444a094
fix(state): keep a setter in a child coroutine inside its inherited s…
abnegate Sep 30, 2026
bee0dd4
fix(relationships): track nested writes and cascades per coroutine
abnegate Sep 30, 2026
fa2b864
fix(transactions): retry an outermost transaction the engine rolled b…
abnegate Sep 30, 2026
594d56c
feat(sqlite): report a busy database as a lock conflict
abnegate Sep 30, 2026
8b8975a
fix(indexes): report a rename only when the schema has the index afte…
abnegate Sep 30, 2026
fef8a64
fix(pool): pin a transaction's connection per coroutine
abnegate Sep 30, 2026
2181007
refactor(relationships): type the per-coroutine stacks as plain lists
abnegate Sep 30, 2026
5c9ba02
fix(mongo): keep mongodb's index rename as it was
abnegate Sep 30, 2026
8d327c9
chore(merge): merge j-db-fix-56 (transaction retry) into feat-query-lib
abnegate Sep 30, 2026
b552cda
chore(merge): merge j-db-fix-55 (schema review) into feat-query-lib
abnegate Sep 30, 2026
c04793f
chore(merge): merge j-db-fix-54 (coroutine state) into feat-query-lib
abnegate Sep 30, 2026
6316a1d
fix(state): type the Swoole coroutine ids j-db-fix-54 reads directly
abnegate Sep 30, 2026
8cc1836
test(transactions): let run() wait for the deadlocked writers
abnegate Sep 30, 2026
69f054c
test(indexes): type 55's rename-index helpers for PHPStan level max
abnegate Sep 30, 2026
50e46a2
perf(cache): retire cached finds by epoch instead of deleting the has…
abnegate Sep 30, 2026
566c98f
fix(query): refuse an unpageable joined read before cursor() or itera…
abnegate Sep 30, 2026
cfbb78a
fix(mongo): report as created only the documents skipDuplicates upser…
abnegate Sep 30, 2026
b2e5e2a
perf(filters): read the filter toggles once per decoded document
abnegate Sep 30, 2026
736a6bd
perf(redis): check an upsert batch's unique indexes with one read of …
abnegate Sep 30, 2026
9c8ea95
refactor(query): resolve bare joined attribute names through one reso…
abnegate Sep 30, 2026
00cf9e1
perf(hooks): find a cascade's surviving peers only when a hook handle…
abnegate Sep 30, 2026
15271c0
style: name values and state after what they hold, and trim narrating…
abnegate Sep 30, 2026
ef99690
refactor(cache): map a query to its slot with integer arithmetic only
abnegate Sep 30, 2026
d0ff932
chore(merge): merge j-db-fix-57 (cache and review minors) into feat-q…
abnegate Sep 30, 2026
81a243a
test(unit): type fix 57's new unit tests for PHPStan level max
abnegate Sep 30, 2026
7131e85
perf(collections): build the metadata collection's model once per pro…
abnegate Sep 30, 2026
2273de8
perf(documents): skip query validation for a read by id without queries
abnegate Sep 30, 2026
884068a
perf(document): copy only non-empty arrays and nested documents on clone
abnegate Sep 30, 2026
a10c18f
perf(collections): reuse a cached definition's model while its copy i…
abnegate Sep 30, 2026
17ad1d6
perf(documents): fingerprint a collection for the validator cache wit…
abnegate Sep 30, 2026
868385d
test(pool): expect no checkout for a warm read by id without queries
abnegate Sep 30, 2026
6ef9ac1
perf(sql): read a document for update without the query builder
abnegate Sep 30, 2026
12b378c
fix(adapters): read the tenant through a method instead of the proper…
abnegate Sep 30, 2026
52c2a35
perf(sql): read a shared table's document without the query builder
abnegate Sep 30, 2026
43d0bd1
docs(adapters): say which PHP releases the tenant read works around
abnegate Sep 30, 2026
aca0f84
chore(merge): merge feat-query-lib (fix 59, tenant read through a met…
abnegate Sep 30, 2026
558fd9a
fix(sql): read a shared-table read's tenant through currentTenant()
abnegate Sep 30, 2026
f70184f
perf(transactions): read an unchanged collection definition once per …
abnegate Oct 1, 2026
4e21fbb
test(sqlite): pin the untyped spatial column and its raw wkt bind
abnegate Oct 1, 2026
044036a
test(mirror): cover updateCollection on a mirror without a destination
abnegate Oct 1, 2026
2c609ba
test(mirror): cover schema writes and upserts on a mirror without a d…
abnegate Oct 1, 2026
8dc1d42
perf(joins): break ties on the joined id only where the rows show the…
abnegate Oct 1, 2026
2c3bd1d
test(mirror): cover attribute and index deletes and updates without a…
abnegate Oct 1, 2026
626e5d6
perf(documents): reuse the sum validator of a declared attribute per …
abnegate Oct 1, 2026
2a83ba0
test(attributes): cover an attribute the adapter reports as not created
abnegate Oct 1, 2026
61d39f6
test(attributes): cover a refused batch and a mismatch in the one-by-…
abnegate Oct 1, 2026
cdd4a1a
test(attributes): cover a valid vector attribute update
abnegate Oct 1, 2026
9a7a508
test(collections): keep the metadata failure when the dropped table c…
abnegate Oct 1, 2026
0862d7d
test(documents): cover a NaN counter change, a join on getDocument an…
abnegate Oct 1, 2026
1c69f4f
test(indexes): fail a rename the adapter does not redo after reversin…
abnegate Oct 1, 2026
40f6628
test(relationships): cover the rollback steps of a failed relationshi…
abnegate Oct 1, 2026
7413275
test(validator): reject exists and notExists queries without attributes
abnegate Oct 1, 2026
a90fa32
test(adapters): fail a key the pattern engine cannot filter
abnegate Oct 1, 2026
3495da1
test(memory): undo an increment of an absent value and keep a value d…
abnegate Oct 1, 2026
7b68ec2
test(mysql): pin NO_SEMIJOIN on outer joins over the joined collectio…
abnegate Oct 1, 2026
2d12185
test(memory): page a cursor without an order by sequence
abnegate Oct 1, 2026
af9ac9d
test(mongo): cover the unique index wait and a sequence page without …
abnegate Oct 1, 2026
b4b5cc0
test(mongo): keep a find's results when its open cursor cannot be killed
abnegate Oct 1, 2026
02dfa46
test(mongo): map index failures while creating a collection
abnegate Oct 1, 2026
cf7c3b2
test(redis): page a cursor without an order by sequence
abnegate Oct 1, 2026
8df50c6
test(redis): rename nothing for a many-to-many key without stored def…
abnegate Oct 1, 2026
0209bc8
test(sqlite): refuse a fulltext index when the tenant cannot be quoted
abnegate Oct 1, 2026
9c306c3
test(mariadb): refuse an index on a collection without a definition
abnegate Oct 1, 2026
1a48a8f
test(sql): cover empty batches, a late operator and a unique violatio…
abnegate Oct 1, 2026
3afd364
test(sql): set now on a document an upsert creates
abnegate Oct 1, 2026
52a9ab5
test(sql): page a joined vector search past a null order value
abnegate Oct 1, 2026
f79b948
test(sql): refuse a connection that is not a pdo on first use
abnegate Oct 1, 2026
83e82f8
test(documents): refuse a join without a collection when validation i…
abnegate Oct 1, 2026
395f610
test(relationships): drop a relationship change nested past the maxim…
abnegate Oct 1, 2026
e3332ab
test(memory): type the ids helper of the unordered cursor test
abnegate Oct 1, 2026
9526a5d
chore(merge): merge j-db-fix-58 (per-call overhead) into feat-query-lib
abnegate Oct 1, 2026
202521a
test(adapters): run the pattern-engine failure test in its own process
abnegate Oct 1, 2026
87f5b15
chore(merge): merge feat-query-lib into the j-db-fix-58 integration
abnegate Oct 1, 2026
7b5e6b6
style(adapters): drop the unused Document import of LockedDocumentRea…
abnegate Oct 1, 2026
3a936d7
test(unit): type fix 58's cache and pool tests for PHPStan level max
abnegate Oct 1, 2026
6f9d37a
perf(relationships): prepare a create's new related documents instead…
abnegate Oct 1, 2026
1dd60b3
perf(relationships): skip relating a prepared document by id where th…
abnegate Oct 1, 2026
5a47b8f
perf(joins): return before the joined tie keys on reads without joins
abnegate Oct 1, 2026
6370714
perf(relationships): prepare an update's new related documents like a…
abnegate Oct 1, 2026
8cd7aa8
chore(merge): merge j-db-fix-60 (join order) into feat-query-lib
abnegate Oct 1, 2026
979711b
perf(documents): skip validating and converting an empty query list i…
abnegate Oct 1, 2026
67b268f
perf(sql): write the statement of count() and sum() without queries i…
abnegate Oct 1, 2026
664ba4d
refactor(adapters): remove getTenantQuery, getSpatialTypeFromWKT and …
abnegate Oct 1, 2026
947e89a
refactor(sql): drop operator bindings only SQLite's upsert could reach
abnegate Oct 1, 2026
c1949f7
refactor(attributes): drop the required-default refusal updateAttribu…
abnegate Oct 1, 2026
f2e743c
refactor(sql): drop a selection check every joinless builder read passes
abnegate Oct 1, 2026
97bafe5
refactor(adapters): remove protected adapter methods nothing calls
abnegate Oct 1, 2026
7a4bb39
perf(joins): sort only the joined rows of the main rows a page can reach
abnegate Oct 1, 2026
33603a5
test(joins): page a left-joined read by offset and cursor on every lane
abnegate Oct 1, 2026
242e7f4
chore(merge): merge feat-query-lib into j-db-fix-63
abnegate Oct 1, 2026
52671b1
chore(merge): merge feat-query-lib into j-db-fix-62
abnegate Oct 1, 2026
dc9cb59
perf(joins): join the bounded page from a derived table of its main rows
abnegate Oct 1, 2026
c90065f
perf(documents): accept a declared single-number sum attribute withou…
abnegate Oct 1, 2026
891625d
perf(queries): ask for objects before defined attributes when convert…
abnegate Oct 1, 2026
a0f8a8e
test(relationships): narrow the equivalence suite's observations for …
abnegate Oct 1, 2026
7ddf260
chore(merge): merge feat-query-lib into j-db-fix-65
abnegate Oct 1, 2026
5e7ae9d
refactor: remove public methods that no consumer calls
abnegate Oct 1, 2026
746f90d
chore(merge): merge feat-query-lib into j-db-fix-64b
abnegate Oct 1, 2026
9d65f26
perf(pool): remember the defined-attributes answer of each schema mod…
abnegate Oct 1, 2026
f88af6d
perf(attributes): tell a relationship attribute by its stored type
abnegate Oct 1, 2026
3b95e6f
perf(queries): check a plain query list with only the validators it r…
abnegate Oct 1, 2026
0c1b77f
chore(merge): merge feat-query-lib into j-db-fix-65
abnegate Oct 1, 2026
0251b01
fix(joins): type the bounded page's filters as the builder's queries
abnegate Oct 1, 2026
1a55000
perf(sql): write the statement of a filtered count() and sum() instea…
abnegate Oct 1, 2026
634d8fb
perf(collections): hand out cached definitions with their permissions…
abnegate Oct 1, 2026
ea3173f
perf(sql): write the metadata comments once until the metadata changes
abnegate Oct 1, 2026
e0bdc0f
perf(joins): bound inner and filtered one-to-many joins to the page's…
abnegate Oct 1, 2026
78e1fc0
test(joins): page inner and filtered joined reads by offset and curso…
abnegate Oct 1, 2026
d770e8d
refactor(joins): treat an empty or as failing a missing joined row
abnegate Oct 2, 2026
e7c9c2e
test(joins): pin the joined tenant, nested groups and searches of the…
abnegate Oct 2, 2026
b441bd8
chore(merge): merge feat-query-lib into j-db-fix-66
abnegate Oct 2, 2026
a6ac6e4
test(cache): keep the validator purge race on the cached validator path
abnegate Oct 2, 2026
c117911
feat(pdo): carry the lost connection on statements refused after it
abnegate Oct 2, 2026
e181e02
fix(transactions): retry only failures that can succeed on another at…
abnegate Oct 2, 2026
c1849b3
test(transactions): fail simulated commits the way the adapters do
abnegate Oct 2, 2026
e6ee45f
fix(schema): stop withRetries() repeating what a transaction already …
abnegate Oct 2, 2026
a0c9b06
fix(transactions): let only the outermost withTransaction() retry
abnegate Oct 2, 2026
4f9d4f9
fix(mongo): report no transaction retries on a standalone server
abnegate Oct 2, 2026
75fe95d
perf(joins): bound matched joins on mysql only
abnegate Oct 2, 2026
c0c767e
perf(joins): bound one matched join at most on mysql
abnegate Oct 2, 2026
2cc323d
perf(joins): keep the whole join for inner and joined-filtered reads
abnegate Oct 2, 2026
76085e5
chore(merge): merge feat-query-lib into j-db-fix-67
abnegate Oct 2, 2026
d96e33d
fix(joins): map dotted main attribute names in the bounded page
abnegate Oct 2, 2026
ae997b0
fix(documents): keep magic and hooked property reads off the hot paths
abnegate Oct 2, 2026
9ef7fc5
test(validators): guard per-document attribute reads against magic pr…
abnegate Oct 2, 2026
b96f370
perf(mongo): cast a page of read documents in one pooled call
abnegate Oct 2, 2026
4519cf6
perf(mongo): walk a found record without mapping its scalar values
abnegate Oct 2, 2026
c74f5d1
docs(joins): document join ordering bounds, MySQL statistics window a…
abnegate Oct 3, 2026
21adee8
fix(relationships): file associative related documents correctly and …
abnegate Oct 5, 2026
037c958
fix(transactions): key the definition memo by filter state
abnegate Oct 5, 2026
5f51d55
docs(documents): mark the prepared create methods internal
abnegate Oct 5, 2026
da413b2
docs(upgrade): explain coroutine scopes across finished starters
abnegate Oct 5, 2026
66b1e51
fix(relationships): keep set-null peers only for a delete someone hears
abnegate Oct 5, 2026
b82a1f5
test(cache): bound document cache round trips by 7.3.12's and check i…
abnegate Oct 5, 2026
7152129
test(cache): check query cache invalidation through reads instead of …
abnegate Oct 5, 2026
8eff8cc
test(sql): check aggregation and search reads through their results o…
abnegate Oct 5, 2026
db210cc
docs(changelog): state when a set-null delete keeps the documents it …
abnegate Oct 5, 2026
bcfbb5c
test(transactions): type the decoded definition before reading it
abnegate Oct 5, 2026
ca241ce
test(relationships): mark the watched bulk update overrides
abnegate Oct 5, 2026
8fdc79a
fix(transactions): leave a retryable conflict in a savepoint to the o…
abnegate Oct 5, 2026
dbae06f
docs(upgrade): list the relationship toggle among the scoped setters
abnegate Oct 5, 2026
ec14f5f
test(cache): declare the ttl parameter on the query cache test's save…
abnegate Oct 5, 2026
0156ffa
test(cache): narrow the invalidation cache double's save return type
abnegate Oct 5, 2026
7000c40
test(cache): check database query cache invalidation through reads
abnegate Oct 5, 2026
5d8595e
test(documents): share one uncached twin across the document cache tests
abnegate Oct 5, 2026
94ec1c4
fix(relationships): keep related documents for a create retried after…
abnegate Oct 5, 2026
af083ef
docs(relationships): mark restore() internal
abnegate Oct 5, 2026
585fb60
fix(types): return a list from the parent conflict test's stored rows
abnegate Oct 5, 2026
f27607f
fix(relationships): restore a failed create's related documents for a…
abnegate Oct 5, 2026
88de405
chore(deps): update utopia-php/mongo to drop the vulnerable mongodb/m…
abnegate Oct 5, 2026
d00ac77
fix(mongo): filter database and collection names in delete and exists
abnegate Oct 5, 2026
d21e839
fix(types): type the mongo name filter test's client double
abnegate Oct 5, 2026
7cf7f7a
test(cache): check query cache expiry and invalidation without counti…
abnegate Oct 5, 2026
3eb5649
test(pool): check the delegated builder by the rows it reads
abnegate Oct 5, 2026
50407aa
test(postgres): leave the power result to the operator e2e tests
abnegate Oct 5, 2026
420ba66
test(postgres): leave vector column creation to the vector e2e tests
abnegate Oct 5, 2026
92441b7
test(sql): check query comments without pinning the statements they lead
abnegate Oct 5, 2026
52acc3f
test(redis): locate the stored document by its payload, not its key l…
abnegate Oct 5, 2026
54022b2
fix(types): type the malformed query cache payloads
abnegate Oct 5, 2026
ef6a717
fix(state): keep a write local in a coroutine whose starter has returned
abnegate Oct 5, 2026
5474661
docs(upgrade): describe writes in coroutines whose starter has returned
abnegate Oct 5, 2026
b6cc16b
fix(mongo): retry only the commit when its result is unknown
abnegate Oct 5, 2026
1f0d7df
docs(upgrade): describe commits with an unknown result
abnegate Oct 5, 2026
4d07ecf
chore: merge mongo commit-only retry into j-db-76
abnegate Oct 5, 2026
8faa0d7
fix(state): register the local write's cleanup before counting it
abnegate Oct 5, 2026
ba7792b
test(state): cover a cut-off role write under skip and a clone taken …
abnegate Oct 5, 2026
3ebf95e
docs(upgrade): say which coroutines are cut off and what that costs
abnegate Oct 5, 2026
3025403
docs(state): describe cut-off coroutines by the ancestor that returned
abnegate Oct 5, 2026
ce89f5d
fix(mongo): rerun on an aborted first commit and keep Unconfirmed on …
abnegate Oct 5, 2026
4b70178
fix(mongo): keep schema changes whose definition commit is unconfirmed
abnegate Oct 5, 2026
5bf5c63
docs(upgrade): describe aborted and unconfirmed mongodb commits
abnegate Oct 5, 2026
1a2c42c
chore: merge state cut-off coroutine fixes into j-db-76
abnegate Oct 5, 2026
72598b8
test(mongo): select the unconfirmed schema write by its content
abnegate Oct 5, 2026
85934b6
docs(upgrade): describe how deletes handle an unconfirmed definition
abnegate Oct 5, 2026
b84ef2b
docs(upgrade): warn against setter pairs in cut-off coroutines
abnegate Oct 5, 2026
e8cb1af
test(mongo): keep phpstan from narrowing the unconfirmed write flag
abnegate Oct 5, 2026
72983c4
fix(transactions): announce document purges when a commit is unconfirmed
abnegate Oct 5, 2026
ab37d11
style(mongo): drop narrating comments from the transaction methods
abnegate Oct 5, 2026
3d14b3c
docs(upgrade): note postgres join planning and mariadb shared aggrega…
abnegate Oct 5, 2026
eaf6369
fix(transactions): keep only the outermost unconfirmed attempt's purg…
abnegate Oct 5, 2026
5553054
style(mongo): restore the why-comments on the transaction bypass and …
abnegate Oct 5, 2026
c34d6f5
docs(changelog): order the unconfirmed commit's purge events before t…
abnegate Oct 5, 2026
2fd7060
style(test): fix statement indentation in the purge event test
abnegate Oct 5, 2026
f6089f9
test(transactions): cover a retried attempt that throws another commi…
abnegate Oct 5, 2026
2627d5a
fix(transactions): apply an unconfirmed commit to its own transaction…
abnegate Oct 5, 2026
84a40ba
docs(upgrade): mark the join and shared aggregation timings as develo…
abnegate Oct 5, 2026
c8c9a26
fix(adapters): read schema models through typed getters in validators…
abnegate Oct 5, 2026
f3f32a8
fix(schema): read schema models through typed getters instead of magi…
abnegate Oct 5, 2026
e319f8b
fix(adapters): read schema models through typed getters in sqlite, mo…
abnegate Oct 5, 2026
a228c11
test(phpstan): flag magic and hooked property fetches in src
abnegate Oct 5, 2026
f4ad5de
test(schema): guard schema operations against magic property reads
abnegate Oct 5, 2026
1648b3d
Merge branch 'ws-j-db-77-a1' into qlt-fix/j-db-77
abnegate Oct 5, 2026
9f17baa
Merge branch 'ws-j-db-77-a2' into qlt-fix/j-db-77
abnegate Oct 5, 2026
f90daab
Merge branch 'ws-j-db-77-a3' into qlt-fix/j-db-77
abnegate Oct 5, 2026
faafd08
Merge branch 'ws-j-db-77-tests' into qlt-fix/j-db-77
abnegate Oct 5, 2026
49eb04b
Merge branch 'ws-j-db-77-rule' into qlt-fix/j-db-77
abnegate Oct 5, 2026
aecdb66
test(phpstan): suggest behaviour-preserving replacements for magic pr…
abnegate Oct 5, 2026
510719e
test(schema): record attribute magic access through one recorder
abnegate Oct 5, 2026
5d44353
test(phpstan): make rule fixtures discoverable without the composer c…
abnegate Oct 5, 2026
68fddb5
refactor(schema): hoist loop-invariant getters and spell out names in…
abnegate Oct 5, 2026
0acfb5e
fix(relationships): rename the junction column when a one-way many-to…
abnegate Oct 5, 2026
b5db0ec
Merge branch 'ws-j-db-77-f3' into qlt-fix/j-db-77
abnegate Oct 5, 2026
ceed07e
Merge branch 'ws-j-db-77-f1' into qlt-fix/j-db-77
abnegate Oct 5, 2026
3189c55
Merge branch 'ws-j-db-77-f2' into qlt-fix/j-db-77
abnegate Oct 5, 2026
4be8ba0
style(relationships): drop the unused database import
abnegate Oct 5, 2026
d2c85cb
style(adapters): drop unused imports
abnegate Oct 5, 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
  •  
  •  
  •  
11 changes: 8 additions & 3 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff 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,13 @@ 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"
container=$(docker create composer:2.8)
docker cp "$container":/usr/bin/composer /tmp/composer
docker rm "$container"
docker run --rm -v $PWD:/app -v /tmp/composer:/usr/local/bin/composer:ro -w /app php:8.5.8-cli-alpine sh -c \
"composer install --profile --ignore-platform-req='ext-*' && \
composer check"
11 changes: 8 additions & 3 deletions .github/workflows/linter.yml
Original file line number Diff line number Diff 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,13 @@ 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"
container=$(docker create composer:2.8)
docker cp "$container":/usr/bin/composer /tmp/composer
docker rm "$container"
docker run --rm -v $PWD:/app -v /tmp/composer:/usr/local/bin/composer:ro -w /app php:8.5.8-cli-alpine sh -c \
"composer install --profile --ignore-platform-reqs && \
composer lint"
139 changes: 114 additions & 25 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ 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 }}
CACHE_5_KEY: databases-dev-cache-5-${{ github.event.pull_request.head.sha || github.sha }}

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

jobs:
setup:
Expand All @@ -25,6 +26,7 @@ jobs:
uses: docker/build-push-action@1104d471370f9806843c095c1db02b5a90c5f8b6 # v3.3.1
with:
context: .
file: Dockerfile
push: false
tags: ${{ env.IMAGE }}
load: true
Expand Down Expand Up @@ -60,34 +62,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 +119,80 @@ 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 -e XDEBUG_MODE=off tests vendor/bin/paratest --configuration phpunit.xml --functional --processes 4 /usr/src/code/tests/e2e/Adapter/${{matrix.adapter}}Test.php

setup_cache_5:
name: Setup & Build Docker Image (utopia-php/cache 5.x)
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0

- name: Build Docker Image
uses: docker/build-push-action@1104d471370f9806843c095c1db02b5a90c5f8b6 # v3.3.1
with:
context: .
file: Dockerfile
build-args: |
UTOPIA_CACHE_VERSION=^5.1
push: false
tags: ${{ env.IMAGE }}
load: true
cache-from: |
type=gha
type=gha,scope=cache-5
cache-to: type=gha,mode=max,scope=cache-5
outputs: type=docker,dest=/tmp/${{ env.IMAGE }}.tar

- name: Cache Docker Image
uses: actions/cache@6f8efc29b200d32929f49075959781ed54ec270c # v3.5.0
with:
key: ${{ env.CACHE_5_KEY }}
path: /tmp/${{ env.IMAGE }}.tar

cache_5_test:
name: "Cache 5.x Tests (${{ matrix.suite }})"
runs-on: ubuntu-latest
needs: setup_cache_5
strategy:
fail-fast: false
matrix:
include:
- suite: Unit
profiles: ""
path: /usr/src/code/tests/unit
- suite: MariaDB
profiles: "--profile mariadb"
path: /usr/src/code/tests/e2e/Adapter/MariaDBTest.php

steps:
- name: checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Load Cache
uses: actions/cache@6f8efc29b200d32929f49075959781ed54ec270c # v3.5.0
with:
key: ${{ env.CACHE_5_KEY }}
path: /tmp/${{ env.IMAGE }}.tar
fail-on-cache-miss: true

- name: Load and Start Services
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose ${{ matrix.profiles }} up -d --wait

- name: Show utopia-php/cache Version
run: |
docker compose exec -T tests php -r '
require "vendor/autoload.php";
echo "utopia-php/cache ", \Composer\InstalledVersions::getPrettyVersion("utopia-php/cache"), PHP_EOL;
'

- 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 ${{ matrix.path }}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Makefile
.envrc
.vscode
tmp
*.sql
117 changes: 117 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# 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) |

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.5+, 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, Memory, Redis, 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: Attributes, Collections, Databases, Documents, Indexes, Relationships, Transactions
- `Hook/` -- event hooks and interceptors: Lifecycle, Named, Decorator, Transform, Read, Write, WriteContext, Interceptor, Permissions, Relationships, Tenancy, PermissionFilter, TenantFilter, the join and raw-builder filters, Mongo/PermissionFilter, Mongo/TenantFilter
- `Event/` -- Domain, DispatcherHook, plus Collection/{Created,Deleted}, Document/{Created,Deleted,Updated} and Documents/{Created,Deleted,Updated}
- `Cache/` -- `find()` query cache (QueryCache, Invalidator) and cache bookkeeping
- `State/` -- per-coroutine state (Value) and Snapshot
- `Validator/` -- input validators (21 top-level + Authorization/, Queries/, Query/)
- `Helpers/` -- ID, Permission, Role utilities
- `Exception/` -- 20 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 7 traits in `Traits/` for organization. Each trait groups related operations (documents, attributes, indexes, 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` (host port 8703), `mysql` (8706), `postgres` (8701), `mongo` (9706)
- `redis` (8708) 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.
Loading
Loading