Skip to content

fix: Allow pdb.agg() with solve_mvcc argument to work with all search operators#4534

Merged
philippemnoel merged 1 commit into0.22.xfrom
backport-4508-to-0.22.x
Mar 27, 2026
Merged

fix: Allow pdb.agg() with solve_mvcc argument to work with all search operators#4534
philippemnoel merged 1 commit into0.22.xfrom
backport-4508-to-0.22.x

Conversation

@paradedb-bot
Copy link
Copy Markdown
Contributor

Description

Backport of #4508 to 0.22.x.

… operators (#4508)

# Ticket(s) Closed

- Closes #4456

## What

`pdb.agg('...', false)` (and `true`) now works with all ParadeDB search
operators (`|||`, `&&&`, `###`, `===`), not just `@@@`. Previously this
query errored:

```sql
SELECT pdb.agg('{"value_count": {"field": "id"}}', false)
FROM mock_items
WHERE description ||| 'running shoes';
-- ERROR: pdb.agg() must be handled by ParadeDB's custom scan...
```

## Why

Two bugs prevented the AggregateScan from being created for non-`@@@`
operators:

1. **`is_anyelement_search_opoid()` only recognized `@@@`.**
   The function checked operator OIDs against two hardcoded `@@@`
   overloads (`@@@(anyelement, searchqueryinput)` and
   `@@@(anyelement, pdb.query)`). Operators like `|||`, `&&&`, `###`,
and `===` were not recognized, so `QualExtractState.uses_our_operator`
   was never set to `true` when walking the WHERE clause quals.

2. **`targetlist.rs` didn't set `uses_our_operator` for `pdb.agg()`.**
   Even if the qual state missed the operator, the presence of
   `pdb.agg()` in the SELECT list should have been sufficient to
   trigger the AggregateScan — since the placeholder state function
   will always error if PostgreSQL's standard aggregate machinery
   tries to process it.

Without either flag set, the AggregateScan path was rejected, only a
BaseScan was created for the WHERE clause, and PostgreSQL called the
placeholder `pdb.agg()` state function, which intentionally errors.

The single-argument `pdb.agg(jsonb)` worked with `|||` because it
followed a different code path that didn't depend on this flag in the
same way. The existing tests all used `@@@`, masking the gap.

## How

Three changes:

1. **`api/operator.rs` — recognize all search operators:**
   Added `is_paradedb_search_operator()` as a name-based fallback in
   `is_anyelement_search_opoid()`. It does a syscache lookup on the
   operator OID and matches the name against all ParadeDB operators
   (`@@@`, `|||`, `&&&`, `===`, `###`, `##`, `##>`). This covers all
   type overloads without enumerating 28 individual OIDs. The existing
   OID fast-path for `@@@` is preserved.

2. **`aggregatescan/targetlist.rs` — safety net for `pdb.agg()`:**
   When `AggregateType::try_from()` identifies a `Custom` aggregate,
   set `uses_our_operator = true` unconditionally. If `pdb.agg()` is
   in the target list, the AggregateScan must handle it regardless of
   which operator is in the WHERE clause.

3. **`hook.rs` — deduplicate operator check:**
   Removed the duplicate `is_paradedb_search_operator()` from `hook.rs`
   and replaced it with a call to the shared version in
   `api/operator.rs`. Single source of truth for the operator name list.

## Tests

Added Section 4 to `aggregate_edgecases.sql` covering:
- `|||` with `solve_mvcc=false` (the exact failing query from the issue)
- `|||` with `solve_mvcc=true` (explicit)
- `|||` with `solve_mvcc=false` + `GROUP BY` (with fast field)
- `|||` with deletion + `solve_mvcc=false` vs `true` (MVCC correctness)
- `|||` with single-arg `pdb.agg()` (baseline, already worked)
- `&&&` with `solve_mvcc=false` (conjunction operator)
- `###` with `solve_mvcc=false` (phrase match operator)
- `===` with `solve_mvcc=false` (exact term operator)

EXPLAIN VERBOSE confirms AggregateScan is used (not Aggregate →
BaseScan)
for all test cases.

---------

Signed-off-by: Ruchir Raj <[email protected]>
(cherry picked from commit 725708e)
@paradedb-bot paradedb-bot requested a review from a team as a code owner March 27, 2026 23:11
@paradedb-bot paradedb-bot added the automated-cherry-pick This PR is the result of our automated cherry-pick machinery. label Mar 27, 2026
@paradedb-bot paradedb-bot requested review from rebasedming and removed request for a team March 27, 2026 23:11
@paradedb-bot paradedb-bot added the automated-cherry-pick This PR is the result of our automated cherry-pick machinery. label Mar 27, 2026
@paradedb-bot paradedb-bot requested a review from RuchirRaj March 27, 2026 23:11
@philippemnoel philippemnoel merged commit 02be4de into 0.22.x Mar 27, 2026
22 checks passed
@philippemnoel philippemnoel deleted the backport-4508-to-0.22.x branch March 27, 2026 23:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automated-cherry-pick This PR is the result of our automated cherry-pick machinery.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants