Skip to content

Carry, per function, the guard under which its answer is absent - #131

Merged
estebanzimanyi merged 1 commit into
MobilityDB:masterfrom
estebanzimanyi:feat/return-nullability
Sep 3, 2026
Merged

Carry, per function, the guard under which its answer is absent#131
estebanzimanyi merged 1 commit into
MobilityDB:masterfrom
estebanzimanyi:feat/return-nullability

Conversation

@estebanzimanyi

Copy link
Copy Markdown
Member

A C return type says what a value LOOKS like and never whether there is one.
bool tbool_value_at_timestamptz(..., bool *value) answers a value and a flag;
Temporal *temporal_at_timestamptz(...) answers a pointer that may be NULL; and
neither signature separates "no answer here" from "the answer is false". The
catalog states nullability for PARAMETERS -- shape.nullable, from the Doxygen
@param ... may be NULL -- and states nothing about the RESULT, so each binding
invents a convention for absence: a nullable in MEOS.NET, a None in
PyMEOS-CFFI, a zero value and a dereference in the two that carry neither.

The PostgreSQL wrapper already states it. Each wrapper guards PG_RETURN_NULL()
with the condition under which the answer is absent, and that guard is a token:
if (! result) for a null pointer, if (! found) for an out-parameter that
reports absence itself, if (result == DBL_MAX) for the distance sentinel,
if (result < 0) for a three-valued predicate. This reads that guard and
attaches it as shape.nullableResult, over the @csqlfn chain parser.sqlfn
already builds, so no new join and no new tag.

MEASURED against MobilityDB 4d3de5fc0e: 1038 wrappers carry an absence guard, in
the proportions the wrappers themselves state -- 275 if (! result), 67 the
distance sentinel, 61 the three-valued form, 11 if (! found).

A THIRD OF THE TREE DELEGATES, AND READING ONLY THE WRAPPER INVERTS THOSE.
Temporal_at_timestamptz is one line, return Temporal_restrict_timestamptz(fcinfo, REST_AT);, and the PG_RETURN_NULL sits in
that shared helper: 961 of 2894 wrapper bodies take that shape. Following the
helper the body names takes the count from 529 to 1038 and, more to the point,
turns the whole restriction family from "always answers" into what it is. The
walk is bounded and cycle-safe.

PG_ARGISNULL guards are excluded: those propagate a null ARGUMENT, which
shape.nullable already carries, and counting them would mark a function
absence-capable because its caller may pass nothing.

tests/test_nullresult.py covers the guard forms, the delegation hop, a
delegation cycle, the argument-null exclusion, and that a wrapper without a
guard and a function without a wrapper are left without the field rather than
given a false one. The suite goes 307 to 314 and the floor with it.

A C return type says what a value LOOKS like and never whether there is one.
`bool tbool_value_at_timestamptz(..., bool *value)` answers a value and a flag;
`Temporal *temporal_at_timestamptz(...)` answers a pointer that may be NULL; and
neither signature separates "no answer here" from "the answer is false". The
catalog states nullability for PARAMETERS -- `shape.nullable`, from the Doxygen
`@param ... may be NULL` -- and states nothing about the RESULT, so each binding
invents a convention for absence: a nullable in MEOS.NET, a `None` in
PyMEOS-CFFI, a zero value and a dereference in the two that carry neither.

The PostgreSQL wrapper already states it. Each wrapper guards `PG_RETURN_NULL()`
with the condition under which the answer is absent, and that guard is a token:
`if (! result)` for a null pointer, `if (! found)` for an out-parameter that
reports absence itself, `if (result == DBL_MAX)` for the distance sentinel,
`if (result < 0)` for a three-valued predicate. This reads that guard and
attaches it as `shape.nullableResult`, over the `@csqlfn` chain `parser.sqlfn`
already builds, so no new join and no new tag.

MEASURED against MobilityDB 4d3de5fc0e: 1038 wrappers carry an absence guard, in
the proportions the wrappers themselves state -- 275 `if (! result)`, 67 the
distance sentinel, 61 the three-valued form, 11 `if (! found)`.

A THIRD OF THE TREE DELEGATES, AND READING ONLY THE WRAPPER INVERTS THOSE.
`Temporal_at_timestamptz` is one line, `return
Temporal_restrict_timestamptz(fcinfo, REST_AT);`, and the PG_RETURN_NULL sits in
that shared helper: 961 of 2894 wrapper bodies take that shape. Following the
helper the body names takes the count from 529 to 1038 and, more to the point,
turns the whole restriction family from "always answers" into what it is. The
walk is bounded and cycle-safe.

`PG_ARGISNULL` guards are excluded: those propagate a null ARGUMENT, which
`shape.nullable` already carries, and counting them would mark a function
absence-capable because its caller may pass nothing.

tests/test_nullresult.py covers the guard forms, the delegation hop, a
delegation cycle, the argument-null exclusion, and that a wrapper without a
guard and a function without a wrapper are left without the field rather than
given a false one. The suite goes 307 to 314 and the floor with it.
@estebanzimanyi
estebanzimanyi merged commit ed63fda into MobilityDB:master Sep 3, 2026
3 checks passed
@estebanzimanyi
estebanzimanyi deleted the feat/return-nullability branch September 3, 2026 11:32
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