Skip to content

Propagate null through agtype arithmetic operators#2405

Merged
jrgemignani merged 1 commit intoapache:masterfrom
MuhammadTahaNaveed:i2394
Apr 27, 2026
Merged

Propagate null through agtype arithmetic operators#2405
jrgemignani merged 1 commit intoapache:masterfrom
MuhammadTahaNaveed:i2394

Conversation

@MuhammadTahaNaveed
Copy link
Copy Markdown
Member

The scalar branch of agtype_add lacked an AGTV_NULL case and fell through to the scalar-scalar concat path, so null + 1 produced the two-element list [null, 1] instead of null. This surfaced most visibly inside a list comprehension projection (e.g. [x IN [1, null, 2] | x + 1] yielded [2, [null, 1], 3]) because there both operands are typed agtype and bypass the agtype_any_add wrapper that already short-circuits on null.

The sibling operators (-, *, /, %, ^, unary -) had the same gap but surfaced as "Invalid input parameter types" errors rather than concats.

Short-circuit all seven functions to SQL NULL when any operand is AGTV_NULL. SQL NULL (not a wrapped AGTV_NULL scalar) matches AGE's existing convention: RETURN null AS v already yields SQL NULL at the row level, and agtype_any_add already returns PG NULL for AGTV_NULL input, so the two operator-resolution paths stay indistinguishable. List aggregation re-packs SQL NULL entries as AGTV_NULL, so [x IN [1, null, 2] | x + 1] now renders as [2, null, 3].

The scalar branch of agtype_add lacked an AGTV_NULL case and fell through
to the scalar-scalar concat path, so `null + 1` produced the two-element
list `[null, 1]` instead of null. This surfaced most visibly inside a
list comprehension projection (e.g. `[x IN [1, null, 2] | x + 1]` yielded
`[2, [null, 1], 3]`) because there both operands are typed agtype and
bypass the agtype_any_add wrapper that already short-circuits on null.

The sibling operators (-, *, /, %, ^, unary -) had the same gap but
surfaced as "Invalid input parameter types" errors rather than concats.

Short-circuit all seven functions to SQL NULL when any operand is
AGTV_NULL. SQL NULL (not a wrapped AGTV_NULL scalar) matches AGE's
existing convention: `RETURN null AS v` already yields SQL NULL at the
row level, and agtype_any_add already returns PG NULL for AGTV_NULL
input, so the two operator-resolution paths stay indistinguishable.
List aggregation re-packs SQL NULL entries as AGTV_NULL, so
`[x IN [1, null, 2] | x + 1]` now renders as `[2, null, 3]`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@jrgemignani jrgemignani merged commit 774e781 into apache:master Apr 27, 2026
7 checks passed
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Propagates SQL NULL through scalar agtype arithmetic operators to match openCypher semantics and prevent unintended concatenation / type errors, especially in list comprehension projections.

Changes:

  • Add early-return PG_RETURN_NULL() handling for AGTV_NULL operands across + - * / % ^ and unary -.
  • Extend regression coverage for list comprehensions to ensure null propagation across arithmetic operators.
  • Add/adjust agtype regression expectations to reflect SQL NULL results for arithmetic involving null.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/backend/utils/adt/agtype_ops.c Adds AGTV_NULL short-circuit returns for scalar arithmetic operators.
regress/sql/list_comprehension.sql Adds regression queries covering null propagation in list comprehension projections.
regress/sql/agtype.sql Adds regression queries for arithmetic operators with null operands.
regress/expected/list_comprehension.out Updates expected results to show null propagation instead of sublists/errors.
regress/expected/agtype.out Updates expected results to show SQL NULL outputs for null arithmetic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread regress/sql/agtype.sql
SELECT 'null'::agtype * '1';
SELECT 'null'::agtype / '1';
SELECT 'null'::agtype % '1';
SELECT 'null'::agtype ^ '1';
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests pass a bare string literal as the RHS (type unknown), which relies on implicit coercion/operator resolution and can make the intent of testing the scalar agtype operator path less explicit. Consider casting the RHS to agtype (e.g., '1'::agtype) so the tests more directly exercise agtype-vs-agtype arithmetic and are less sensitive to changes in implicit casts.

Suggested change
SELECT 'null'::agtype ^ '1';
SELECT 'null'::agtype ^ '1'::agtype;

Copilot uses AI. Check for mistakes.
Comment on lines +168 to +172
/* openCypher: arithmetic over null yields null. */
if (agtv_lhs->type == AGTV_NULL || agtv_rhs->type == AGTV_NULL)
{
PG_RETURN_NULL();
}
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The identical AGTV_NULL short-circuit block is now duplicated across multiple operator functions (add/sub/mul/div/mod/pow/neg). To reduce repetition and keep behavior consistent over time, consider factoring this into a small helper (e.g., a static inline function or macro) used by each operator.

Copilot uses AI. Check for mistakes.
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.

List-comprehension projection over a null element may produce a nested list instead of null.

3 participants