Files
buzz/scripts/maintenance
Matt Toohey 60256e4323 fix(search): parse kind-0 content once and name the PostgreSQL 16 floor
Addresses the three findings from the review of ca974eb29; no behaviour
changes beyond what it asks.

The kind-0 `name_match` select column parsed `content` four times per
candidate row (one `pg_input_is_valid` plus three `::jsonb` casts), and it
ran for every row the WHERE admitted because the inner DISTINCT ON subquery
has no LIMIT. On a brownfield database the candidate set is exactly the
noisy one this branch fixes, so a short typeahead could spend hundreds of
milliseconds per keystroke in `jsonb_in`. Parse once instead: a
`CROSS JOIN LATERAL (SELECT CASE WHEN pg_input_is_valid(content, 'jsonb')
THEN content::jsonb END AS j OFFSET 0) AS profile` joins only for
`kinds == [0]`, and `name_match` reads `profile.j ->> 'name'`,
`'display_name'`, and `'displayName'`. `OFFSET 0` stops the planner pulling
the subquery up and re-expanding the cast at each reference. A NULL `j`
(invalid JSON) or a non-object document yields NULL from `->>`, an empty
vector, and `false`, so `name_match` is still never NULL and the ORDER BY is
unchanged. Every changed `push` sits inside the `profile_query` branch and
the split literal concatenates to the previous bytes, so the SQL emitted for
non-kind-0 searches is byte-identical.

Verified on PostgreSQL 17.9: with a notice-raising IMMUTABLE stand-in for
the cast, the lateral shape evaluates it 7 times over 7 rows and the
pulled-up shape without `OFFSET 0` evaluates it 21 times; `name_match` is
`false`, not NULL, for `not json`, `[1,2]`, `"wes"`, and `null` content.

Migration 0056 is the first to require PostgreSQL 16, and
`check_function_bodies` made it fail on an older server with a bare
"function pg_input_is_valid(text, unknown) does not exist". A preflight
`DO` block at the top now raises "Buzz requires PostgreSQL 16 or later
(migration 0056 uses pg_input_is_valid); this server is <version>" before
CREATE FUNCTION runs. The embedded-migrator content test asserts the
preflight precedes CREATE FUNCTION. README.md and the CONTRIBUTING.md
prerequisites table record the floor, noting that both Compose bundles ship
17 so it only matters for a database you run yourself. 0056 is unmerged, so
only development databases that already applied this branch's earlier
version see its checksum change.

`scripts/maintenance/nip_rs_search_allowlist.sql` now runs the same
`to_regprocedure('profile_search_tsv(text)')` precheck as
`profile_search_text_fields.sql`, after BEGIN and lock_timeout but before
ALTER TABLE, so a database that has not started on 0056 fails with the
readable instruction before taking ACCESS EXCLUSIVE. Exercised against an
empty scratch database: BEGIN, SET, the instruction, exit 3, no lock.

Verified with the lld linker workaround for sqlx proc-macros on this
machine: `scripts/postgres-test-run.sh -p buzz-search --tests` (23 passed,
including the four kind-0 tests and the two pre-existing ones);
`scripts/postgres-test-run.sh -p buzz-db --lib -E 'test(migration::postgres_tests)'`
(12 passed); `cargo test -p buzz-db --lib runtime::migration` (14 passed);
`cargo fmt --all --check`; and `cargo clippy -p buzz-search -p buzz-db
--all-targets -- -D warnings`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Matt Toohey <contact@matttoohey.com>
2026-10-02 14:29:16 +10:00
..