Skip to content

expr: Convert string.rs to the sqlfunc macro - #39097

Closed
antiguru wants to merge 1 commit into
sqlfunc-convert-timestampfrom
sqlfunc-convert-string
Closed

antiguru wants to merge 1 commit into
sqlfunc-convert-timestampfrom
sqlfunc-convert-string

Conversation

@antiguru

@antiguru antiguru commented Sep 23, 2026 •

Copy link
Copy Markdown
Member

Converts eleven implementations in string.rs to #[sqlfunc]. The four impl<E: Eval> LazyUnaryFunc blocks are generic over the sub-expression type, which the macro doesn't support, so they stay. CastStringToChar and CastStringToVarChar keep a could_error that depends on their payload. CastStringToInt2Vector, RegexpMatch and RegexpSplitToArray were hand-written lazy impls because they allocate their result and check NULL in the body. The arena passed to EagerUnaryFunc::call covers the allocation, and the input type covers the NULL check.

With these converted, doc/developer/sqlfunc.md says no convertible hand-written implementation remains.

The registry reports only sqlfunc_signature moving, on all sixteen records.

Based on #39096.

🤖 Generated with Claude Code

@antiguru
antiguru requested a review from a team as a code owner September 23, 2026 21:10
@antiguru
antiguru added this pull request to stack #38942 September 24, 2026 08:52
@antiguru
antiguru force-pushed the sqlfunc-convert-string branch from 72b8c84 to e1ac970 Compare September 24, 2026 10:14
@antiguru
antiguru force-pushed the sqlfunc-convert-timestamp branch from 4ec5632 to 9aebe53 Compare September 24, 2026 10:14
@antiguru
antiguru requested a review from petrosagg September 24, 2026 11:18
@antiguru
antiguru force-pushed the sqlfunc-convert-timestamp branch from 9aebe53 to ab55f6f Compare September 24, 2026 14:29
@antiguru
antiguru force-pushed the sqlfunc-convert-string branch from e1ac970 to 2b18b50 Compare September 24, 2026 14:29
@antiguru
antiguru force-pushed the sqlfunc-convert-timestamp branch from ab55f6f to ba19b21 Compare September 25, 2026 16:00
@antiguru
antiguru force-pushed the sqlfunc-convert-string branch from 2b18b50 to 357a56a Compare September 25, 2026 16:00
@antiguru
antiguru removed the request for review from petrosagg October 13, 2026 15:55
@antiguru
antiguru force-pushed the sqlfunc-convert-timestamp branch from ba19b21 to f2987f6 Compare October 14, 2026 09:11
@antiguru
antiguru force-pushed the sqlfunc-convert-string branch from 357a56a to a4a16bb Compare October 14, 2026 09:11
@antiguru
antiguru requested a review from mgree October 14, 2026 10:22
Eleven implementations in `src/expr/src/scalar/func/impls/string.rs` move under
`#[sqlfunc]`. The four `impl<E: Eval> LazyUnaryFunc` blocks interleaved among
them are generic over a sub-expression type and are untouched.

`CastStringToNumeric`, `CastStringToTimestamp` and `CastStringToTimestampTz`
read their payload in both `call` and `output_sql_type`, so each takes a `&self`
receiver and an `output_type_expr` that builds the scale or precision out of
that payload. The expression sets nullability from the input, and the macro's
unary tail adds only `propagates_nulls && input_type.nullable`, which the
non-nullable `&str` input makes the same term. `introduces_nulls` derives false
from the non-`Option` outputs. Their `Display` wrote a fixed string, so
`sqlname` reproduces it, and `inverse` carries over as a modifier.

`CastStringToChar` and `CastStringToVarChar` need the same shape of
`output_type_expr`, plus `could_error = self.fail_on_len &&
self.length.is_some()`, which a `Result` output would otherwise derive as an
unconditional true. `CastStringToVarChar` also keeps `preserves_uniqueness` and
`is_eliminable_cast` as expressions over `self.length`. `CastStringToChar`'s
`is_eliminable_cast` returned the trait default, so the override goes and its
reason moves onto the function. Both format their payload, so they keep their
`Display` impls under `skip_display = true`.

`IsLikeMatch` and `IsRegexpMatch` need no output modifier: their
`output_sql_type` returned `Bool.nullable(input.nullable)`, which is what a
`bool` output plus the tail gives. Both read their payload in `call` and format
it in `Display`, so they take a `&self` receiver and `skip_display = true`.

`CastStringToInt2Vector`, `RegexpMatch` and `RegexpSplitToArray` were
hand-written `LazyUnaryFunc` implementations because they allocate into a
`RowArena` and check NULL in the body. `EagerUnaryFunc::call` supplies the
arena, and the NULL check moves into the input type. The first two take `&'a
str`, which is already non-nullable. `RegexpMatch` takes
`ExcludeNull<Datum<'a>>`, because `regexp_match_static` wants a raw `Datum` and
a bare `Datum` accepts NULL; `ExcludeNull` reports `nullable() == false`, so the
derived `propagates_nulls` is true, matching the declaration, and its
`try_from_result` turns `Datum::Null` into `Err(Ok(Datum::Null))`, which the
evaluation layer returns as `Ok(Datum::Null)`. `CastStringToInt2Vector` returns
`Int2Vector<'a>`, whose column type the macro derives, so it needs no output
modifier, and it reads no payload, so the macro generates its unit struct.
`RegexpMatch` and `RegexpSplitToArray` return a `Datum`, whose column type is
not statically known, so each carries an `output_type_expr`.
`RegexpSplitToArray` returns NULL only for a NULL input, so it declares
`introduces_nulls = false` over the `Datum` default. `RegexpMatch` returns NULL
when the pattern does not match, which is what the `Datum` default says.

`RegexpReplace` is the one `EagerBinaryFunc`. It reads `regex` and `limit` in
`call`, so it takes a `&self` receiver, and its `output_sql_type` spelled out
the macro's own binary tail minus the non-nullable-position term, which the
non-nullable `&str` inputs make redundant, so it needs no output modifier. Its
`Display` reads the pattern, the case sensitivity and the limit, so it keeps its
hand-written impl under `skip_display = true`.

`doc/developer/sqlfunc.md` named the three convertible `LazyUnaryFunc`
implementations that remained. There are none left, so the section now states
that the two shapes it describes account for every hand-written implementation
under `src/expr/src/scalar/func/`.

The registry reports only `sqlfunc_signature` moving, on all sixteen records.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@antiguru
antiguru force-pushed the sqlfunc-convert-string branch from a4a16bb to 773e421 Compare October 14, 2026 15:18
@antiguru
antiguru force-pushed the sqlfunc-convert-timestamp branch from f2987f6 to 7937dc3 Compare October 14, 2026 15:18
@antiguru
antiguru removed this pull request from stack #38942 October 14, 2026 15:45
@antiguru

Copy link
Copy Markdown
Member Author

Folded into #39062, which now carries all twenty file conversions as separate commits, per review feedback to land them as one PR.

Posted by Claude Code.

@antiguru antiguru closed this Oct 14, 2026
antiguru added a commit that referenced this pull request Oct 14, 2026
…ro (#39062)

Converts the remaining hand-written scalar functions to `#[sqlfunc]`,
one commit per source file or function: the `Cast*ToNumeric` casts
(`int16`, `int32`, `int64`, `uint16`, `uint32`, `uint64`, `float32`,
`float64`, `jsonb`), `PadChar`, `AdjustNumericScale`,
`CastArrayToString`, `CastRangeToString`, and all of `date.rs`,
`time.rs`, `map.rs`, `record.rs`, `list.rs`, `timestamp.rs` and
`string.rs`. Each commit message accounts for every method the deleted
impl overrode.

No declared property changes. The registry reports only the macro
provenance fields moving. Where a hand-written impl declared a property
more conservatively than the body needs, the conversion pins the
declared value: `could_error` on `record_get`, and `introduces_nulls`,
`could_error` and a nullable output on `map_build_from_record_list`.
#39814 corrects both.

Supersedes #39079 to #39097, which held one conversion each.

Based on #39061.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@antiguru
antiguru deleted the sqlfunc-convert-string branch October 14, 2026 19:52
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