Skip to content

fix: bring back the 8.0.0 rules that 8.0.1 reverted by mistake (8.0.2) - #1004

Merged
abnegate merged 10 commits into
mainfrom
fix/postgres-phrase-search
Oct 12, 2026
Merged

abnegate merged 10 commits into
mainfrom
fix/postgres-phrase-search

Conversation

@abnegate

@abnegate abnegate commented Oct 12, 2026 •

Copy link
Copy Markdown
Member

Why

8.0.1 (#1002) restored 7.4.1 behaviour wherever 8.0.0 had changed it. Six of those changes were deliberate 8.0.0 changes the maintainer had already decided on, not regressions. 8.0.2 brings all six back. Everything else in 8.0.1 stays: the link rule, authorizeCreate()/authorizeUpdate(), float binding, relationship selects, query messages, containsAll(), orderRandom() with a cursor, and the MongoDB write-conflict retries.

# 8.0.2 (as 8.0.0) 8.0.1 (reverted) Decision
1 PostgreSQL exact search "foo bar" matches the adjacent phrase, as on every other engine every word, any order 2026-10-06
2 increase/decreaseDocumentAttribute() refuse a fractional change, max or min on an integer attribute with Exception\Type passed to the engine (rounded, truncated, or an error on PostgreSQL) 2026-10-01
3 A counter change ≤ 0 throws Exception\Type, which catch (DatabaseException) catches \InvalidArgumentException BUG-11, 2026-10-07
4 Numeric operators refuse fractional limits on integer attributes; numeric text such as power('2') stays accepted 7.x's numeric-only checks 2026-10-01
5 The UID description names every character it accepts (period, hyphen, underscore) 7.x text, which omits periods and hyphens 8.0.0 fix
6 An update does not re-validate stored values it leaves unchanged, for every type (J4C-011) re-validated, except object values 8.0.0 fix
7 document_purge fires after the outermost commit, never for a rolled-back write. The cache entry is still purged inside the transaction (2026-10-08 decision) fired just before the commit 8.0.0 design

What changes

  • Source: each item returns to its 8.0.0 code. Item 1 drops Builder\Postgres::compileSearchExpression(), and the R3-09 word splitting (foo/bar, foo,bar) stays. Item 2 is in Trait\Documents, with the Memory/Redis bound comparison. Item 3 is in Trait\Documents. The other items are Validator\Operator (4), Validator\UID (5), Validator\Structure/ObjectValue (6) and Trait\Transactions (7).
  • Docs: CHANGELOG.md has an 8.0.2 section and marks each reverted 8.0.1 entry. UPGRADE.md states the 8.0.0 rules again and notes that 8.0.1 reverted them by mistake.

Tests (fail-first)

All of these were written first and failed on main (8.0.1):

  • New e2e pair in DocumentTests, run on every engine with a full-text index. An exact term returns only the phrase, with notSearch() excluding only the phrase. A term without quotes matches any word in any order. On 8.0.1 PostgreSQL returned 4 rows instead of 2.
  • The unit tests 8.0.1 rewrote go back to their 8.0.0 expectations. These are FractionalBoundTest, FractionalOperatorLimitTest, IncreaseDecreaseTest, IncreaseValueTest, NumericUpdateGuardsTest, OperatorLimitTest, OperatorValidatorTest, RedisAdapterPathsTest, DocumentPurgeTest and SearchTermTest, plus e2e OperatorTests and the counter cases in DocumentTests. Each file failed on 8.0.1 (8, 2, 3, 17, 1, 6, 2, 1, 21 and 17 failures).
  • StoredValueRevalidationTest now asserts that a narrowed range or shrunk enum leaves the stored value in place and lets the update through, and that writing a refused value still fails.
  • QueriesCompatibilityTest asserts that the UID description matches what the validator accepts.
  • New DocumentPurgeTest::testACachePurgeFailureRollsTheWriteBackAndFiresNoPurgeEvent pins the 10-08 rule that stays: the cache purge runs inside the transaction, so a cache failure rolls the write back and no event fires.
  • Local runs: the unit files above pass. A filtered e2e subset (counters, operators, purge, stored values, floats, full-text) passes on PostgreSQL, MariaDB, MySQL, MongoDB, SQLite, Memory and Redis.

🤖 Generated with Claude Code

abnegate and others added 2 commits October 12, 2026 17:08
…ery engine

The user decided on 2026-10-06 that search('"foo bar"') matches the adjacent
phrase on every engine, as MariaDB, MySQL and SQLite always did, and 8.0.0
shipped that on PostgreSQL too. 8.0.1 bound the 7.x value again, so PostgreSQL
returned 'trump donald' and 'donald john trump' for "donald trump". The e2e
pair now runs on every engine with a fulltext index: the exact term returns
only documents holding the phrase (notSearch excludes only those, except on
MongoDB, whose notSearch is a regex over the raw term), and a term without
quotes keeps matching any word in any order. The PostgreSQL unit bindings
expect the phrase quotes and the 'or'-joined words of 8.0.0 again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8.0.0 matched search('"foo bar"') as the adjacent phrase on PostgreSQL, as
MariaDB, MySQL and SQLite always had, and the user confirmed on 2026-10-06
that this is the intended behaviour on every engine. 8.0.1 took the 7.x
any-order match for a regression and bound the term in single quotes again.
Dropping that override hands the prepared term back to the query builder,
which keeps an exact term's double quotes for websearch_to_tsquery(). The
7.4.1 word splitting (foo/bar and foo,bar search both words) still runs
first, so that fix stays intact. UPGRADE.md and CHANGELOG.md (8.0.2) say
8.0.1 reverted the phrase match by mistake.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: utopia-php/database/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 91abba83-e509-42ee-8e62-3eea238f2e2d



📥 Commits

Reviewing files that changed from the base of the PR and between 176dd25 and b840f8b.




📒 Files selected for processing (5)
  • CHANGELOG.md
  • UPGRADE.md
  • src/Database/Builder/Postgres.php
  • tests/e2e/Adapter/Scopes/DocumentTests.php
  • tests/unit/Builder/SearchTermTest.php



Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.





📝 Walkthrough
📝 Walkthrough
📝 Walkthrough

Walkthrough

The PostgreSQL builder’s custom search expression override was removed. Tests and documentation now describe quoted terms as adjacent-phrase searches and unquoted terms as any-word searches.

Changes

PostgreSQL Search Behavior

Layer / File(s) Summary
Use the shared search behavior
src/Database/Builder/Postgres.php
The builder description changes, and its custom compileSearchExpression() override is removed.
Verify exact and unquoted searches
tests/unit/Builder/SearchTermTest.php, tests/e2e/Adapter/Scopes/DocumentTests.php
Tests cover exact adjacent phrases, negated exact searches, and unquoted searches with different word orders and separators.
Document PostgreSQL search terms
UPGRADE.md, CHANGELOG.md
The documentation describes exact-phrase matching, unquoted any-word matching, and the related version history.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~15 minutes

Change: Bug fix

Suggested reviewers: claude





Merge Risk: ⚪ Minimal · up to b840f

PostgreSQL quoted searches are intended to match adjacent phrases, while unquoted searches match any term; separate searches require every term in any order. The reviewed code and test expectations align, with no specific merge-blocking concern identified.

Pre-merge checks | Passed 4 | Failed 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 3 files. (2 skipped: 2… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title accurately identifies the main change: restoring the 8.0.0 PostgreSQL search behavior that 8.0.1 reverted. It is concise and specific enough for project history.

Full details: Docstring Coverage

Explanation

Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 3 files. (2 skipped: 2 unsupported.)


  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR




🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR





  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

abnegate and others added 8 commits October 12, 2026 17:42
…vent rules again

The user decided these 8.0 rules stand, and 8.0.1 reverted them as if they
were regressions (decisions 10-01, BUG-11 approved 10-07, re-confirmed
10-12):

- increase/decreaseDocumentAttribute() refuse a fractional change, max or min
  on an integer attribute, and a change of 0 or less throws Exception\Type
  (BUG-11: catch (DatabaseException) must see it);
- numeric operators refuse a fractional limit on an integer attribute,
  while numeric text such as power('2') stays accepted;
- the UID description names the characters Key accepts (period, hyphen,
  underscore, none of them leading), not 7.x's narrower wrong text;
- an update does not re-validate stored values it leaves unchanged, for
  every attribute type (J4C-011), so a narrowed range or enum never blocks
  an update of another attribute, while a value the update writes is held
  to the new rule;
- document_purge fires after the outermost commit and never for a write
  that rolled back.

The tests 8.0.1 rewrote go back to their 8.0.0 expectations. A new
DocumentPurgeTest case pins the 10-08 decision that stays: the cache purge
runs inside the write's transaction, so a cache failure rolls the write back
and no purge event fires.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ception\Type again

The user decided on 10-01 that the library refuses a fractional change, max
or min on an integer attribute (Appwrite rounds the bounds itself), and
BUG-11, approved on 10-07, made a change of 0 or less throw Exception\Type so
that catch (DatabaseException) sees it. 8.0.1 handed fractional values to the
engine again (rounded on MariaDB/MySQL, truncated on MongoDB, an error on
PostgreSQL) and threw \InvalidArgumentException. This takes back the 8.0.0
checks in Documents and the Memory/Redis bound comparison.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ers again

The user decided on 10-01 that operators refuse fractional bounds on integer
attributes like increase does, with whole floats compared exactly, and that
numeric text such as power('2') stays accepted. 8.0.1 replaced those checks
with 7.x's numeric-only ones. Validator\Operator is the 8.0.0 validator
again; numeric text keeps passing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8.0.0 corrected the UID description to the rule Key enforces (period,
hyphen and underscore, none of them leading). 8.0.1 returned 7.x's text,
which leaves out periods and hyphens and so tells callers a wrong rule while
acceptance stayed the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ated again

8.0.0 (J4C-011) stopped re-validating the stored values an update leaves
unchanged, because a value stored under older or looser rules then failed
every later update of any attribute and could not be repaired. 8.0.1
validated them again and kept the leniency for object values only, so a
narrowed range or enum blocked unrelated updates once more. The user decided
the 8.0 rule stands for every type: a value the update writes is still held
to the current definition.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8.0.0 fired document_purge after the outermost transaction committed, so no
listener ever hears of a write that rolled back. 8.0.1 fired it just before
the commit. The user decided the 8.0 order stands. The cache entry itself is
still purged inside the write's transaction (the 10-08 decision that keeps
a cache outage from committing a write with a stale entry), and the
DocumentPurgeTest case added with the tests pins both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CHANGELOG.md gains the full 8.0.2 section and marks each reverted 8.0.1
entry. UPGRADE.md states the 8.0.0 rules for fractional counters, operator
limits, stored-value validation and document_purge again, and notes that
8.0.1 reverted them by mistake.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 8.0.0 OperatorTests came back with the operator rules, and with them the
engineFamily() helper that 8.0.1 had added there went away, although the
float binding test in DocumentTests still calls it. The helper now lives in
DocumentTests next to its only caller.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@abnegate abnegate changed the title fix: match an exact search as the adjacent phrase on PostgreSQL again (8.0.2) fix: bring back the 8.0.0 rules that 8.0.1 reverted by mistake (8.0.2) Oct 12, 2026
@abnegate
abnegate merged commit 54dfa6f into main Oct 12, 2026
24 checks passed
@abnegate
abnegate deleted the fix/postgres-phrase-search branch October 12, 2026 05:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant