From 65342ff734e8e89b6b00bad5455a8bf7dcde0eed Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Fri, 18 Sep 2026 17:03:54 +0200 Subject: [PATCH 01/19] expr: Give EagerUnaryFunc::call a RowArena `EagerBinaryFunc::call` and `EagerVariadicFunc::call` take an arena, unary did not. That gap blocks the compound-type casts that allocate their output, which have to stay hand-written `LazyUnaryFunc` impls to reach an arena at all. `EagerUnaryFunc::call` now takes `temp_storage: &'a RowArena`, and the blanket `LazyUnaryFunc` impl forwards the arena it already holds. `Shape::takes_arena` answers true for every arity, so `#[sqlfunc]` generates the parameter on unary `call` and forwards it when the annotated function asks for one. A unary function with a trailing `&RowArena` is therefore accepted rather than rejected. The hand-written unary impls take the parameter and ignore it. Co-Authored-By: Claude Opus 5 (1M context) --- src/catalog/tests/audit_log_details.rs | 4 +- src/catalog/tests/sql_parse_catalog.rs | 11 +++- src/expr-derive-impl/src/lib.rs | 13 ++++ src/expr-derive-impl/src/shape.rs | 5 +- ...erive_impl__test__unary_all_modifiers.snap | 6 +- ...xpr_derive_impl__test__unary_arena_fn.snap | 64 +++++++++++++++++- ...r_derive_impl__test__unary_arena_used.snap | 65 +++++++++++++++++++ .../mz_expr_derive_impl__test__unary_fn.snap | 6 +- ...erive_impl__test__unary_generic_range.snap | 6 +- .../mz_expr_derive_impl__test__unary_ref.snap | 6 +- src/expr-derive-impl/src/sqlfunc.rs | 19 ++---- src/expr/src/scalar/func/impls/char.rs | 4 +- src/expr/src/scalar/func/impls/date.rs | 8 +-- src/expr/src/scalar/func/impls/float32.rs | 4 +- src/expr/src/scalar/func/impls/float64.rs | 4 +- src/expr/src/scalar/func/impls/int16.rs | 4 +- src/expr/src/scalar/func/impls/int32.rs | 4 +- src/expr/src/scalar/func/impls/int64.rs | 4 +- src/expr/src/scalar/func/impls/jsonb.rs | 4 +- src/expr/src/scalar/func/impls/numeric.rs | 4 +- src/expr/src/scalar/func/impls/string.rs | 14 ++-- src/expr/src/scalar/func/impls/time.rs | 8 +-- src/expr/src/scalar/func/impls/timestamp.rs | 34 +++++----- src/expr/src/scalar/func/impls/uint16.rs | 4 +- src/expr/src/scalar/func/impls/uint32.rs | 4 +- src/expr/src/scalar/func/impls/uint64.rs | 4 +- src/expr/src/scalar/func/unary.rs | 4 +- 27 files changed, 234 insertions(+), 83 deletions(-) create mode 100644 src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_arena_used.snap diff --git a/src/catalog/tests/audit_log_details.rs b/src/catalog/tests/audit_log_details.rs index 2c172d8afe24b..90656850a64eb 100644 --- a/src/catalog/tests/audit_log_details.rs +++ b/src/catalog/tests/audit_log_details.rs @@ -26,6 +26,7 @@ use mz_audit_log::EventDetails; use mz_catalog::durable::objects::serialization::RustType; use mz_catalog_protos::objects::audit_log_event_v1; use mz_expr::func::{EagerUnaryFunc, ParseCatalogAuditLogDetails}; +use mz_repr::RowArena; use mz_repr::adt::jsonb::Jsonb; use proptest::prelude::*; @@ -42,8 +43,9 @@ proptest! { let proto_json = serde_json::to_value(&proto).expect("proto serializes"); let jsonb = Jsonb::from_serde_json(proto_json).expect("valid jsonb"); + let temp_storage = RowArena::new(); let got = ParseCatalogAuditLogDetails - .call(jsonb.as_ref()) + .call(jsonb.as_ref(), &temp_storage) .expect("helper succeeded"); let got_json = got.as_ref().to_serde_json(); diff --git a/src/catalog/tests/sql_parse_catalog.rs b/src/catalog/tests/sql_parse_catalog.rs index 9531021c3a929..e6e937efcf3f1 100644 --- a/src/catalog/tests/sql_parse_catalog.rs +++ b/src/catalog/tests/sql_parse_catalog.rs @@ -20,20 +20,25 @@ use mz_repr::adt::jsonb::Jsonb; use mz_repr::adt::mz_acl_item::{AclMode, MzAclItem}; use mz_repr::network_policy_id::NetworkPolicyId; use mz_repr::role_id::RoleId; -use mz_repr::{CatalogItemId, GlobalId}; +use mz_repr::{CatalogItemId, GlobalId, RowArena}; use mz_sql::names::{DatabaseId, SchemaId}; use proptest::prelude::*; fn serialize_and_parse_catalog_id(val: &impl serde::Serialize) -> String { let json = serde_json::to_value(val).unwrap(); let jsonb = Jsonb::from_serde_json(json).unwrap(); - ParseCatalogId.call(jsonb.as_ref()).unwrap() + let temp_storage = RowArena::new(); + ParseCatalogId.call(jsonb.as_ref(), &temp_storage).unwrap() } fn serialize_and_parse_catalog_privileges(val: &impl serde::Serialize) -> Vec { let json = serde_json::to_value(val).unwrap(); let jsonb = Jsonb::from_serde_json(json).unwrap(); - ParseCatalogPrivileges.call(jsonb.as_ref()).unwrap().0 + let temp_storage = RowArena::new(); + ParseCatalogPrivileges + .call(jsonb.as_ref(), &temp_storage) + .unwrap() + .0 } proptest! { diff --git a/src/expr-derive-impl/src/lib.rs b/src/expr-derive-impl/src/lib.rs index 6746e7e218f47..420ed7af92d13 100644 --- a/src/expr-derive-impl/src/lib.rs +++ b/src/expr-derive-impl/src/lib.rs @@ -108,6 +108,19 @@ mod test { insta::assert_snapshot!("unary_arena_fn", output, &input); } + #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri + #[mz_ore::test] + fn insta_test_unary_arena_used() { + let attr = quote! { sqlname = "to_arena_string", test = true }; + let item = quote! { + fn to_arena_string<'a>(a: i32, temp_storage: &'a RowArena) -> &'a str { + temp_storage.push_string(a.to_string()) + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("unary_arena_used", output, &input); + } + #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri #[mz_ore::test] fn insta_test_unary_ref() { diff --git a/src/expr-derive-impl/src/shape.rs b/src/expr-derive-impl/src/shape.rs index 22de6fe8f70df..aed3c4a51678d 100644 --- a/src/expr-derive-impl/src/shape.rs +++ b/src/expr-derive-impl/src/shape.rs @@ -170,9 +170,10 @@ impl Shape { /// Whether the trait's `call` receives a `&'a RowArena`. pub(crate) fn takes_arena(&self) -> bool { + // Exhaustive so each arity states its own answer instead of inheriting a + // constant. match self { - Shape::Unary => false, - Shape::Binary | Shape::Variadic => true, + Shape::Unary | Shape::Binary | Shape::Variadic => true, } } diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_all_modifiers.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_all_modifiers.snap index 212a16c82a90a..eff8fd6a32783 100644 --- a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_all_modifiers.snap +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_all_modifiers.snap @@ -18,7 +18,11 @@ pub struct UnaryAllModifiers; impl crate::func::EagerUnaryFunc for UnaryAllModifiers { type Input<'a> = Datum<'a>; type Output<'a> = Datum<'a>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>( + &self, + a: Self::Input<'a>, + temp_storage: &'a mz_repr::RowArena, + ) -> Self::Output<'a> { unary_all_modifiers(a) } fn output_sql_type( diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_arena_fn.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_arena_fn.snap index fdb1a9d0684b5..b5de6faac963a 100644 --- a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_arena_fn.snap +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_arena_fn.snap @@ -2,6 +2,66 @@ source: src/expr-derive-impl/src/lib.rs expression: "#[sqlfunc(test = true)]\nfn unary_fn<'a>(a: Datum<'a>, temp_storage: &RowArena) -> bool {\n unimplemented!()\n}\n" --- -::core::compile_error! { - "Unary functions do not yet support RowArena." +#[derive( + Ord, + PartialOrd, + Clone, + Debug, + Eq, + PartialEq, + serde::Serialize, + serde::Deserialize, + Hash, +)] +#[cfg_attr(any(test, feature = "proptest"), derive(proptest_derive::Arbitrary))] +pub struct UnaryFn; +impl crate::func::EagerUnaryFunc for UnaryFn { + type Input<'a> = Datum<'a>; + type Output<'a> = bool; + fn call<'a>( + &self, + a: Self::Input<'a>, + temp_storage: &'a mz_repr::RowArena, + ) -> Self::Output<'a> { + unary_fn(a, temp_storage) + } + fn output_sql_type( + &self, + input_type: mz_repr::SqlColumnType, + ) -> mz_repr::SqlColumnType { + use mz_repr::AsColumnType; + let output = Self::Output::as_column_type(); + let propagates_nulls = crate::func::EagerUnaryFunc::propagates_nulls(self); + let nullable = output.nullable; + output.nullable(nullable || (propagates_nulls && input_type.nullable)) + } +} +impl std::fmt::Display for UnaryFn { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(stringify!(unary_fn)) + } +} +impl crate::func::FuncName for UnaryFn { + const NAME: &'static str = stringify!(unary_fn); + #[cfg(feature = "func-registry")] + const SQLFUNC: Option = Some(crate::func::SqlFuncSource { + decl: "#[sqlfunc(test = true)] fn unary_fn<'a>(a: Datum<'a>, temp_storage: &RowArena) -> bool", + signature: "fn(Datum<'a>) -> bool", + body_fingerprint: 4726381389472009124u64, + }); + #[cfg(feature = "func-registry")] + fn sqlfunc_input_types() -> Option> { + use crate::func::registry::{ProbeColumnType as _, ProbeColumnTypeFallback as _}; + [ + (&crate::func::registry::ColumnTypeProbe::< + Datum<'static>, + >(::std::marker::PhantomData)) + .column_type(), + ] + .into_iter() + .collect() + } +} +fn unary_fn<'a>(a: Datum<'a>, temp_storage: &RowArena) -> bool { + unimplemented!() } diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_arena_used.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_arena_used.snap new file mode 100644 index 0000000000000..3b99d84a59791 --- /dev/null +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_arena_used.snap @@ -0,0 +1,65 @@ +--- +source: src/expr-derive-impl/src/lib.rs +expression: "#[sqlfunc(sqlname = \"to_arena_string\", test = true)]\nfn to_arena_string<'a>(a: i32, temp_storage: &'a RowArena) -> &'a str {\n temp_storage.push_string(a.to_string())\n}\n" +--- +#[derive( + Ord, + PartialOrd, + Clone, + Debug, + Eq, + PartialEq, + serde::Serialize, + serde::Deserialize, + Hash, +)] +#[cfg_attr(any(test, feature = "proptest"), derive(proptest_derive::Arbitrary))] +pub struct ToArenaString; +impl crate::func::EagerUnaryFunc for ToArenaString { + type Input<'a> = i32; + type Output<'a> = &'a str; + fn call<'a>( + &self, + a: Self::Input<'a>, + temp_storage: &'a mz_repr::RowArena, + ) -> Self::Output<'a> { + to_arena_string(a, temp_storage) + } + fn output_sql_type( + &self, + input_type: mz_repr::SqlColumnType, + ) -> mz_repr::SqlColumnType { + use mz_repr::AsColumnType; + let output = Self::Output::as_column_type(); + let propagates_nulls = crate::func::EagerUnaryFunc::propagates_nulls(self); + let nullable = output.nullable; + output.nullable(nullable || (propagates_nulls && input_type.nullable)) + } +} +impl std::fmt::Display for ToArenaString { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str("to_arena_string") + } +} +impl crate::func::FuncName for ToArenaString { + const NAME: &'static str = stringify!(to_arena_string); + #[cfg(feature = "func-registry")] + const SQLFUNC: Option = Some(crate::func::SqlFuncSource { + decl: "#[sqlfunc(sqlname = \"to_arena_string\", test = true)] fn to_arena_string<'a>(a: i32, temp_storage: &'a RowArena) -> &'a str", + signature: "fn(i32) -> &'a str", + body_fingerprint: 17915273539525400975u64, + }); + #[cfg(feature = "func-registry")] + fn sqlfunc_input_types() -> Option> { + use crate::func::registry::{ProbeColumnType as _, ProbeColumnTypeFallback as _}; + [ + (&crate::func::registry::ColumnTypeProbe::(::std::marker::PhantomData)) + .column_type(), + ] + .into_iter() + .collect() + } +} +fn to_arena_string<'a>(a: i32, temp_storage: &'a RowArena) -> &'a str { + temp_storage.push_string(a.to_string()) +} diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_fn.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_fn.snap index a747f717228a8..d599f6341bda7 100644 --- a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_fn.snap +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_fn.snap @@ -18,7 +18,11 @@ pub struct UnaryFn; impl crate::func::EagerUnaryFunc for UnaryFn { type Input<'a> = Datum<'a>; type Output<'a> = bool; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>( + &self, + a: Self::Input<'a>, + temp_storage: &'a mz_repr::RowArena, + ) -> Self::Output<'a> { unary_fn(a) } fn output_sql_type( diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_generic_range.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_generic_range.snap index 00f49b11b93ba..b7c6b38a7cb12 100644 --- a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_generic_range.snap +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_generic_range.snap @@ -18,7 +18,11 @@ pub struct RangeLower; impl crate::func::EagerUnaryFunc for RangeLower { type Input<'a> = Range>; type Output<'a> = Option>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>( + &self, + a: Self::Input<'a>, + temp_storage: &'a mz_repr::RowArena, + ) -> Self::Output<'a> { range_lower(a) } fn output_sql_type( diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_ref.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_ref.snap index 9b2deda1e20f5..3f3e50f3ed19f 100644 --- a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_ref.snap +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_ref.snap @@ -18,7 +18,11 @@ pub struct UnaryFn; impl crate::func::EagerUnaryFunc for UnaryFn { type Input<'a> = &'a i16; type Output<'a> = bool; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>( + &self, + a: Self::Input<'a>, + temp_storage: &'a mz_repr::RowArena, + ) -> Self::Output<'a> { unary_fn(a) } fn output_sql_type( diff --git a/src/expr-derive-impl/src/sqlfunc.rs b/src/expr-derive-impl/src/sqlfunc.rs index 762aa77981148..ff92db8bad607 100644 --- a/src/expr-derive-impl/src/sqlfunc.rs +++ b/src/expr-derive-impl/src/sqlfunc.rs @@ -49,12 +49,7 @@ pub fn sqlfunc( let tokens = match determine_arity(&func) { Arity::Nullary => Err(darling::Error::custom("Nullary functions not supported")), - Arity::Unary { arena: false } => { - generate(Shape::Unary, &func, modifiers, None, false, &attr) - } - Arity::Unary { arena: true } => Err(darling::Error::custom( - "Unary functions do not yet support RowArena.", - )), + Arity::Unary => generate(Shape::Unary, &func, modifiers, None, false, &attr), Arity::Binary => generate(Shape::Binary, &func, modifiers, None, false, &attr), Arity::Variadic { has_self } => generate( Shape::Variadic, @@ -100,15 +95,9 @@ fn generate_test(_attr: TokenStream, _item: TokenStream, _name: &Ident) -> Token /// Arity classification for a function annotated with `#[sqlfunc]`. enum Arity { Nullary, - Unary { - /// Whether a trailing `&RowArena` parameter is present, which - /// `EagerUnaryFunc::call` has no place for. - arena: bool, - }, + Unary, Binary, - Variadic { - has_self: bool, - }, + Variadic { has_self: bool }, } /// Checks whether a parameter's type is `Variadic<...>` or `OptionalArg<...>`, @@ -163,7 +152,7 @@ fn determine_arity(func: &syn::ItemFn) -> Arity { } else { match effective_count { 0 => Arity::Nullary, - 1 => Arity::Unary { arena }, + 1 => Arity::Unary, 2 => Arity::Binary, _ => unreachable!(), } diff --git a/src/expr/src/scalar/func/impls/char.rs b/src/expr/src/scalar/func/impls/char.rs index d7235ef27eec9..2a6f81b4c870c 100644 --- a/src/expr/src/scalar/func/impls/char.rs +++ b/src/expr/src/scalar/func/impls/char.rs @@ -11,7 +11,7 @@ use std::fmt; use mz_expr_derive::sqlfunc; use mz_repr::adt::char::{Char, CharLength, format_str_pad}; -use mz_repr::{SqlColumnType, SqlScalarType}; +use mz_repr::{RowArena, SqlColumnType, SqlScalarType}; use serde::{Deserialize, Serialize}; use crate::scalar::func::EagerUnaryFunc; @@ -38,7 +38,7 @@ impl EagerUnaryFunc for PadChar { type Input<'a> = &'a str; type Output<'a> = Char; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { Char(format_str_pad(a, self.length)) } diff --git a/src/expr/src/scalar/func/impls/date.rs b/src/expr/src/scalar/func/impls/date.rs index bb4f5dd1ee548..4402dc52572b7 100644 --- a/src/expr/src/scalar/func/impls/date.rs +++ b/src/expr/src/scalar/func/impls/date.rs @@ -15,7 +15,7 @@ use mz_repr::adt::date::Date; use mz_repr::adt::datetime::DateTimeUnits; use mz_repr::adt::numeric::Numeric; use mz_repr::adt::timestamp::{CheckedTimestamp, DateLike, TimestampPrecision}; -use mz_repr::{SqlColumnType, SqlScalarType, strconv}; +use mz_repr::{RowArena, SqlColumnType, SqlScalarType, strconv}; use serde::{Deserialize, Serialize}; use crate::EvalError; @@ -50,7 +50,7 @@ impl EagerUnaryFunc for CastDateToTimestamp { type Input<'a> = Date; type Output<'a> = Result, EvalError>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { let out = CheckedTimestamp::from_timestamplike(NaiveDate::from(a).and_hms_opt(0, 0, 0).unwrap())?; let updated = out.round_to_precision(self.0)?; @@ -97,7 +97,7 @@ impl EagerUnaryFunc for CastDateToTimestampTz { type Input<'a> = Date; type Output<'a> = Result>, EvalError>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { let out = CheckedTimestamp::from_timestamplike(DateTime::::from_naive_utc_and_offset( NaiveDate::from(a).and_hms_opt(0, 0, 0).unwrap(), @@ -179,7 +179,7 @@ impl EagerUnaryFunc for ExtractDate { type Input<'a> = Date; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { extract_date_inner(self.0, a.into()) } diff --git a/src/expr/src/scalar/func/impls/float32.rs b/src/expr/src/scalar/func/impls/float32.rs index 3bd1e2b0368bc..1be0ac9f8d763 100644 --- a/src/expr/src/scalar/func/impls/float32.rs +++ b/src/expr/src/scalar/func/impls/float32.rs @@ -11,7 +11,7 @@ use std::fmt; use mz_expr_derive::sqlfunc; use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale}; -use mz_repr::{SqlColumnType, SqlScalarType, strconv}; +use mz_repr::{RowArena, SqlColumnType, SqlScalarType, strconv}; use serde::{Deserialize, Serialize}; use crate::EvalError; @@ -206,7 +206,7 @@ impl EagerUnaryFunc for CastFloat32ToNumeric { type Input<'a> = f32; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { if a.is_infinite() { return Err(EvalError::InfinityOutOfDomain( "casting real to numeric".into(), diff --git a/src/expr/src/scalar/func/impls/float64.rs b/src/expr/src/scalar/func/impls/float64.rs index de6096dfd1b52..feb716016cb92 100644 --- a/src/expr/src/scalar/func/impls/float64.rs +++ b/src/expr/src/scalar/func/impls/float64.rs @@ -14,7 +14,7 @@ use mz_expr_derive::sqlfunc; use mz_ore::cast::TryCastFrom; use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale}; use mz_repr::adt::timestamp::CheckedTimestamp; -use mz_repr::{SqlColumnType, SqlScalarType, strconv}; +use mz_repr::{RowArena, SqlColumnType, SqlScalarType, strconv}; use serde::{Deserialize, Serialize}; use crate::EvalError; @@ -217,7 +217,7 @@ impl EagerUnaryFunc for CastFloat64ToNumeric { type Input<'a> = f64; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { if a.is_infinite() { return Err(EvalError::InfinityOutOfDomain( "casting double precision to numeric".into(), diff --git a/src/expr/src/scalar/func/impls/int16.rs b/src/expr/src/scalar/func/impls/int16.rs index 6a3c38d2b02e2..711424f16267b 100644 --- a/src/expr/src/scalar/func/impls/int16.rs +++ b/src/expr/src/scalar/func/impls/int16.rs @@ -11,7 +11,7 @@ use std::fmt; use mz_expr_derive::sqlfunc; use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale}; -use mz_repr::{SqlColumnType, SqlScalarType, strconv}; +use mz_repr::{RowArena, SqlColumnType, SqlScalarType, strconv}; use serde::{Deserialize, Serialize}; use crate::EvalError; @@ -141,7 +141,7 @@ impl EagerUnaryFunc for CastInt16ToNumeric { type Input<'a> = i16; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { let mut a = Numeric::from(i32::from(a)); if let Some(scale) = self.0 { if numeric::rescale(&mut a, scale.into_u8()).is_err() { diff --git a/src/expr/src/scalar/func/impls/int32.rs b/src/expr/src/scalar/func/impls/int32.rs index e89dce9d726b7..fc895deda3306 100644 --- a/src/expr/src/scalar/func/impls/int32.rs +++ b/src/expr/src/scalar/func/impls/int32.rs @@ -13,7 +13,7 @@ use mz_expr_derive::sqlfunc; use mz_ore::cast::ReinterpretCast; use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale}; use mz_repr::adt::system::{Oid, PgLegacyChar}; -use mz_repr::{SqlColumnType, SqlScalarType, strconv}; +use mz_repr::{RowArena, SqlColumnType, SqlScalarType, strconv}; use serde::{Deserialize, Serialize}; use crate::EvalError; @@ -156,7 +156,7 @@ impl EagerUnaryFunc for CastInt32ToNumeric { type Input<'a> = i32; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { let mut a = Numeric::from(a); if let Some(scale) = self.0 { if numeric::rescale(&mut a, scale.into_u8()).is_err() { diff --git a/src/expr/src/scalar/func/impls/int64.rs b/src/expr/src/scalar/func/impls/int64.rs index 763661faadab8..731f701e0e2ef 100644 --- a/src/expr/src/scalar/func/impls/int64.rs +++ b/src/expr/src/scalar/func/impls/int64.rs @@ -12,7 +12,7 @@ use std::fmt; use mz_expr_derive::sqlfunc; use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale}; use mz_repr::adt::system::Oid; -use mz_repr::{SqlColumnType, SqlScalarType, strconv}; +use mz_repr::{RowArena, SqlColumnType, SqlScalarType, strconv}; use serde::{Deserialize, Serialize}; use crate::EvalError; @@ -133,7 +133,7 @@ impl EagerUnaryFunc for CastInt64ToNumeric { type Input<'a> = i64; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { let mut a = Numeric::from(a); if let Some(scale) = self.0 { if numeric::rescale(&mut a, scale.into_u8()).is_err() { diff --git a/src/expr/src/scalar/func/impls/jsonb.rs b/src/expr/src/scalar/func/impls/jsonb.rs index 92fadbe54f8bd..f682658e85f55 100644 --- a/src/expr/src/scalar/func/impls/jsonb.rs +++ b/src/expr/src/scalar/func/impls/jsonb.rs @@ -12,7 +12,7 @@ use std::fmt; use mz_expr_derive::sqlfunc; use mz_repr::adt::jsonb::{Jsonb, JsonbRef}; use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale}; -use mz_repr::{Datum, Row, RowPacker, SqlColumnType, SqlScalarType, strconv}; +use mz_repr::{Datum, Row, RowArena, RowPacker, SqlColumnType, SqlScalarType, strconv}; use serde::{Deserialize, Serialize}; use crate::EvalError; @@ -102,7 +102,7 @@ impl EagerUnaryFunc for CastJsonbToNumeric { type Input<'a> = JsonbRef<'a>; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { match a.into_datum() { Datum::Numeric(mut num) => match self.0 { None => Ok(num.into_inner()), diff --git a/src/expr/src/scalar/func/impls/numeric.rs b/src/expr/src/scalar/func/impls/numeric.rs index 79c0d8369a286..2640a9f7844fa 100644 --- a/src/expr/src/scalar/func/impls/numeric.rs +++ b/src/expr/src/scalar/func/impls/numeric.rs @@ -12,7 +12,7 @@ use std::fmt; use dec::{OrderedDecimal, Rounding}; use mz_expr_derive::sqlfunc; use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale}; -use mz_repr::{SqlColumnType, SqlScalarType, strconv}; +use mz_repr::{RowArena, SqlColumnType, SqlScalarType, strconv}; use serde::{Deserialize, Serialize}; use crate::EvalError; @@ -325,7 +325,7 @@ impl EagerUnaryFunc for AdjustNumericScale { type Input<'a> = Numeric; type Output<'a> = Result; - fn call<'a>(&self, mut d: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, mut d: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { if numeric::rescale(&mut d, self.0.into_u8()).is_err() { return Err(EvalError::NumericFieldOverflow); }; diff --git a/src/expr/src/scalar/func/impls/string.rs b/src/expr/src/scalar/func/impls/string.rs index 28de6c8a32734..bfc99ff1e8a34 100644 --- a/src/expr/src/scalar/func/impls/string.rs +++ b/src/expr/src/scalar/func/impls/string.rs @@ -179,7 +179,7 @@ impl EagerUnaryFunc for CastStringToNumeric { type Input<'a> = &'a str; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { let mut d = strconv::parse_numeric(a)?; if let Some(scale) = self.0 { if numeric::rescale(&mut d.0, scale.into_u8()).is_err() { @@ -239,7 +239,7 @@ impl EagerUnaryFunc for CastStringToTimestamp { type Input<'a> = &'a str; type Output<'a> = Result, EvalError>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { let out = strconv::parse_timestamp(a)?; let updated = out.round_to_precision(self.0)?; Ok(updated) @@ -292,7 +292,7 @@ impl EagerUnaryFunc for CastStringToTimestampTz { type Input<'a> = &'a str; type Output<'a> = Result>, EvalError>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { let out = strconv::parse_timestamptz(a)?; let updated = out.round_to_precision(self.0)?; Ok(updated) @@ -696,7 +696,7 @@ impl EagerUnaryFunc for CastStringToChar { type Input<'a> = &'a str; type Output<'a> = Result, EvalError>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { let s = format_str_trim(a, self.length, self.fail_on_len).map_err(|_| { assert!(self.fail_on_len); EvalError::StringValueTooLong { @@ -878,7 +878,7 @@ impl EagerUnaryFunc for CastStringToVarChar { type Input<'a> = &'a str; type Output<'a> = Result, EvalError>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { let s = mz_repr::adt::varchar::format_str(a, self.length, self.fail_on_len).map_err(|_| { assert!(self.fail_on_len); @@ -1120,7 +1120,7 @@ impl EagerUnaryFunc for IsLikeMatch { type Input<'a> = &'a str; type Output<'a> = bool; - fn call<'a>(&self, haystack: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, haystack: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { self.0.is_match(haystack) } @@ -1157,7 +1157,7 @@ impl EagerUnaryFunc for IsRegexpMatch { type Input<'a> = &'a str; type Output<'a> = bool; - fn call<'a>(&self, haystack: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, haystack: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { self.0.is_match(haystack) } diff --git a/src/expr/src/scalar/func/impls/time.rs b/src/expr/src/scalar/func/impls/time.rs index a3a2e11e8ec36..9b75479e20e8d 100644 --- a/src/expr/src/scalar/func/impls/time.rs +++ b/src/expr/src/scalar/func/impls/time.rs @@ -16,7 +16,7 @@ use mz_repr::adt::datetime::{DateTimeField, DateTimeUnits}; use mz_repr::adt::interval::Interval; use mz_repr::adt::numeric::{DecimalLike, Numeric}; use mz_repr::adt::timestamp::TimeLike; -use mz_repr::{SqlColumnType, SqlScalarType, strconv}; +use mz_repr::{RowArena, SqlColumnType, SqlScalarType, strconv}; use serde::{Deserialize, Serialize}; use crate::EvalError; @@ -103,7 +103,7 @@ impl EagerUnaryFunc for ExtractTime { type Input<'a> = NaiveTime; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { date_part_time_inner(self.0, a) } @@ -135,7 +135,7 @@ impl EagerUnaryFunc for DatePartTime { type Input<'a> = NaiveTime; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { date_part_time_inner(self.0, a) } @@ -180,7 +180,7 @@ impl EagerUnaryFunc for TimezoneTime { type Input<'a> = NaiveTime; type Output<'a> = NaiveTime; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { timezone_time(self.tz, a, &self.wall_time) } diff --git a/src/expr/src/scalar/func/impls/timestamp.rs b/src/expr/src/scalar/func/impls/timestamp.rs index 67101b913c809..625ce9f94f107 100644 --- a/src/expr/src/scalar/func/impls/timestamp.rs +++ b/src/expr/src/scalar/func/impls/timestamp.rs @@ -21,7 +21,7 @@ use mz_repr::adt::timestamp::{ CheckedTimestamp, MAX_PRECISION, TimestampPrecision, checked_add_with_leapsecond, checked_sub_with_leapsecond, }; -use mz_repr::{SqlColumnType, SqlScalarType, strconv}; +use mz_repr::{RowArena, SqlColumnType, SqlScalarType, strconv}; use serde::{Deserialize, Serialize}; use crate::EvalError; @@ -91,7 +91,7 @@ impl EagerUnaryFunc for CastTimestampToTimestampTz { type Input<'a> = CheckedTimestamp; type Output<'a> = Result>, EvalError>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { let out = CheckedTimestamp::try_from(DateTime::::from_naive_utc_and_offset(a.into(), Utc))?; let updated = out.round_to_precision(self.to)?; @@ -147,7 +147,7 @@ impl EagerUnaryFunc for AdjustTimestampPrecision { type Input<'a> = CheckedTimestamp; type Output<'a> = Result, EvalError>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { // This should never have been called if precisions are same. // Adding a soft-assert to flag if there are such instances. mz_ore::soft_assert_no_log!(self.to != self.from); @@ -202,7 +202,7 @@ impl EagerUnaryFunc for CastTimestampTzToTimestamp { type Input<'a> = CheckedTimestamp>; type Output<'a> = Result, EvalError>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { let out = CheckedTimestamp::try_from(a.naive_utc())?; let updated = out.round_to_precision(self.to)?; Ok(updated) @@ -257,7 +257,7 @@ impl EagerUnaryFunc for AdjustTimestampTzPrecision { type Input<'a> = CheckedTimestamp>; type Output<'a> = Result>, EvalError>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { // This should never have been called if precisions are same. // Adding a soft-assert to flag if there are such instances. mz_ore::soft_assert_no_log!(self.to != self.from); @@ -354,7 +354,7 @@ impl EagerUnaryFunc for ExtractInterval { type Input<'a> = Interval; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { date_part_interval_inner(self.0, a) } @@ -386,7 +386,7 @@ impl EagerUnaryFunc for DatePartInterval { type Input<'a> = Interval; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { date_part_interval_inner(self.0, a) } @@ -464,7 +464,7 @@ impl EagerUnaryFunc for ExtractTimestamp { type Input<'a> = CheckedTimestamp; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { date_part_timestamp_inner(self.0, &*a) } @@ -500,7 +500,7 @@ impl EagerUnaryFunc for ExtractTimestampTz { type Input<'a> = CheckedTimestamp>; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { date_part_timestamp_inner(self.0, &*a) } @@ -539,7 +539,7 @@ impl EagerUnaryFunc for DatePartTimestamp { type Input<'a> = CheckedTimestamp; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { date_part_timestamp_inner(self.0, &*a) } @@ -571,7 +571,7 @@ impl EagerUnaryFunc for DatePartTimestampTz { type Input<'a> = CheckedTimestamp>; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { date_part_timestamp_inner(self.0, &*a) } @@ -632,7 +632,7 @@ impl EagerUnaryFunc for DateTruncTimestamp { type Input<'a> = CheckedTimestamp; type Output<'a> = Result, EvalError>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { date_trunc_inner(self.0, &*a)?.try_into().err_into() } @@ -668,7 +668,7 @@ impl EagerUnaryFunc for DateTruncTimestampTz { type Input<'a> = CheckedTimestamp>; type Output<'a> = Result>, EvalError>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { date_trunc_inner(self.0, &*a)?.try_into().err_into() } @@ -748,7 +748,7 @@ impl EagerUnaryFunc for TimezoneTimestamp { type Input<'a> = CheckedTimestamp; type Output<'a> = Result>, EvalError>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { timezone_timestamp(self.0, a.to_naive()) } @@ -780,7 +780,7 @@ impl EagerUnaryFunc for TimezoneTimestampTz { type Input<'a> = CheckedTimestamp>; type Output<'a> = Result, EvalError>; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { timezone_timestamptz(self.0, a.into())? .try_into() .err_into() @@ -817,7 +817,7 @@ impl EagerUnaryFunc for ToCharTimestamp { type Input<'a> = CheckedTimestamp; type Output<'a> = String; - fn call<'a>(&self, input: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, input: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { self.format.render(&*input) } @@ -852,7 +852,7 @@ impl EagerUnaryFunc for ToCharTimestampTz { type Input<'a> = CheckedTimestamp>; type Output<'a> = String; - fn call<'a>(&self, input: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, input: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { self.format.render(&*input) } diff --git a/src/expr/src/scalar/func/impls/uint16.rs b/src/expr/src/scalar/func/impls/uint16.rs index afa6f6d0d9c94..c386835620ac0 100644 --- a/src/expr/src/scalar/func/impls/uint16.rs +++ b/src/expr/src/scalar/func/impls/uint16.rs @@ -11,7 +11,7 @@ use std::fmt; use mz_expr_derive::sqlfunc; use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale}; -use mz_repr::{SqlColumnType, SqlScalarType, strconv}; +use mz_repr::{RowArena, SqlColumnType, SqlScalarType, strconv}; use serde::{Deserialize, Serialize}; use crate::EvalError; @@ -123,7 +123,7 @@ impl EagerUnaryFunc for CastUint16ToNumeric { type Input<'a> = u16; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { let mut a = Numeric::from(i32::from(a)); if let Some(scale) = self.0 { if numeric::rescale(&mut a, scale.into_u8()).is_err() { diff --git a/src/expr/src/scalar/func/impls/uint32.rs b/src/expr/src/scalar/func/impls/uint32.rs index 24d4d456136e1..0c42de90c4ddf 100644 --- a/src/expr/src/scalar/func/impls/uint32.rs +++ b/src/expr/src/scalar/func/impls/uint32.rs @@ -11,7 +11,7 @@ use std::fmt; use mz_expr_derive::sqlfunc; use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale}; -use mz_repr::{SqlColumnType, SqlScalarType, strconv}; +use mz_repr::{RowArena, SqlColumnType, SqlScalarType, strconv}; use serde::{Deserialize, Serialize}; use crate::EvalError; @@ -134,7 +134,7 @@ impl EagerUnaryFunc for CastUint32ToNumeric { type Input<'a> = u32; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { let mut a = Numeric::from(a); if let Some(scale) = self.0 { if numeric::rescale(&mut a, scale.into_u8()).is_err() { diff --git a/src/expr/src/scalar/func/impls/uint64.rs b/src/expr/src/scalar/func/impls/uint64.rs index 86ad5cee67da3..966f9dca9fcd8 100644 --- a/src/expr/src/scalar/func/impls/uint64.rs +++ b/src/expr/src/scalar/func/impls/uint64.rs @@ -11,7 +11,7 @@ use std::fmt; use mz_expr_derive::sqlfunc; use mz_repr::adt::numeric::{self, Numeric, NumericMaxScale}; -use mz_repr::{SqlColumnType, SqlScalarType, strconv}; +use mz_repr::{RowArena, SqlColumnType, SqlScalarType, strconv}; use serde::{Deserialize, Serialize}; use crate::EvalError; @@ -132,7 +132,7 @@ impl EagerUnaryFunc for CastUint64ToNumeric { type Input<'a> = u64; type Output<'a> = Result; - fn call<'a>(&self, a: Self::Input<'a>) -> Self::Output<'a> { + fn call<'a>(&self, a: Self::Input<'a>, _temp_storage: &'a RowArena) -> Self::Output<'a> { let mut a = Numeric::from(a); if let Some(scale) = self.0 { if numeric::rescale(&mut a, scale.into_u8()).is_err() { diff --git a/src/expr/src/scalar/func/unary.rs b/src/expr/src/scalar/func/unary.rs index 60c7eb268da11..7783b6be946c9 100644 --- a/src/expr/src/scalar/func/unary.rs +++ b/src/expr/src/scalar/func/unary.rs @@ -111,7 +111,7 @@ pub trait EagerUnaryFunc { type Input<'a>: InputDatumType<'a, EvalError>; type Output<'a>: OutputDatumType<'a, EvalError>; - fn call<'a>(&self, input: Self::Input<'a>) -> Self::Output<'a>; + fn call<'a>(&self, input: Self::Input<'a>, temp_storage: &'a RowArena) -> Self::Output<'a>; /// The output SqlColumnType of this function fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType; @@ -165,7 +165,7 @@ impl LazyUnaryFunc for T { ) -> Result, EvalError> { match T::Input::<'_>::try_from_result(a.eval(datums, temp_storage)) { // If we can convert to the input type then we call the function - Ok(input) => self.call(input).into_result(temp_storage), + Ok(input) => self.call(input, temp_storage).into_result(temp_storage), // If we can't and we got a non-null datum something went wrong in the planner Err(Ok(datum)) if !datum.is_null() => { Err(EvalError::Internal("invalid input type".into())) From f3264dbfb5a888c14183af8d4f26a300bbe139cf Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Fri, 18 Sep 2026 17:15:04 +0200 Subject: [PATCH 02/19] expr-derive-impl: Accept stateful unary and binary functions `#[sqlfunc(StructName, ..)]` on a function with a `&self` receiver now works for unary and binary, as it already did for variadic. The macro attaches an inherent method and the trait impl to the existing struct instead of defining a unit struct. Both arms previously indexed arguments from position zero, so the receiver reached `arg_type` and produced `compile_error!("Unsupported argument type")`. Because the arities share one generator, this is an offset and a call expression rather than two copies of the variadic logic. Also inlines `Shape::takes_arena`, which had become a constant-valued method with a single call site now that every arity takes the arena, and drops the test that looped over shapes asserting it. Co-Authored-By: Claude Opus 5 (1M context) --- src/expr-derive-impl/src/generate.rs | 19 +++--- src/expr-derive-impl/src/lib.rs | 26 +++++++ src/expr-derive-impl/src/shape.rs | 9 --- ...z_expr_derive_impl__test__binary_self.snap | 67 +++++++++++++++++++ ...mz_expr_derive_impl__test__unary_self.snap | 56 ++++++++++++++++ src/expr-derive-impl/src/sqlfunc.rs | 16 +++-- 6 files changed, 170 insertions(+), 23 deletions(-) create mode 100644 src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__binary_self.snap create mode 100644 src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_self.snap diff --git a/src/expr-derive-impl/src/generate.rs b/src/expr-derive-impl/src/generate.rs index 8203dca714909..5de9ea2619b2e 100644 --- a/src/expr-derive-impl/src/generate.rs +++ b/src/expr-derive-impl/src/generate.rs @@ -183,14 +183,19 @@ pub(crate) fn generate( let generic_params = signature::find_generic_type_params(func); // Unary and binary bind their inputs positionally, ignoring how the function - // spells its parameters, while variadic forwards the real names. + // spells its parameters, while variadic forwards the real names. A `&self` + // receiver occupies position 0, so it offsets where the bound arguments start. + let self_offset = usize::from(has_self); let (param_types_raw, param_names) = match shape { Shape::Unary => ( - vec![signature::arg_type(func, 0)?], + vec![signature::arg_type(func, self_offset)?], vec![Ident::new("a", Span::call_site())], ), Shape::Binary => ( - vec![signature::arg_type(func, 0)?, signature::arg_type(func, 1)?], + vec![ + signature::arg_type(func, self_offset)?, + signature::arg_type(func, self_offset + 1)?, + ], vec![ Ident::new("a", Span::call_site()), Ident::new("b", Span::call_site()), @@ -283,11 +288,9 @@ pub(crate) fn generate( let methods = override_methods(shape, &mods, introduces_nulls_fn); - let arena_param = if shape.takes_arena() { - quote! { , temp_storage: &'a mz_repr::RowArena } - } else { - quote! {} - }; + // Every arity's `call` receives the arena; only `arena` (whether the annotated + // function itself wants it) varies. + let arena_param = quote! { , temp_storage: &'a mz_repr::RowArena }; let arena_arg = if arena { quote! { , temp_storage } } else { diff --git a/src/expr-derive-impl/src/lib.rs b/src/expr-derive-impl/src/lib.rs index 420ed7af92d13..71f527fc58f70 100644 --- a/src/expr-derive-impl/src/lib.rs +++ b/src/expr-derive-impl/src/lib.rs @@ -408,4 +408,30 @@ mod test { "is_infinity_monotone must be rejected on unary rather than ignored, got:\n{output}" ); } + + #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri + #[mz_ore::test] + fn insta_test_unary_self() { + let attr = quote! { PadTo, sqlname = "pad_to", test = true }; + let item = quote! { + fn pad_to<'a>(&self, a: &'a str, temp_storage: &'a RowArena) -> &'a str { + temp_storage.push_string(format!("{a:width$}", width = self.width)) + } + }; + let (output, input) = crate::test_sqlfunc(attr, item); + insta::assert_snapshot!("unary_self", output, &input); + } + + #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri + #[mz_ore::test] + fn insta_test_binary_self() { + let attr = quote! { ClampAt, sqlname = "clamp_at", is_infix_op = false, test = true }; + let item = quote! { + fn clamp_at<'a>(&self, a: i64, b: i64) -> i64 { + a.clamp(self.lower, b) + } + }; + let (output, input) = crate::test_sqlfunc(attr, item); + insta::assert_snapshot!("binary_self", output, &input); + } } diff --git a/src/expr-derive-impl/src/shape.rs b/src/expr-derive-impl/src/shape.rs index aed3c4a51678d..55fe80669d548 100644 --- a/src/expr-derive-impl/src/shape.rs +++ b/src/expr-derive-impl/src/shape.rs @@ -168,15 +168,6 @@ impl Shape { } } - /// Whether the trait's `call` receives a `&'a RowArena`. - pub(crate) fn takes_arena(&self) -> bool { - // Exhaustive so each arity states its own answer instead of inheriting a - // constant. - match self { - Shape::Unary | Shape::Binary | Shape::Variadic => true, - } - } - /// The tail of the output-type method, which sets the nullability of the /// `output` column type the caller computed. /// diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__binary_self.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__binary_self.snap new file mode 100644 index 0000000000000..e2898a9be1901 --- /dev/null +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__binary_self.snap @@ -0,0 +1,67 @@ +--- +source: src/expr-derive-impl/src/lib.rs +expression: "#[sqlfunc(ClampAt, sqlname = \"clamp_at\", is_infix_op = false, test = true)]\nfn clamp_at<'a>(&self, a: i64, b: i64) -> i64 {\n a.clamp(self.lower, b)\n}\n" +--- +impl ClampAt { + fn clamp_at<'a>(&self, a: i64, b: i64) -> i64 { + a.clamp(self.lower, b) + } +} +impl crate::func::binary::EagerBinaryFunc for ClampAt { + type Input<'a> = (i64, i64); + type Output<'a> = i64; + fn call<'a>( + &self, + (a, b): Self::Input<'a>, + temp_storage: &'a mz_repr::RowArena, + ) -> Self::Output<'a> { + self.clamp_at(a, b) + } + fn output_sql_type( + &self, + input_types: &[mz_repr::SqlColumnType], + ) -> mz_repr::SqlColumnType { + use mz_repr::AsColumnType; + let output = Self::Output::as_column_type(); + let propagates_nulls = crate::func::binary::EagerBinaryFunc::propagates_nulls( + self, + ); + let nullable = output.nullable; + let non_nullable_input_is_nullable = false + || input_types.get(0usize).map_or(false, |t| t.nullable) + || input_types.get(1usize).map_or(false, |t| t.nullable); + let inputs_nullable = input_types.iter().any(|it| it.nullable); + let is_null = nullable || non_nullable_input_is_nullable + || (propagates_nulls && inputs_nullable); + output.nullable(is_null) + } + fn is_infix_op(&self) -> bool { + false + } +} +impl std::fmt::Display for ClampAt { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str("clamp_at") + } +} +impl crate::func::FuncName for ClampAt { + const NAME: &'static str = stringify!(clamp_at); + #[cfg(feature = "func-registry")] + const SQLFUNC: Option = Some(crate::func::SqlFuncSource { + decl: "#[sqlfunc(ClampAt, sqlname = \"clamp_at\", is_infix_op = false, test = true)] fn clamp_at<'a>(&self, a: i64, b: i64) -> i64", + signature: "fn(i64, i64) -> i64", + body_fingerprint: 6685180180282358271u64, + }); + #[cfg(feature = "func-registry")] + fn sqlfunc_input_types() -> Option> { + use crate::func::registry::{ProbeColumnType as _, ProbeColumnTypeFallback as _}; + [ + (&crate::func::registry::ColumnTypeProbe::(::std::marker::PhantomData)) + .column_type(), + (&crate::func::registry::ColumnTypeProbe::(::std::marker::PhantomData)) + .column_type(), + ] + .into_iter() + .collect() + } +} diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_self.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_self.snap new file mode 100644 index 0000000000000..1ce9e1444377e --- /dev/null +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_self.snap @@ -0,0 +1,56 @@ +--- +source: src/expr-derive-impl/src/lib.rs +expression: "#[sqlfunc(PadTo, sqlname = \"pad_to\", test = true)]\nfn pad_to<'a>(&self, a: &'a str, temp_storage: &'a RowArena) -> &'a str {\n temp_storage.push_string(format!(\"{a:width$}\", width = self.width))\n}\n" +--- +impl PadTo { + fn pad_to<'a>(&self, a: &'a str, temp_storage: &'a RowArena) -> &'a str { + temp_storage.push_string(format!("{a:width$}", width = self.width)) + } +} +impl crate::func::EagerUnaryFunc for PadTo { + type Input<'a> = &'a str; + type Output<'a> = &'a str; + fn call<'a>( + &self, + a: Self::Input<'a>, + temp_storage: &'a mz_repr::RowArena, + ) -> Self::Output<'a> { + self.pad_to(a, temp_storage) + } + fn output_sql_type( + &self, + input_type: mz_repr::SqlColumnType, + ) -> mz_repr::SqlColumnType { + use mz_repr::AsColumnType; + let output = Self::Output::as_column_type(); + let propagates_nulls = crate::func::EagerUnaryFunc::propagates_nulls(self); + let nullable = output.nullable; + output.nullable(nullable || (propagates_nulls && input_type.nullable)) + } +} +impl std::fmt::Display for PadTo { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str("pad_to") + } +} +impl crate::func::FuncName for PadTo { + const NAME: &'static str = stringify!(pad_to); + #[cfg(feature = "func-registry")] + const SQLFUNC: Option = Some(crate::func::SqlFuncSource { + decl: "#[sqlfunc(PadTo, sqlname = \"pad_to\", test = true)] fn pad_to<'a>(&self, a: &'a str, temp_storage: &'a RowArena) -> &'a str", + signature: "fn(&'a str) -> &'a str", + body_fingerprint: 9097626259777403017u64, + }); + #[cfg(feature = "func-registry")] + fn sqlfunc_input_types() -> Option> { + use crate::func::registry::{ProbeColumnType as _, ProbeColumnTypeFallback as _}; + [ + (&crate::func::registry::ColumnTypeProbe::< + &'static str, + >(::std::marker::PhantomData)) + .column_type(), + ] + .into_iter() + .collect() + } +} diff --git a/src/expr-derive-impl/src/sqlfunc.rs b/src/expr-derive-impl/src/sqlfunc.rs index ff92db8bad607..17b691b1ec618 100644 --- a/src/expr-derive-impl/src/sqlfunc.rs +++ b/src/expr-derive-impl/src/sqlfunc.rs @@ -49,8 +49,12 @@ pub fn sqlfunc( let tokens = match determine_arity(&func) { Arity::Nullary => Err(darling::Error::custom("Nullary functions not supported")), - Arity::Unary => generate(Shape::Unary, &func, modifiers, None, false, &attr), - Arity::Binary => generate(Shape::Binary, &func, modifiers, None, false, &attr), + Arity::Unary { has_self } => { + generate(Shape::Unary, &func, modifiers, struct_ty, has_self, &attr) + } + Arity::Binary { has_self } => { + generate(Shape::Binary, &func, modifiers, struct_ty, has_self, &attr) + } Arity::Variadic { has_self } => generate( Shape::Variadic, &func, @@ -95,8 +99,8 @@ fn generate_test(_attr: TokenStream, _item: TokenStream, _name: &Ident) -> Token /// Arity classification for a function annotated with `#[sqlfunc]`. enum Arity { Nullary, - Unary, - Binary, + Unary { has_self: bool }, + Binary { has_self: bool }, Variadic { has_self: bool }, } @@ -152,8 +156,8 @@ fn determine_arity(func: &syn::ItemFn) -> Arity { } else { match effective_count { 0 => Arity::Nullary, - 1 => Arity::Unary, - 2 => Arity::Binary, + 1 => Arity::Unary { has_self }, + 2 => Arity::Binary { has_self }, _ => unreachable!(), } } From d4d260fe1e1faaa20b4196ab71e17b3f30e024c7 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Fri, 18 Sep 2026 17:18:16 +0200 Subject: [PATCH 03/19] expr-derive-impl: Fix comments claiming struct_ty is variadic-only generate's doc comment and the attribute-parsing comment in sqlfunc both described the struct-name argument as exclusive to variadic dispatch. Any arity accepts one. Co-Authored-By: Claude Opus 5 (1M context) --- src/expr-derive-impl/src/generate.rs | 6 +++--- src/expr-derive-impl/src/sqlfunc.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/expr-derive-impl/src/generate.rs b/src/expr-derive-impl/src/generate.rs index 5de9ea2619b2e..a7ba3cff96dd9 100644 --- a/src/expr-derive-impl/src/generate.rs +++ b/src/expr-derive-impl/src/generate.rs @@ -157,9 +157,9 @@ fn variadic_params( /// Generates the trait impl for one `#[sqlfunc]`-annotated function, together with the /// items [`expand`] wraps around it. /// -/// `struct_ty` names the generated struct when the call site spells one, which only -/// the variadic arity accepts. `has_self` says the annotated function takes a -/// receiver, so the trait's `call` dispatches through it. +/// `struct_ty` names the generated struct when the call site spells one. `has_self` +/// says the annotated function takes a receiver, so the trait's `call` dispatches +/// through it. pub(crate) fn generate( shape: Shape, func: &syn::ItemFn, diff --git a/src/expr-derive-impl/src/sqlfunc.rs b/src/expr-derive-impl/src/sqlfunc.rs index 17b691b1ec618..52f05099b7124 100644 --- a/src/expr-derive-impl/src/sqlfunc.rs +++ b/src/expr-derive-impl/src/sqlfunc.rs @@ -32,7 +32,7 @@ pub fn sqlfunc( ) -> darling::Result { let mut attr_args = darling::ast::NestedMeta::parse_meta_list(attr.clone())?; - // Check if the first attribute arg is a bare Path (struct name for variadic). + // Check if the first attribute arg is a bare Path (struct name). let struct_ty = match attr_args.first() { Some(darling::ast::NestedMeta::Meta(syn::Meta::Path(_))) => { let darling::ast::NestedMeta::Meta(syn::Meta::Path(path)) = attr_args.remove(0) else { From 9867164433a6f63280d3f2fbd68ba147d23e9832 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Fri, 18 Sep 2026 17:23:18 +0200 Subject: [PATCH 04/19] expr-derive-impl: Add a skip_display modifier Twenty-four of the hand-written scalar functions format their name from struct state, for example `extract_{unit}_ts`. Suppressing the generated `Display` lets them keep the impl they already have, which is simpler than teaching `sqlname` to evaluate an expression with `self` in scope. Co-Authored-By: Claude Opus 5 (1M context) --- src/expr-derive-impl/src/generate.rs | 21 +++++--- src/expr-derive-impl/src/lib.rs | 17 +++++++ src/expr-derive-impl/src/modifiers.rs | 8 +++ src/expr-derive-impl/src/shape.rs | 4 +- ..._expr_derive_impl__test__skip_display.snap | 49 +++++++++++++++++++ 5 files changed, 91 insertions(+), 8 deletions(-) create mode 100644 src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__skip_display.snap diff --git a/src/expr-derive-impl/src/generate.rs b/src/expr-derive-impl/src/generate.rs index a7ba3cff96dd9..872a425770111 100644 --- a/src/expr-derive-impl/src/generate.rs +++ b/src/expr-derive-impl/src/generate.rs @@ -61,11 +61,14 @@ struct Codegen { /// The registry's record of this function's source, generated as members of the /// `FuncName` impl. source: TokenStream, + /// Suppresses the generated `Display` impl, so a call site whose SQL name + /// depends on struct state can supply its own. + skip_display: bool, } /// Wraps an arity's trait impl with the parts every arity shares: the unit-struct -/// definition (unless `has_self`), the `Display` impl, the `FuncName` impl, and the -/// annotated function itself. +/// definition (unless `has_self`), the `Display` impl (unless `skip_display`), the +/// `FuncName` impl, and the annotated function itself. fn expand(e: &Codegen, func: &syn::ItemFn, trait_impl: TokenStream) -> TokenStream { let func_name = &func.sig.ident; let Codegen { @@ -73,12 +76,17 @@ fn expand(e: &Codegen, func: &syn::ItemFn, trait_impl: TokenStream) -> TokenStre has_self, sqlname, source, + skip_display, } = e; - let display_impl = quote! { - impl std::fmt::Display for #struct_name { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.write_str(#sqlname) + let display_impl = if *skip_display { + quote! {} + } else { + quote! { + impl std::fmt::Display for #struct_name { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(#sqlname) + } } } }; @@ -342,6 +350,7 @@ pub(crate) fn generate( has_self, sqlname, source: sqlfunc_source(attr, func, ¶m_types_raw, output_ty_raw, ¶m_types), + skip_display: mods.skip_display(), }; Ok(expand(&codegen, func, trait_impl)) } diff --git a/src/expr-derive-impl/src/lib.rs b/src/expr-derive-impl/src/lib.rs index 71f527fc58f70..d34ef0f6d55c6 100644 --- a/src/expr-derive-impl/src/lib.rs +++ b/src/expr-derive-impl/src/lib.rs @@ -434,4 +434,21 @@ mod test { let (output, input) = crate::test_sqlfunc(attr, item); insta::assert_snapshot!("binary_self", output, &input); } + + #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri + #[mz_ore::test] + fn insta_test_skip_display() { + let attr = quote! { ExtractThing, skip_display = true, test = true }; + let item = quote! { + fn extract_thing<'a>(&self, a: i64) -> i64 { + a + self.offset + } + }; + let (output, input) = crate::test_sqlfunc(attr, item); + assert!( + !output.contains("impl std :: fmt :: Display"), + "skip_display must suppress the Display impl, got:\n{output}" + ); + insta::assert_snapshot!("skip_display", output, &input); + } } diff --git a/src/expr-derive-impl/src/modifiers.rs b/src/expr-derive-impl/src/modifiers.rs index 9b80017465d9e..4a1f8907a8414 100644 --- a/src/expr-derive-impl/src/modifiers.rs +++ b/src/expr-derive-impl/src/modifiers.rs @@ -57,6 +57,9 @@ pub(crate) struct Modifiers { is_eliminable_cast: Option, /// Whether to generate a snapshot test for the function. Defaults to false. test: Option, + /// Suppresses the generated `fmt::Display`. Set this when the struct's name + /// depends on its state, so the call site keeps a hand-written impl. + skip_display: Option, } impl Modifiers { @@ -89,6 +92,11 @@ impl Modifiers { self.introduces_nulls.as_ref() } + /// Whether the call site asked to suppress the generated `Display` impl. + pub(crate) fn skip_display(&self) -> bool { + self.skip_display.unwrap_or(false) + } + /// The method-producing modifiers that are present, in this method's own fixed /// order, which does not match any of the three per-arity tables in /// `crate::shape`. A caller that needs table order joins against diff --git a/src/expr-derive-impl/src/shape.rs b/src/expr-derive-impl/src/shape.rs index 55fe80669d548..38ea235eb33bb 100644 --- a/src/expr-derive-impl/src/shape.rs +++ b/src/expr-derive-impl/src/shape.rs @@ -26,8 +26,8 @@ pub(crate) enum Shape { /// A modifier that maps directly onto one optional trait method. /// /// Modifiers that do not produce a trait method, such as `sqlname`, `output_type`, -/// `output_type_expr`, and `test`, are absent: `crate::generate::generate` handles -/// those explicitly because they feed `Display`, the output-type body, or the +/// `output_type_expr`, `test`, and `skip_display`, are absent: `crate::generate::generate` +/// handles those explicitly because they feed `Display`, the output-type body, or the /// expansion decision instead. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum Modifier { diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__skip_display.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__skip_display.snap new file mode 100644 index 0000000000000..d3100211a17a6 --- /dev/null +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__skip_display.snap @@ -0,0 +1,49 @@ +--- +source: src/expr-derive-impl/src/lib.rs +expression: "#[sqlfunc(ExtractThing, skip_display = true, test = true)]\nfn extract_thing<'a>(&self, a: i64) -> i64 {\n a + self.offset\n}\n" +--- +impl ExtractThing { + fn extract_thing<'a>(&self, a: i64) -> i64 { + a + self.offset + } +} +impl crate::func::EagerUnaryFunc for ExtractThing { + type Input<'a> = i64; + type Output<'a> = i64; + fn call<'a>( + &self, + a: Self::Input<'a>, + temp_storage: &'a mz_repr::RowArena, + ) -> Self::Output<'a> { + self.extract_thing(a) + } + fn output_sql_type( + &self, + input_type: mz_repr::SqlColumnType, + ) -> mz_repr::SqlColumnType { + use mz_repr::AsColumnType; + let output = Self::Output::as_column_type(); + let propagates_nulls = crate::func::EagerUnaryFunc::propagates_nulls(self); + let nullable = output.nullable; + output.nullable(nullable || (propagates_nulls && input_type.nullable)) + } +} +impl crate::func::FuncName for ExtractThing { + const NAME: &'static str = stringify!(extract_thing); + #[cfg(feature = "func-registry")] + const SQLFUNC: Option = Some(crate::func::SqlFuncSource { + decl: "#[sqlfunc(ExtractThing, skip_display = true, test = true)] fn extract_thing<'a>(&self, a: i64) -> i64", + signature: "fn(i64) -> i64", + body_fingerprint: 12939012847358732198u64, + }); + #[cfg(feature = "func-registry")] + fn sqlfunc_input_types() -> Option> { + use crate::func::registry::{ProbeColumnType as _, ProbeColumnTypeFallback as _}; + [ + (&crate::func::registry::ColumnTypeProbe::(::std::marker::PhantomData)) + .column_type(), + ] + .into_iter() + .collect() + } +} From fb05bb2327e3fbf6d62067dd3aec021042def493 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Fri, 18 Sep 2026 17:29:49 +0200 Subject: [PATCH 05/19] expr-derive-impl: Fix skip_display test to match prettyplease output The insta_test_skip_display assert checked for "impl std :: fmt :: Display" with spaces around the double colons, the raw TokenStream::to_string() idiom. But test_sqlfunc formats output through prettyplease, which never spaces double colons, so the assert could never fire and only the snapshot enforced suppression. Match the unspaced form prettyplease actually produces. Co-Authored-By: Claude Opus 5 (1M context) --- src/expr-derive-impl/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/expr-derive-impl/src/lib.rs b/src/expr-derive-impl/src/lib.rs index d34ef0f6d55c6..b12462465448a 100644 --- a/src/expr-derive-impl/src/lib.rs +++ b/src/expr-derive-impl/src/lib.rs @@ -446,7 +446,7 @@ mod test { }; let (output, input) = crate::test_sqlfunc(attr, item); assert!( - !output.contains("impl std :: fmt :: Display"), + !output.contains("impl std::fmt::Display"), "skip_display must suppress the Display impl, got:\n{output}" ); insta::assert_snapshot!("skip_display", output, &input); From 86e5b433868aa0760d8229babf8ee9ed2d961009 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Sat, 19 Sep 2026 22:30:02 +0200 Subject: [PATCH 06/19] expr: Convert RangeCreate to the sqlfunc macro `RangeCreate` carries an `elem_type` field, so it needs the stateful shape the macro now accepts: the function takes `&self` and the macro generates the `call` body as a method on the existing struct. Its `Display` impl picks a name from `elem_type` rather than using a fixed string, so `skip_display` keeps the hand-written impl and the macro generates only `FuncName`. That removes the manual `func_name!` entry, which would otherwise collide with the generated impl. The output type is expressed with `output_type_expr` because it reads `elem_type` and ignores the input types, matching the hand-written body. The function returns `Range>` rather than packing the range itself, so it needs no arena and `introduces_nulls` derives the `false` the impl declared. The remaining trait methods derive the same values from the signature that they derived from the associated types before, so the generated impl matches. Also corrects two stale claims in the macro documentation: unary functions now accept a `&RowArena` and a `&self` receiver, and `output_type_expr` applies to all three arities. Co-Authored-By: Claude Opus 5 (1M context) --- doc/developer/sqlfunc.md | 33 +++++++-- .../tests/snapshots/func_registry.json | 2 +- .../snapshots/func_registry_digests.json | 2 +- .../tests/snapshots/func_registry_source.json | 2 +- src/expr-derive/src/lib.rs | 18 ++--- src/expr/src/scalar/func.rs | 1 - src/expr/src/scalar/func/variadic.rs | 68 ++++++++----------- 7 files changed, 71 insertions(+), 55 deletions(-) diff --git a/doc/developer/sqlfunc.md b/doc/developer/sqlfunc.md index d98d3e4e5c710..d6f669c1a5595 100644 --- a/doc/developer/sqlfunc.md +++ b/doc/developer/sqlfunc.md @@ -31,8 +31,8 @@ The macro determines function arity from parameter count and types, after exclud | Effective params | Dispatches to | Notes | |---|---|---| | 0 | Error | Nullary functions are not supported. | -| 1 | `EagerUnaryFunc` | Does not support `&RowArena`. | -| 2 | `EagerBinaryFunc` | Supports `&RowArena`. | +| 1 | `EagerUnaryFunc` | Supports `&RowArena` and `&self`. | +| 2 | `EagerBinaryFunc` | Supports `&RowArena` and `&self`. | | 3+ | `EagerVariadicFunc` | Supports `&RowArena` and `&self`. | **Exception:** If any parameter uses `Variadic` or `OptionalArg`, the function is always treated as variadic, regardless of parameter count. @@ -184,6 +184,18 @@ Generate a snapshot test for the macro expansion. Snapshot files are stored in `src/expr-derive-impl/src/snapshots/`. Update them with `cargo insta accept` after running `cargo test -p mz-expr-derive-impl`. +### `skip_display` + +Suppresses the generated `Display` impl. Use this when the struct's `sqlname` depends +on its own state rather than being a fixed string, so the call site keeps a +hand-written `Display` impl. `RangeCreate` is an example: it picks between +`int4range`, `int8range`, `daterange`, `numrange`, `tsrange`, and `tstzrange` based on +its `elem_type` field. + +* **Type:** `bool` +* **Default:** `false` +* **Applies to:** all arities + Which modifiers apply to which arity is declared in `src/expr-derive-impl/src/shape.rs`, one table per arity. A modifier absent from an arity's table is rejected with an error naming both the modifier and the arity. @@ -253,9 +265,7 @@ Both are defined in `src/repr/src/scalar.rs`. A trailing `&RowArena` parameter gives the function access to temporary storage for allocating return values that borrow from the arena. It is excluded from arity detection and from the generated `Input` type. -The arena is always passed to binary and variadic `call` implementations (the trait requires it); for functions that don't use it, the parameter is simply unused. - -Unary functions do not support `&RowArena`. +The arena is always passed to unary, binary, and variadic `call` implementations (the trait requires it); for functions that don't use it, the parameter is simply unused. ### Input type @@ -348,3 +358,16 @@ fn array_create<'a>(&self, datums: Variadic>, temp_storage: &'a RowAre // output_type_expr: output type depends on runtime struct fields } ``` + +## Shapes the macro does not cover + +Two shapes stay hand-written by design. + +* Functions generic over an `Eval` implementor, which hold sub-expressions in + `Box` and evaluate them per element. The macro would need to emit an + implementation generic over a struct type parameter with a trait bound, which is a + different mechanism from the type-parameter erasure it applies today. The nine + compound-type casts under `src/expr/src/scalar/func/impls/` are these. +* Functions that do not evaluate every operand. The macro emits `Eager*` + implementations, which evaluate all arguments before dispatch. `And`, `Or`, + `Coalesce`, `Greatest`, `Least`, `ErrorIfNull`, and `CaseLiteral` are these. diff --git a/src/compute-types/tests/snapshots/func_registry.json b/src/compute-types/tests/snapshots/func_registry.json index 40d22170943a0..2dd86a5685095 100644 --- a/src/compute-types/tests/snapshots/func_registry.json +++ b/src/compute-types/tests/snapshots/func_registry.json @@ -619,7 +619,7 @@ "map_build": {"variant":"MapBuild","propagates_nulls":false,"introduces_nulls":false,"could_error":true,"is_monotone":false,"is_associative":false,"is_infix_op":false,"input_types":["String:NotNull","Int32:NotNull"],"output_type":"Map { value_type: Int32, custom_id: None }:NotNull","sqlfunc_signature":"fn(Variadic<(Option<&'a str>, Datum<'a>)>) -> Result, EvalError>"}, "or": {"variant":"Or","propagates_nulls":false,"introduces_nulls":false,"could_error":false,"is_monotone":true,"is_associative":true,"is_infix_op":true,"input_types":["Bool:NotNull","Bool:NotNull"],"output_type":"Bool:NotNull","sqlfunc_signature":null}, "pad_leading": {"variant":"PadLeading","propagates_nulls":true,"introduces_nulls":false,"could_error":true,"is_monotone":false,"is_associative":false,"is_infix_op":false,"input_types":["String:NotNull","Int32:NotNull","String:NotNull"],"output_type":"String:NotNull","sqlfunc_signature":"fn(&'a str, i32, OptionalArg<&'a str>) -> Result"}, - "range_create": {"variant":"RangeCreate","propagates_nulls":false,"introduces_nulls":false,"could_error":true,"is_monotone":false,"is_associative":false,"is_infix_op":false,"input_types":["Int32:NotNull","Int32:NotNull","String:NotNull"],"output_type":"Range { element_type: Int32 }:NotNull","sqlfunc_signature":null}, + "range_create": {"variant":"RangeCreate","propagates_nulls":false,"introduces_nulls":false,"could_error":true,"is_monotone":false,"is_associative":false,"is_infix_op":false,"input_types":["Int32:NotNull","Int32:NotNull","String:NotNull"],"output_type":"Range { element_type: Int32 }:NotNull","sqlfunc_signature":"fn(Datum<'a>, Datum<'a>, Datum<'a>) -> Result, EvalError>"}, "record_create": {"variant":"RecordCreate","propagates_nulls":false,"introduces_nulls":false,"could_error":true,"is_monotone":false,"is_associative":false,"is_infix_op":false,"input_types":["Int32:NotNull"],"output_type":"Record { fields: [(ColumnName(\"a\"), SqlColumnType { scalar_type: Int32, nullable: false })], custom_id: None }:NotNull","sqlfunc_signature":"fn(Variadic>) -> Result, EvalError>"}, "regexp_match": {"variant":"RegexpMatch","propagates_nulls":true,"introduces_nulls":true,"could_error":true,"is_monotone":false,"is_associative":false,"is_infix_op":false,"input_types":["String:NotNull","String:NotNull","String:NotNull"],"output_type":"Array(String):Null","sqlfunc_signature":"fn(&'a str, &'a str, OptionalArg<&'a str>) -> Result, EvalError>"}, "regexp_replace": {"variant":"RegexpReplace","propagates_nulls":true,"introduces_nulls":false,"could_error":true,"is_monotone":false,"is_associative":false,"is_infix_op":false,"input_types":["String:NotNull","String:NotNull","String:NotNull","String:NotNull"],"output_type":"String:NotNull","sqlfunc_signature":"fn(&'a str, &'a str, &'a str, OptionalArg<&'a str>) -> Result, EvalError>"}, diff --git a/src/compute-types/tests/snapshots/func_registry_digests.json b/src/compute-types/tests/snapshots/func_registry_digests.json index 5f8d4a2116229..9805e189b233d 100644 --- a/src/compute-types/tests/snapshots/func_registry_digests.json +++ b/src/compute-types/tests/snapshots/func_registry_digests.json @@ -1,3 +1,3 @@ { - "1": "sha256:6464d6eed984478e19c76a27b6cd2f5b86318b8fffabd73d0aa70d7f41a5db4b" + "1": "sha256:5ec3291615e7d51e5979e724b81697fa80cadb0faddbbd988fea5ab3aedb6748" } diff --git a/src/compute-types/tests/snapshots/func_registry_source.json b/src/compute-types/tests/snapshots/func_registry_source.json index 413670921c869..946559e2c8430 100644 --- a/src/compute-types/tests/snapshots/func_registry_source.json +++ b/src/compute-types/tests/snapshots/func_registry_source.json @@ -619,7 +619,7 @@ "map_build": {"display":"map_build","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"SqlScalarType::Map { value_type: Box::new(self.value_type.clone()), custom_id: None }.nullable(false)\", introduces_nulls = false)] fn map_build<'a>(&self, datums: Variadic<(Option<&str>, Datum<'a>)>, temp_storage: &'a RowArena,) -> Result, EvalError>","body_fingerprint":"3cf673accdd42c16"}, "or": {"display":"OR","sqlfunc_decl":null,"body_fingerprint":null}, "pad_leading": {"display":"lpad","sqlfunc_decl":"#[sqlfunc(sqlname = \"lpad\")] fn pad_leading(string: &str, raw_len: i32, pad: OptionalArg<&str>, temp_storage: &RowArena,) -> Result","body_fingerprint":"029efffef6e8305f"}, - "range_create": {"display":"int4range","sqlfunc_decl":null,"body_fingerprint":null}, + "range_create": {"display":"int4range","sqlfunc_decl":"#[sqlfunc(RangeCreate, skip_display = true, output_type_expr = \"SqlScalarType::Range { element_type: Box::new(self.elem_type.clone()) }.nullable(false)\", introduces_nulls = false)] fn range_create<'a>(&self, lower: Datum<'a>, upper: Datum<'a>, flags_datum: Datum<'a>, temp_storage: &'a RowArena,) -> Result, EvalError>","body_fingerprint":"034a866eda0ee6d4"}, "record_create": {"display":"record_create","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"SqlScalarType::Record { fields: self.field_names.clone().into_iter().zip_eq(input_types.iter().cloned()).collect(), custom_id: None }.nullable(false)\", introduces_nulls = false)] fn record_create<'a>(&self, datums: Variadic>, temp_storage: &'a RowArena,) -> Result, EvalError>","body_fingerprint":"92fe79be327ac13b"}, "regexp_match": {"display":"regexp_match","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true)\", introduces_nulls = true)] fn regexp_match<'a>(haystack: &'a str, needle: &str, flags: OptionalArg<&str>, temp_storage: &'a RowArena,) -> Result, EvalError>","body_fingerprint":"b4cc3a20cfd56b95"}, "regexp_replace": {"display":"regexp_replace","sqlfunc_decl":"#[sqlfunc] fn regexp_replace<'a>(source: &'a str, pattern: &str, replacement: &str, flags_opt: OptionalArg<&str>, temp_storage: &RowArena,) -> Result, EvalError>","body_fingerprint":"b90fa61dbc761b25"}, diff --git a/src/expr-derive/src/lib.rs b/src/expr-derive/src/lib.rs index af194b195e414..428d9f3ca51ce 100644 --- a/src/expr-derive/src/lib.rs +++ b/src/expr-derive/src/lib.rs @@ -29,13 +29,13 @@ /// * `is_infix_op`: A boolean indicating whether the function is an infix operator. Applies to /// binary functions only. /// * `output_type`: The output type of the function. -/// * `output_type_expr`: An expression that evaluates to the output type. Applies to binary -/// and variadic functions. For binary functions, the expression has access to `input_type_a` -/// and `input_type_b`; for variadic functions, it has access to `input_types: &[SqlColumnType]`. -/// Should evaluate to a `SqlColumnType` value. This expression determines the *base* output -/// type; the macro separately computes nullability by combining `introduces_nulls`, -/// `propagates_nulls`, and input nullability. Requires `introduces_nulls`, and conflicts -/// with `output_type`. +/// * `output_type_expr`: An expression that evaluates to the output type. Applies to unary, +/// binary, and variadic functions. For unary functions, the expression has access to +/// `input_type: SqlColumnType`; for binary and variadic functions, +/// `input_types: &[SqlColumnType]`. Should evaluate to a `SqlColumnType` value. This +/// expression determines the *base* output type; the macro separately computes +/// nullability by combining `introduces_nulls`, `propagates_nulls`, and input nullability. +/// Requires `introduces_nulls`, and conflicts with `output_type`. /// * `could_error`: A boolean indicating whether the function could error. /// * `propagates_nulls`: A boolean indicating whether the function propagates nulls. Applies to /// binary and variadic functions. If not specified, use the default implementation from the @@ -45,10 +45,12 @@ /// The default is to return the `nullable` property of the output type. /// * `test`: A boolean indicating whether to generate a snapshot test for the function. /// Defaults to `false`. +/// * `skip_display`: A boolean suppressing the generated `fmt::Display` impl. Set this +/// when the struct's name depends on its state, so the call site keeps a hand-written +/// `Display` impl instead. /// /// # Limitations /// * The input and output types can contain lifetime parameters, as long as they are `'a`. -/// * Unary functions cannot yet receive a `&RowArena` as an argument. /// /// # Examples /// ```ignore diff --git a/src/expr/src/scalar/func.rs b/src/expr/src/scalar/func.rs index 9c3ae37aa3212..db16018e2dd32 100644 --- a/src/expr/src/scalar/func.rs +++ b/src/expr/src/scalar/func.rs @@ -187,7 +187,6 @@ func_name! { ListLengthMax => "list_length_max", MapBuildFromRecordList => "map_build_from_record_list", PadChar => "pad_char", - variadic::RangeCreate => "range_create", RecordGet => "record_get", RegexpMatch => "regexp_match", RegexpReplace => "regexp_replace", diff --git a/src/expr/src/scalar/func/variadic.rs b/src/expr/src/scalar/func/variadic.rs index 3248a896eddb7..8a4089ac0ab88 100644 --- a/src/expr/src/scalar/func/variadic.rs +++ b/src/expr/src/scalar/func/variadic.rs @@ -598,48 +598,40 @@ impl fmt::Display for RangeCreate { } } -impl EagerVariadicFunc for RangeCreate { - type Input<'a> = (Datum<'a>, Datum<'a>, Datum<'a>); - type Output<'a> = Result, EvalError>; - - fn call<'a>( - &self, - (lower, upper, flags_datum): Self::Input<'a>, - temp_storage: &'a RowArena, - ) -> Self::Output<'a> { - let flags = match flags_datum { - Datum::Null => { - return Err(EvalError::InvalidRange( - InvalidRangeError::NullRangeBoundFlags, - )); - } - o => o.unwrap_str(), - }; - - let (lower_inclusive, upper_inclusive) = parse_range_bound_flags(flags)?; - - let mut range = Range::new(Some(( - RangeBound::new(lower, lower_inclusive), - RangeBound::new(upper, upper_inclusive), - ))); +#[sqlfunc( + RangeCreate, + skip_display = true, + output_type_expr = "SqlScalarType::Range { element_type: Box::new(self.elem_type.clone()) }.nullable(false)", + introduces_nulls = false +)] +fn range_create<'a>( + &self, + lower: Datum<'a>, + upper: Datum<'a>, + flags_datum: Datum<'a>, + temp_storage: &'a RowArena, +) -> Result, EvalError> { + let flags = match flags_datum { + Datum::Null => { + return Err(EvalError::InvalidRange( + InvalidRangeError::NullRangeBoundFlags, + )); + } + o => o.unwrap_str(), + }; - range.canonicalize()?; + let (lower_inclusive, upper_inclusive) = parse_range_bound_flags(flags)?; - Ok(temp_storage.make_datum(|row| { - row.push_range(range).expect("errors already handled"); - })) - } + let mut range = Range::new(Some(( + RangeBound::new(lower, lower_inclusive), + RangeBound::new(upper, upper_inclusive), + ))); - fn output_type(&self, _input_types: &[SqlColumnType]) -> SqlColumnType { - SqlScalarType::Range { - element_type: Box::new(self.elem_type.clone()), - } - .nullable(false) - } + range.canonicalize()?; - fn introduces_nulls(&self) -> bool { - false - } + Ok(temp_storage.make_datum(|row| { + row.push_range(range).expect("errors already handled"); + })) } #[sqlfunc(sqlname = "datediff")] From df9c0a96a3c63a498daf823149d88977fc07de3f Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Sat, 19 Sep 2026 22:40:11 +0200 Subject: [PATCH 07/19] expr-derive-impl: Correct the macro's modifier and generated-item docs Three documents describe which modifiers apply to which arity: the field docs on `Modifiers`, the macro's rustdoc, and `doc/developer/sqlfunc.md`. They had drifted from the tables in `shape.rs`, which are the actual authority. Corrects `output_type_expr` to all arities, `is_infix_op` to binary and variadic, `is_monotone` to all arities, and adds the three modifiers the rustdoc omitted entirely. Moves the struct name, `&self` receiver, and `&RowArena` sections out from under the variadic heading, since all three apply at every arity, and states that the generated struct and the generated `Display` are each conditional. Adds the `non_nullable_input_is_nullable` term to the null-handling formula. The formula omitted it, so it could not be used to reason about a function whose parameters do not all accept NULL. Co-Authored-By: Claude Opus 5 (1M context) --- doc/developer/sqlfunc.md | 38 ++++++++++++++++++++++---------------- src/expr-derive/src/lib.rs | 15 +++++++++++---- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/doc/developer/sqlfunc.md b/doc/developer/sqlfunc.md index d6f669c1a5595..7504e0a3abe4b 100644 --- a/doc/developer/sqlfunc.md +++ b/doc/developer/sqlfunc.md @@ -1,7 +1,8 @@ # The `#[sqlfunc]` macro The `#[sqlfunc]` attribute macro generates boilerplate for SQL scalar functions. -It creates a unit struct, a trait implementation (`EagerUnaryFunc`, `EagerBinaryFunc`, or `EagerVariadicFunc`), and a `Display` impl from a plain Rust function. +From a plain Rust function it creates a trait implementation (`EagerUnaryFunc`, `EagerBinaryFunc`, or `EagerVariadicFunc`), a `FuncName` implementation, and, unless the function takes a `&self` receiver, the struct itself. +It also creates a `Display` implementation unless `skip_display` suppresses it. The macro lives in `src/expr-derive-impl/src/sqlfunc.rs`. The generated trait implementations live in `src/expr/src/scalar/func/{unary,binary,variadic}.rs`. @@ -186,8 +187,8 @@ Update them with `cargo insta accept` after running `cargo test -p mz-expr-deriv ### `skip_display` -Suppresses the generated `Display` impl. Use this when the struct's `sqlname` depends -on its own state rather than being a fixed string, so the call site keeps a +Suppresses the generated `Display` impl. Use this when the displayed name depends on the +struct's own state rather than being a fixed string, so the call site keeps a hand-written `Display` impl. `RangeCreate` is an example: it picks between `int4range`, `int8range`, `daterange`, `numrange`, `tsrange`, and `tstzrange` based on its `elem_type` field. @@ -200,11 +201,11 @@ Which modifiers apply to which arity is declared in `src/expr-derive-impl/src/shape.rs`, one table per arity. A modifier absent from an arity's table is rejected with an error naming both the modifier and the arity. -## Variadic functions +## Generated items ### Struct name -For variadic functions, the struct name can be specified as the first positional argument. +The struct name can be specified as the first positional argument, at any arity. This is required when a `&self` receiver is present (the struct is defined externally): ```rust @@ -229,11 +230,19 @@ fn replace(text: &str, from: &str, to: &str) -> Result { When a `&self` receiver is present, the macro assumes the struct is defined externally and generates: * A method `impl StructName { fn ... }` containing the function body. -* An `EagerVariadicFunc` trait implementation that delegates to the method. -* A `Display` implementation. +* The `Eager*Func` trait implementation for the function's arity, delegating to the method. +* A `Display` implementation, unless `skip_display` suppresses it. Without `&self`, the macro generates the struct itself (with standard derives) in addition to the trait and display implementations. +### `&RowArena` + +A trailing `&RowArena` parameter gives the function access to temporary storage for allocating return values that borrow from the arena. +It is excluded from arity detection and from the generated `Input` type. +The arena is always passed to unary, binary, and variadic `call` implementations (the trait requires it); for functions that don't use it, the parameter is simply unused. + +## Variadic functions + ### `Variadic` and `OptionalArg` These wrapper types affect both arity detection and null handling. @@ -261,12 +270,6 @@ fn pad_leading(string: &str, raw_len: i32, pad: OptionalArg<&str>) -> Result(&self, datums: Variadic>, temp_storage: &'a RowAre Two shapes stay hand-written by design. -* Functions generic over an `Eval` implementor, which hold sub-expressions in - `Box` and evaluate them per element. The macro would need to emit an +* Functions generic over an `Eval` implementor, which hold sub-expressions in a `Box` + or a `Box<[E]>` and evaluate them per element. The macro would need to emit an implementation generic over a struct type parameter with a trait bound, which is a different mechanism from the type-parameter erasure it applies today. The nine compound-type casts under `src/expr/src/scalar/func/impls/` are these. diff --git a/src/expr-derive/src/lib.rs b/src/expr-derive/src/lib.rs index 428d9f3ca51ce..eebd97b3ee010 100644 --- a/src/expr-derive/src/lib.rs +++ b/src/expr-derive/src/lib.rs @@ -16,9 +16,12 @@ /// appropriate traits: `EagerUnaryFunc`, `EagerBinaryFunc`, or `EagerVariadicFunc`. /// /// The macro takes the following arguments: -/// * `is_monotone`: An expression indicating whether the function is monotone. For unary functions, -/// it should be an expression that evaluates to a boolean. For binary functions, it should be a -/// tuple of two expressions, each evaluating to a boolean. See `LazyBinaryFunc` for details. +/// * `is_monotone`: An expression indicating whether the function is monotone. For unary and +/// variadic functions, it should be an expression that evaluates to a boolean. For binary +/// functions, it should be a tuple of two expressions, each evaluating to a boolean. See +/// `LazyBinaryFunc` for details. +/// * `is_infinity_monotone`: A boolean indicating whether `is_monotone`'s endpoint-sampling +/// guarantee still holds when an operand may be infinite. Applies to binary functions only. /// * `sqlname`: The SQL name of the function. /// * `preserves_uniqueness`: A boolean indicating whether the function preserves uniqueness. /// Unary functions only. @@ -27,7 +30,11 @@ /// the `!=` operator is the negation of the `=` operator, and we'd mark the `Eq` function with /// `negate = Some(BinaryFunc::NotEq)`. /// * `is_infix_op`: A boolean indicating whether the function is an infix operator. Applies to -/// binary functions only. +/// binary and variadic functions. +/// * `is_associative`: A boolean indicating whether the function is associative. Applies to +/// variadic functions only. +/// * `is_eliminable_cast`: A boolean indicating whether the function is a cast that the +/// optimizer may remove. Applies to unary functions only. /// * `output_type`: The output type of the function. /// * `output_type_expr`: An expression that evaluates to the output type. Applies to unary, /// binary, and variadic functions. For unary functions, the expression has access to From f87194d193bedba081bf742720c28f67f6c55259 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Sat, 19 Sep 2026 22:56:41 +0200 Subject: [PATCH 08/19] expr-derive-impl: Reject sqlname with skip_display, and fix stale claims `sqlname` feeds only the generated `Display` impl, so pairing it with `skip_display` silently discards it. Rejecting the combination turns a modifier that does nothing into a compile error, and both modifier references now say so. Records the interlock behind unary's nullability tail. Unary omits the non-nullable-position term the other two arities carry, which is sound only because `propagates_nulls` is not a unary modifier, so unary's value always equals what that term would test. A test already pins that absence, but it did so without saying why, leaving a maintainer who deliberately adds the modifier free to delete the assertion. The assertion now names what depends on it. Corrects three claims the tree no longer supports. `is_infix_op` has a default on every `Eager*Func` trait, so it does not need to be specified. The nine `LazyUnaryFunc` implementations that stay hand-written are the ones generic over `Eval`, not every compound-type cast, and the ten others are convertible now that `EagerUnaryFunc::call` supplies a `RowArena`. `ListLengthMax` gave its `max_layer` field as the reason it could not use the macro, which a `&self` receiver now handles. Replaces the struct-name example, which showed a two-parameter signature that detects as binary under a name saying variadic, with the real `array_fill` signature. Normalizes the test module on `super::` for its own helper. Co-Authored-By: Claude Opus 5 (1M context) --- doc/developer/sqlfunc.md | 29 ++++++++++++++++++++++---- src/expr-derive-impl/src/generate.rs | 10 ++++++--- src/expr-derive-impl/src/lib.rs | 27 +++++++++++++++++++----- src/expr-derive-impl/src/shape.rs | 14 ++++++++++++- src/expr-derive/src/lib.rs | 3 ++- src/expr/src/scalar/func/impls/list.rs | 2 +- 6 files changed, 70 insertions(+), 15 deletions(-) diff --git a/doc/developer/sqlfunc.md b/doc/developer/sqlfunc.md index 7504e0a3abe4b..4f91655500a65 100644 --- a/doc/developer/sqlfunc.md +++ b/doc/developer/sqlfunc.md @@ -57,6 +57,9 @@ The SQL-visible name of the function. * **Default:** the Rust function name (via `stringify!`) * **Applies to:** all arities +The generated `Display` impl is the only reader, so `skip_display = true` rejects +`sqlname`. + ### `propagates_nulls` Whether a NULL input produces a NULL output, skipping the function body entirely. @@ -191,7 +194,8 @@ Suppresses the generated `Display` impl. Use this when the displayed name depend struct's own state rather than being a fixed string, so the call site keeps a hand-written `Display` impl. `RangeCreate` is an example: it picks between `int4range`, `int8range`, `daterange`, `numrange`, `tsrange`, and `tstzrange` based on -its `elem_type` field. +its `elem_type` field. Setting it to `true` rejects `sqlname`, whose only reader is the +suppressed impl. * **Type:** `bool` * **Default:** `false` @@ -209,8 +213,18 @@ The struct name can be specified as the first positional argument, at any arity. This is required when a `&self` receiver is present (the struct is defined externally): ```rust -#[sqlfunc(ArrayFill, sqlname = "array_fill")] -fn array_fill_variadic<'a>(&self, fill: Datum<'a>, dims: Datum<'a>, temp_storage: &'a RowArena) -> Result, EvalError> { +#[sqlfunc( + ArrayFill, + output_type_expr = "SqlScalarType::Array(Box::new(self.elem_type.clone())).nullable(false)", + introduces_nulls = false +)] +fn array_fill<'a>( + &self, + fill: Datum<'a>, + dims: Option>, + lower_bounds: OptionalArg>>, + temp_storage: &'a RowArena, +) -> Result, EvalError> { // ... } ``` @@ -373,7 +387,14 @@ Two shapes stay hand-written by design. or a `Box<[E]>` and evaluate them per element. The macro would need to emit an implementation generic over a struct type parameter with a trait bound, which is a different mechanism from the type-parameter erasure it applies today. The nine - compound-type casts under `src/expr/src/scalar/func/impls/` are these. + `impl LazyUnaryFunc` blocks under `src/expr/src/scalar/func/impls/` are these. * Functions that do not evaluate every operand. The macro emits `Eager*` implementations, which evaluate all arguments before dispatch. `And`, `Or`, `Coalesce`, `Greatest`, `Least`, `ErrorIfNull`, and `CaseLiteral` are these. + +The ten remaining hand-written `LazyUnaryFunc` implementations under +`src/expr/src/scalar/func/impls/` are in neither category, and all ten are convertible. +Nine allocate their output into a `RowArena`, which `EagerUnaryFunc::call` now supplies. +`RecordGet` returns a field borrowed from its input, so it needs no arena, only an +`output_type_expr` reading `input_type` and the `introduces_nulls` that expression +requires. diff --git a/src/expr-derive-impl/src/generate.rs b/src/expr-derive-impl/src/generate.rs index 872a425770111..f880b0617498a 100644 --- a/src/expr-derive-impl/src/generate.rs +++ b/src/expr-derive-impl/src/generate.rs @@ -233,15 +233,19 @@ pub(crate) fn generate( } } - // TODO: these two conflict checks construct their error with `unknown_field`, + // TODO: the conflict checks below construct their error with `unknown_field`, // which renders as "Unknown field: ", while every other modifier - // legality error in the crate uses `Error::custom`. The message text is pinned - // by a snapshot, so changing the constructor requires updating that snapshot. + // legality error in the crate uses `Error::custom`. if output_type.is_some() && output_type_expr.is_some() { return Err(darling::Error::unknown_field( "output_type and output_type_expr cannot be used together", )); } + if mods.skip_display() && mods.sqlname().is_some() { + return Err(darling::Error::unknown_field( + "sqlname has no effect with skip_display, which suppresses the only impl that reads it", + )); + } if output_type_expr.is_some() && introduces_nulls.is_none() { return Err(darling::Error::unknown_field( "output_type_expr requires introduces_nulls", diff --git a/src/expr-derive-impl/src/lib.rs b/src/expr-derive-impl/src/lib.rs index b12462465448a..5c76b3d3bf9e8 100644 --- a/src/expr-derive-impl/src/lib.rs +++ b/src/expr-derive-impl/src/lib.rs @@ -382,7 +382,7 @@ mod test { #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri #[mz_ore::test] fn unary_rejects_negate_by_name() { - let (output, _input) = crate::test_sqlfunc( + let (output, _input) = super::test_sqlfunc( quote! { negate = to_unary!(super::Foo) }, quote! { fn some_unary<'a>(a: i32) -> i32 { a } @@ -397,7 +397,7 @@ mod test { #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri #[mz_ore::test] fn unary_rejects_is_infinity_monotone() { - let (output, _input) = crate::test_sqlfunc( + let (output, _input) = super::test_sqlfunc( quote! { is_infinity_monotone = false }, quote! { fn some_unary<'a>(a: i32) -> i32 { a } @@ -418,7 +418,7 @@ mod test { temp_storage.push_string(format!("{a:width$}", width = self.width)) } }; - let (output, input) = crate::test_sqlfunc(attr, item); + let (output, input) = super::test_sqlfunc(attr, item); insta::assert_snapshot!("unary_self", output, &input); } @@ -431,7 +431,7 @@ mod test { a.clamp(self.lower, b) } }; - let (output, input) = crate::test_sqlfunc(attr, item); + let (output, input) = super::test_sqlfunc(attr, item); insta::assert_snapshot!("binary_self", output, &input); } @@ -444,11 +444,28 @@ mod test { a + self.offset } }; - let (output, input) = crate::test_sqlfunc(attr, item); + let (output, input) = super::test_sqlfunc(attr, item); assert!( !output.contains("impl std::fmt::Display"), "skip_display must suppress the Display impl, got:\n{output}" ); insta::assert_snapshot!("skip_display", output, &input); } + + #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri + #[mz_ore::test] + fn skip_display_rejects_sqlname() { + let (output, _input) = super::test_sqlfunc( + quote! { ExtractThing, skip_display = true, sqlname = "extract_thing" }, + quote! { + fn extract_thing<'a>(&self, a: i64) -> i64 { a + self.offset } + }, + ); + // A successful expansion also contains both modifier names, because the + // registry's `decl` field echoes the attribute text, so match the error itself. + assert!( + output.contains("sqlname has no effect with skip_display"), + "expected the conflict error, got:\n{output}" + ); + } } diff --git a/src/expr-derive-impl/src/shape.rs b/src/expr-derive-impl/src/shape.rs index 38ea235eb33bb..11be6223b6c15 100644 --- a/src/expr-derive-impl/src/shape.rs +++ b/src/expr-derive-impl/src/shape.rs @@ -182,6 +182,14 @@ impl Shape { /// some input is nullable, because the optimizer short-circuits an all-NULL call. pub(crate) fn nullability(&self, checks: &[TokenStream]) -> TokenStream { match self { + // NOTE: unary omits the non-nullable-position term the other two arities + // carry. That is sound only because `PropagatesNulls` is absent from + // `UNARY_MODIFIERS`, so a unary `propagates_nulls` is always + // `!Input::nullable()`, which is what the position check tests for every + // parameter type in the tree. + // Adding `PropagatesNulls` to the unary table without also adding the term + // here lets `propagates_nulls = false` report a non-nullable output for an + // input the evaluation layer short-circuits to NULL. Shape::Unary => quote! { output.nullable(nullable || (propagates_nulls && input_type.nullable)) }, @@ -224,7 +232,11 @@ mod tests { assert!(has(Shape::Unary, Modifier::IsEliminableCast)); assert!(!has(Shape::Unary, Modifier::Negate)); assert!(!has(Shape::Unary, Modifier::IsInfixOp)); - assert!(!has(Shape::Unary, Modifier::PropagatesNulls)); + assert!( + !has(Shape::Unary, Modifier::PropagatesNulls), + "Shape::nullability omits unary's non-nullable-position term on the strength of \ + this absence" + ); assert!(!has(Shape::Unary, Modifier::IsAssociative)); } } diff --git a/src/expr-derive/src/lib.rs b/src/expr-derive/src/lib.rs index eebd97b3ee010..ab490c4934c91 100644 --- a/src/expr-derive/src/lib.rs +++ b/src/expr-derive/src/lib.rs @@ -54,7 +54,8 @@ /// Defaults to `false`. /// * `skip_display`: A boolean suppressing the generated `fmt::Display` impl. Set this /// when the struct's name depends on its state, so the call site keeps a hand-written -/// `Display` impl instead. +/// `Display` impl instead. Setting it to `true` rejects `sqlname`, whose only reader +/// is the suppressed impl. /// /// # Limitations /// * The input and output types can contain lifetime parameters, as long as they are `'a`. diff --git a/src/expr/src/scalar/func/impls/list.rs b/src/expr/src/scalar/func/impls/list.rs index 7b7e1f2f8db18..544c93e190719 100644 --- a/src/expr/src/scalar/func/impls/list.rs +++ b/src/expr/src/scalar/func/impls/list.rs @@ -293,7 +293,7 @@ fn list_length<'a>(a: DatumList<'a>) -> Result { /// The `list_length_max` implementation. /// -/// We're not deriving `sqlfunc` here because we need to pass in the `max_layer` parameter. +/// TODO: convertible to `#[sqlfunc]`, which accepts a `&self` receiver. #[derive( Ord, PartialOrd, From 94d6dd1822b0c13566786775caf2115eb2894f49 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Thu, 24 Sep 2026 11:47:45 +0200 Subject: [PATCH 09/19] expr-derive-impl: Take inverse and negate as functions, and default introduces_nulls under output_type_expr `inverse` and `negate` now name the function, and the macro wraps it as `Some(crate::UnaryFunc::from(..))` or `Some(crate::BinaryFunc::from(..))`. Every call site spelled that wrapper out, through `to_unary!` or a `Some(..into())` string, so the attribute now carries only what differs per function. `to_unary!` stays for the hand-written `impl` casts. `output_type_expr` no longer requires `introduces_nulls`. Left out, it falls back to the trait default, `Output::nullable()`, which is sound: an output type that cannot hold NULL cannot produce one. Call sites whose explicit value equalled that default drop it. The ones that return a `Datum` or an `Option` but yield NULL only for NULL inputs keep `introduces_nulls = false`. The registry confirms that no declared property moved. `RangeCreate` returns `Range>` rather than packing the range itself, so it needs no arena and derives the `false` it declared. Co-Authored-By: Claude Opus 5.5 (1M context) --- doc/developer/sqlfunc.md | 15 +- .../tests/snapshots/func_registry.json | 2 +- .../snapshots/func_registry_digests.json | 2 +- .../tests/snapshots/func_registry_source.json | 336 +++++++++--------- src/expr-derive-impl/src/generate.rs | 8 +- src/expr-derive-impl/src/lib.rs | 6 +- src/expr-derive-impl/src/shape.rs | 10 + ...rive_impl__test__binary_all_modifiers.snap | 6 +- ...erive_impl__test__unary_all_modifiers.snap | 6 +- src/expr/src/scalar/func.rs | 56 +-- src/expr/src/scalar/func/impls/array.rs | 2 +- src/expr/src/scalar/func/impls/boolean.rs | 10 +- src/expr/src/scalar/func/impls/byte.rs | 2 +- src/expr/src/scalar/func/impls/char.rs | 4 +- src/expr/src/scalar/func/impls/date.rs | 2 +- src/expr/src/scalar/func/impls/float32.rs | 18 +- src/expr/src/scalar/func/impls/float64.rs | 18 +- src/expr/src/scalar/func/impls/int16.rs | 20 +- src/expr/src/scalar/func/impls/int2vector.rs | 3 +- src/expr/src/scalar/func/impls/int32.rs | 26 +- src/expr/src/scalar/func/impls/int64.rs | 24 +- src/expr/src/scalar/func/impls/interval.rs | 6 +- src/expr/src/scalar/func/impls/jsonb.rs | 2 +- .../src/scalar/func/impls/mz_timestamp.rs | 8 +- src/expr/src/scalar/func/impls/numeric.rs | 20 +- src/expr/src/scalar/func/impls/oid.rs | 12 +- .../src/scalar/func/impls/pg_legacy_char.rs | 10 +- src/expr/src/scalar/func/impls/regproc.rs | 6 +- src/expr/src/scalar/func/impls/string.rs | 36 +- src/expr/src/scalar/func/impls/time.rs | 4 +- src/expr/src/scalar/func/impls/timestamp.rs | 8 +- src/expr/src/scalar/func/impls/uint16.rs | 18 +- src/expr/src/scalar/func/impls/uint32.rs | 18 +- src/expr/src/scalar/func/impls/uint64.rs | 18 +- src/expr/src/scalar/func/impls/uuid.rs | 2 +- src/expr/src/scalar/func/impls/varchar.rs | 4 +- src/expr/src/scalar/func/variadic.rs | 18 +- 37 files changed, 369 insertions(+), 397 deletions(-) diff --git a/doc/developer/sqlfunc.md b/doc/developer/sqlfunc.md index 4f91655500a65..8fa18ea1240f8 100644 --- a/doc/developer/sqlfunc.md +++ b/doc/developer/sqlfunc.md @@ -83,7 +83,7 @@ The optimizer uses this to reason about column nullability. A function returning `Option` or `Datum` introduces nulls. A function returning `String` or `i32` does not. * **Applies to:** all arities -* **Note:** required when using `output_type_expr` (because the output type is not statically known). +* **Note:** set it where the return type overstates it. A function returning `Datum` or `Option` that yields NULL only for NULL inputs declares `introduces_nulls = false`. ### `could_error` @@ -126,7 +126,7 @@ Whether the function is injective: if `f(x) = f(y)` then `x = y`. The inverse function, if it exists. -* **Type:** expression evaluating to `Option` +* **Type:** expression convertible into `crate::UnaryFunc`, for example `inverse = NegFloat64` * **Default:** `None` * **Applies to:** unary only @@ -135,7 +135,7 @@ The inverse function, if it exists. The logical negation of a comparison function. For example, `<` negates to `>=`. -* **Type:** expression evaluating to `Option` +* **Type:** expression convertible into `crate::BinaryFunc`, for example `negate = NotEq` * **Default:** `None` * **Applies to:** binary only @@ -174,7 +174,6 @@ Use this for functions whose output type depends on input types or struct fields * **Type:** expression evaluating to `SqlColumnType` * **Default:** none * **Applies to:** all arities -* **Requires:** `introduces_nulls` (must be specified explicitly) * **Cannot be combined with:** `output_type` ### `test` @@ -320,7 +319,7 @@ If the input type does not accept NULL (is non-nullable), the evaluation layer r #[sqlfunc( sqlname = "uint2_to_real", preserves_uniqueness = true, - inverse = to_unary!(super::CastFloat32ToUint16), + inverse = super::CastFloat32ToUint16, is_monotone = true )] fn cast_uint16_to_float32(a: u16) -> f32 { @@ -370,8 +369,7 @@ fn concat(strs: Variadic>) -> Result { #[sqlfunc( ArrayCreate, sqlname = "array_create", - output_type_expr = "SqlScalarType::Array(Box::new(self.elem_type.clone())).nullable(false)", - introduces_nulls = false + output_type_expr = "SqlScalarType::Array(Box::new(self.elem_type.clone())).nullable(false)" )] fn array_create<'a>(&self, datums: Variadic>, temp_storage: &'a RowArena) -> Array<'a> { // &self: struct defined externally with an elem_type field @@ -396,5 +394,4 @@ The ten remaining hand-written `LazyUnaryFunc` implementations under `src/expr/src/scalar/func/impls/` are in neither category, and all ten are convertible. Nine allocate their output into a `RowArena`, which `EagerUnaryFunc::call` now supplies. `RecordGet` returns a field borrowed from its input, so it needs no arena, only an -`output_type_expr` reading `input_type` and the `introduces_nulls` that expression -requires. +`output_type_expr` reading `input_type`. diff --git a/src/compute-types/tests/snapshots/func_registry.json b/src/compute-types/tests/snapshots/func_registry.json index 2dd86a5685095..1183451eb5914 100644 --- a/src/compute-types/tests/snapshots/func_registry.json +++ b/src/compute-types/tests/snapshots/func_registry.json @@ -619,7 +619,7 @@ "map_build": {"variant":"MapBuild","propagates_nulls":false,"introduces_nulls":false,"could_error":true,"is_monotone":false,"is_associative":false,"is_infix_op":false,"input_types":["String:NotNull","Int32:NotNull"],"output_type":"Map { value_type: Int32, custom_id: None }:NotNull","sqlfunc_signature":"fn(Variadic<(Option<&'a str>, Datum<'a>)>) -> Result, EvalError>"}, "or": {"variant":"Or","propagates_nulls":false,"introduces_nulls":false,"could_error":false,"is_monotone":true,"is_associative":true,"is_infix_op":true,"input_types":["Bool:NotNull","Bool:NotNull"],"output_type":"Bool:NotNull","sqlfunc_signature":null}, "pad_leading": {"variant":"PadLeading","propagates_nulls":true,"introduces_nulls":false,"could_error":true,"is_monotone":false,"is_associative":false,"is_infix_op":false,"input_types":["String:NotNull","Int32:NotNull","String:NotNull"],"output_type":"String:NotNull","sqlfunc_signature":"fn(&'a str, i32, OptionalArg<&'a str>) -> Result"}, - "range_create": {"variant":"RangeCreate","propagates_nulls":false,"introduces_nulls":false,"could_error":true,"is_monotone":false,"is_associative":false,"is_infix_op":false,"input_types":["Int32:NotNull","Int32:NotNull","String:NotNull"],"output_type":"Range { element_type: Int32 }:NotNull","sqlfunc_signature":"fn(Datum<'a>, Datum<'a>, Datum<'a>) -> Result, EvalError>"}, + "range_create": {"variant":"RangeCreate","propagates_nulls":false,"introduces_nulls":false,"could_error":true,"is_monotone":false,"is_associative":false,"is_infix_op":false,"input_types":["Int32:NotNull","Int32:NotNull","String:NotNull"],"output_type":"Range { element_type: Int32 }:NotNull","sqlfunc_signature":"fn(Datum<'a>, Datum<'a>, Datum<'a>) -> Result>, EvalError>"}, "record_create": {"variant":"RecordCreate","propagates_nulls":false,"introduces_nulls":false,"could_error":true,"is_monotone":false,"is_associative":false,"is_infix_op":false,"input_types":["Int32:NotNull"],"output_type":"Record { fields: [(ColumnName(\"a\"), SqlColumnType { scalar_type: Int32, nullable: false })], custom_id: None }:NotNull","sqlfunc_signature":"fn(Variadic>) -> Result, EvalError>"}, "regexp_match": {"variant":"RegexpMatch","propagates_nulls":true,"introduces_nulls":true,"could_error":true,"is_monotone":false,"is_associative":false,"is_infix_op":false,"input_types":["String:NotNull","String:NotNull","String:NotNull"],"output_type":"Array(String):Null","sqlfunc_signature":"fn(&'a str, &'a str, OptionalArg<&'a str>) -> Result, EvalError>"}, "regexp_replace": {"variant":"RegexpReplace","propagates_nulls":true,"introduces_nulls":false,"could_error":true,"is_monotone":false,"is_associative":false,"is_infix_op":false,"input_types":["String:NotNull","String:NotNull","String:NotNull","String:NotNull"],"output_type":"String:NotNull","sqlfunc_signature":"fn(&'a str, &'a str, &'a str, OptionalArg<&'a str>) -> Result, EvalError>"}, diff --git a/src/compute-types/tests/snapshots/func_registry_digests.json b/src/compute-types/tests/snapshots/func_registry_digests.json index 9805e189b233d..5b65a2e1f5c3e 100644 --- a/src/compute-types/tests/snapshots/func_registry_digests.json +++ b/src/compute-types/tests/snapshots/func_registry_digests.json @@ -1,3 +1,3 @@ { - "1": "sha256:5ec3291615e7d51e5979e724b81697fa80cadb0faddbbd988fea5ab3aedb6748" + "1": "sha256:0c737bd1372e641b0c9f99554c078c77a33e225a2aa184eda1928a9a68a1a712" } diff --git a/src/compute-types/tests/snapshots/func_registry_source.json b/src/compute-types/tests/snapshots/func_registry_source.json index 946559e2c8430..e9c300fb36c5f 100644 --- a/src/compute-types/tests/snapshots/func_registry_source.json +++ b/src/compute-types/tests/snapshots/func_registry_source.json @@ -18,10 +18,10 @@ "age_timestamp": {"display":"age","sqlfunc_decl":"#[sqlfunc(sqlname = \"age\")] fn age_timestamp(a: CheckedTimestamp, b: CheckedTimestamp,) -> Result","body_fingerprint":"d5c8b6d58dc02ca0"}, "age_timestamp_tz": {"display":"age","sqlfunc_decl":"#[sqlfunc(sqlname = \"age\")] fn age_timestamp_tz(a: CheckedTimestamp>, b: CheckedTimestamp>,) -> Result","body_fingerprint":"d5c8b6d58dc02ca0"}, "array_array_concat": {"display":"||","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"input_types[0].scalar_type.without_modifiers().nullable(true)\", is_infix_op = true, sqlname = \"||\", propagates_nulls = false, introduces_nulls = false)] fn array_array_concat<'a>(a: Option>, b: Option>, temp_storage: &'a RowArena,) -> Result>, EvalError>","body_fingerprint":"9ff9a14b971a8019"}, - "array_contains": {"display":"array_contains","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"array_contains\", propagates_nulls = true, introduces_nulls = false)] fn array_contains<'a>(a: Datum<'a>, array: Array<'a>) -> bool","body_fingerprint":"258f7d3f3cd6573f"}, + "array_contains": {"display":"array_contains","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"array_contains\", propagates_nulls = true)] fn array_contains<'a>(a: Datum<'a>, array: Array<'a>) -> bool","body_fingerprint":"258f7d3f3cd6573f"}, "array_contains_array": {"display":"@>","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"@>\")] fn array_contains_array<'a>(a: Array<'a>, b: Array<'a>) -> bool","body_fingerprint":"f4c8b92bc69598c5"}, "array_contains_array_rev": {"display":"<@","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"<@\")] fn array_contains_array_rev<'a>(a: Array<'a>, b: Array<'a>) -> bool","body_fingerprint":"c3e1b66a36e33dea"}, - "array_length": {"display":"array_length","sqlfunc_decl":"#[sqlfunc(sqlname = \"array_length\", propagates_nulls = true, introduces_nulls = true)] fn array_length<'a>(a: Array<'a>, b: i64) -> Result, EvalError>","body_fingerprint":"511a574b8b3ab56a"}, + "array_length": {"display":"array_length","sqlfunc_decl":"#[sqlfunc(sqlname = \"array_length\", propagates_nulls = true)] fn array_length<'a>(a: Array<'a>, b: i64) -> Result, EvalError>","body_fingerprint":"511a574b8b3ab56a"}, "array_lower": {"display":"array_lower","sqlfunc_decl":"#[sqlfunc(is_infix_op = true)] fn array_lower<'a>(a: Array<'a>, i: i64) -> Result, EvalError>","body_fingerprint":"fa2b8d3be6005650"}, "array_remove": {"display":"array_remove","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"input_types[0].scalar_type.without_modifiers().nullable(true)\", sqlname = \"array_remove\", propagates_nulls = false, introduces_nulls = false)] fn array_remove<'a>(arr: Array<'a>, b: Datum<'a>, temp_storage: &'a RowArena,) -> Result, EvalError>","body_fingerprint":"f8e91dd54724c5e7"}, "array_upper": {"display":"array_upper","sqlfunc_decl":"#[sqlfunc(is_infix_op = true)] fn array_upper<'a>(a: Array<'a>, i: i64) -> Result, EvalError>","body_fingerprint":"8aac123bc3861c38"}, @@ -60,10 +60,10 @@ "convert_from": {"display":"convert_from","sqlfunc_decl":"#[sqlfunc(sqlname = \"convert_from\", propagates_nulls = true)] fn convert_from<'a>(a: &'a[u8], b: &str) -> Result<&'a str, EvalError>","body_fingerprint":"6854417075f37414"}, "date_bin_timestamp": {"display":"bin_unix_epoch_timestamp","sqlfunc_decl":"#[sqlfunc(is_monotone = \"(false, true)\", sqlname = \"bin_unix_epoch_timestamp\")] fn date_bin_timestamp(stride: Interval, source: CheckedTimestamp,) -> Result, EvalError>","body_fingerprint":"810678cf4587a7d7"}, "date_bin_timestamp_tz": {"display":"bin_unix_epoch_timestamptz","sqlfunc_decl":"#[sqlfunc(is_monotone = \"(false, true)\", sqlname = \"bin_unix_epoch_timestamptz\")] fn date_bin_timestamp_tz(stride: Interval, source: CheckedTimestamp>,) -> Result>, EvalError>","body_fingerprint":"de4ceb358cd8ed50"}, - "date_part_interval_f64": {"display":"date_partiv","sqlfunc_decl":"#[sqlfunc(sqlname = \"date_partiv\", propagates_nulls = true, introduces_nulls = false)] fn date_part_interval_f64(units: &str, b: Interval) -> Result","body_fingerprint":"ee191a869c785c4d"}, - "date_part_interval_numeric": {"display":"extractiv","sqlfunc_decl":"#[sqlfunc(sqlname = \"extractiv\", propagates_nulls = true, introduces_nulls = false)] fn date_part_interval_numeric(units: &str, b: Interval) -> Result","body_fingerprint":"5a0c19540e3d27b2"}, - "date_part_time_f64": {"display":"date_partt","sqlfunc_decl":"#[sqlfunc(sqlname = \"date_partt\", propagates_nulls = true, introduces_nulls = false)] fn date_part_time_f64(units: &str, b: chrono::NaiveTime) -> Result","body_fingerprint":"4f0e87bfd219ac4d"}, - "date_part_time_numeric": {"display":"extractt","sqlfunc_decl":"#[sqlfunc(sqlname = \"extractt\", propagates_nulls = true, introduces_nulls = false)] fn date_part_time_numeric(units: &str, b: chrono::NaiveTime) -> Result","body_fingerprint":"84ebc563755b77b2"}, + "date_part_interval_f64": {"display":"date_partiv","sqlfunc_decl":"#[sqlfunc(sqlname = \"date_partiv\", propagates_nulls = true)] fn date_part_interval_f64(units: &str, b: Interval) -> Result","body_fingerprint":"ee191a869c785c4d"}, + "date_part_interval_numeric": {"display":"extractiv","sqlfunc_decl":"#[sqlfunc(sqlname = \"extractiv\", propagates_nulls = true)] fn date_part_interval_numeric(units: &str, b: Interval) -> Result","body_fingerprint":"5a0c19540e3d27b2"}, + "date_part_time_f64": {"display":"date_partt","sqlfunc_decl":"#[sqlfunc(sqlname = \"date_partt\", propagates_nulls = true)] fn date_part_time_f64(units: &str, b: chrono::NaiveTime) -> Result","body_fingerprint":"4f0e87bfd219ac4d"}, + "date_part_time_numeric": {"display":"extractt","sqlfunc_decl":"#[sqlfunc(sqlname = \"extractt\", propagates_nulls = true)] fn date_part_time_numeric(units: &str, b: chrono::NaiveTime) -> Result","body_fingerprint":"84ebc563755b77b2"}, "date_part_timestamp_timestamp_f64": {"display":"date_partts","sqlfunc_decl":"#[sqlfunc(sqlname = \"date_partts\", propagates_nulls = true)] fn date_part_timestamp_timestamp_f64(units: &str, ts: CheckedTimestamp,) -> Result","body_fingerprint":"64042d92ba3d53b5"}, "date_part_timestamp_timestamp_numeric": {"display":"extractts","sqlfunc_decl":"#[sqlfunc(sqlname = \"extractts\", propagates_nulls = true)] fn date_part_timestamp_timestamp_numeric(units: &str, ts: CheckedTimestamp,) -> Result","body_fingerprint":"6d7cba6f9bedfe2d"}, "date_part_timestamp_timestamp_tz_f64": {"display":"date_parttstz","sqlfunc_decl":"#[sqlfunc(sqlname = \"date_parttstz\", propagates_nulls = true)] fn date_part_timestamp_timestamp_tz_f64(units: &str, ts: CheckedTimestamp>,) -> Result","body_fingerprint":"64042d92ba3d53b5"}, @@ -87,12 +87,12 @@ "element_list_concat": {"display":"||","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"||\", propagates_nulls = false)] fn element_list_concat<'a, T: FromDatum<'a>>(a: T, b: Option>, temp_storage: &'a RowArena,) -> DatumList<'a, T>","body_fingerprint":"74d5291ced5c8c85"}, "encode": {"display":"encode","sqlfunc_decl":"#[sqlfunc] fn encode(bytes: &[u8], format: &str) -> Result","body_fingerprint":"386a66d9b57c930f"}, "encoded_bytes_char_length": {"display":"length","sqlfunc_decl":"#[sqlfunc(sqlname = \"length\", propagates_nulls = true)] fn encoded_bytes_char_length(a: &[u8], b: &str) -> Result","body_fingerprint":"00dd505df65ef857"}, - "eq": {"display":"=","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"=\", negate = \"Some(NotEq.into())\")] fn eq<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool","body_fingerprint":"a0737efed3c5645e"}, + "eq": {"display":"=","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"=\", negate = NotEq)] fn eq<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool","body_fingerprint":"a0737efed3c5645e"}, "extract_date_units": {"display":"extractd","sqlfunc_decl":"#[sqlfunc(sqlname = \"extractd\", propagates_nulls = true)] fn extract_date_units(units: &str, b: Date) -> Result","body_fingerprint":"552752eb53e25f38"}, "get_bit": {"display":"get_bit","sqlfunc_decl":"#[sqlfunc(propagates_nulls = true)] fn get_bit(bytes: &[u8], index: i32) -> Result","body_fingerprint":"2c27c6d616894079"}, "get_byte": {"display":"get_byte","sqlfunc_decl":"#[sqlfunc(propagates_nulls = true)] fn get_byte(bytes: &[u8], index: i32) -> Result","body_fingerprint":"dc6519b277087f72"}, - "gt": {"display":">","sqlfunc_decl":"#[sqlfunc(is_monotone = \"(true, true)\", is_infix_op = true, sqlname = \">\", negate = \"Some(Lte.into())\")] fn gt<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool","body_fingerprint":"440733ba035333f6"}, - "gte": {"display":">=","sqlfunc_decl":"#[sqlfunc(is_monotone = \"(true, true)\", is_infix_op = true, sqlname = \">=\", negate = \"Some(Lt.into())\")] fn gte<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool","body_fingerprint":"88cc442a3f88c691"}, + "gt": {"display":">","sqlfunc_decl":"#[sqlfunc(is_monotone = \"(true, true)\", is_infix_op = true, sqlname = \">\", negate = Lte)] fn gt<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool","body_fingerprint":"440733ba035333f6"}, + "gte": {"display":">=","sqlfunc_decl":"#[sqlfunc(is_monotone = \"(true, true)\", is_infix_op = true, sqlname = \">=\", negate = Lt)] fn gte<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool","body_fingerprint":"88cc442a3f88c691"}, "is_like_match_case_insensitive": {"display":"ilike","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"ilike\")] fn is_like_match_case_insensitive(haystack: &str, pattern: &str) -> Result","body_fingerprint":"b81302ce9602899f"}, "is_like_match_case_sensitive": {"display":"like","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"like\")] fn is_like_match_case_sensitive(haystack: &str, pattern: &str) -> Result","body_fingerprint":"b0ccd689977c372e"}, "is_regexp_match_case_insensitive": {"display":"~*","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"~*\")] fn is_regexp_match_case_insensitive(haystack: &str, needle: &str) -> Result","body_fingerprint":"5882540ca41af87c"}, @@ -100,8 +100,8 @@ "jsonb_concat": {"display":"||","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"||\")] fn jsonb_concat<'a>(a: JsonbRef<'a>, b: JsonbRef<'a>, temp_storage: &'a RowArena,) -> Option>","body_fingerprint":"5c7d5752a10e5eb1"}, "jsonb_contains_jsonb": {"display":"@>","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"@>\")] fn jsonb_contains_jsonb<'a>(a: JsonbRef<'a>, b: JsonbRef<'a>) -> bool","body_fingerprint":"eb83b3a7d21cc242"}, "jsonb_contains_string": {"display":"?","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"?\")] fn jsonb_contains_string<'a>(a: JsonbRef<'a>, k: &str) -> bool","body_fingerprint":"4256b57e7bb85cda"}, - "jsonb_delete_int64": {"display":"-","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"SqlScalarType::Jsonb.nullable(true)\", is_infix_op = true, sqlname = \"-\", propagates_nulls = true, introduces_nulls = true)] fn jsonb_delete_int64<'a>(a: Datum<'a>, i: i64, temp_storage: &'a RowArena) -> Datum<'a>","body_fingerprint":"e52af4297083a865"}, - "jsonb_delete_string": {"display":"-","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"SqlScalarType::Jsonb.nullable(true)\", is_infix_op = true, sqlname = \"-\", propagates_nulls = true, introduces_nulls = true)] fn jsonb_delete_string<'a>(a: Datum<'a>, k: &str, temp_storage: &'a RowArena) -> Datum<'a>","body_fingerprint":"1db9bcb86d8fbd17"}, + "jsonb_delete_int64": {"display":"-","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"SqlScalarType::Jsonb.nullable(true)\", is_infix_op = true, sqlname = \"-\", propagates_nulls = true)] fn jsonb_delete_int64<'a>(a: Datum<'a>, i: i64, temp_storage: &'a RowArena) -> Datum<'a>","body_fingerprint":"e52af4297083a865"}, + "jsonb_delete_string": {"display":"-","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"SqlScalarType::Jsonb.nullable(true)\", is_infix_op = true, sqlname = \"-\", propagates_nulls = true)] fn jsonb_delete_string<'a>(a: Datum<'a>, k: &str, temp_storage: &'a RowArena) -> Datum<'a>","body_fingerprint":"1db9bcb86d8fbd17"}, "jsonb_get_int64": {"display":"->","sqlfunc_decl":"#[sqlfunc(sqlname = \"->\", is_infix_op = true)] fn jsonb_get_int64<'a>(a: JsonbRef<'a>, i: i64) -> Option>","body_fingerprint":"f12db52366cb6a1a"}, "jsonb_get_int64_stringify": {"display":"->>","sqlfunc_decl":"#[sqlfunc(sqlname = \"->>\", is_infix_op = true)] fn jsonb_get_int64_stringify<'a>(a: JsonbRef<'a>, i: i64, temp_storage: &'a RowArena,) -> Option<&'a str>","body_fingerprint":"4a3fe3f77f2f86f9"}, "jsonb_get_path": {"display":"#>","sqlfunc_decl":"#[sqlfunc(sqlname = \"#>\", is_infix_op = true)] fn jsonb_get_path<'a>(mut json: JsonbRef<'a>, b: Array<'a>) -> Option>","body_fingerprint":"d6e238bc2cb523f6"}, @@ -109,7 +109,7 @@ "jsonb_get_string": {"display":"->","sqlfunc_decl":"#[sqlfunc(sqlname = \"->\", is_infix_op = true)] fn jsonb_get_string<'a>(a: JsonbRef<'a>, k: &str) -> Option>","body_fingerprint":"2c6b71b287aac9c7"}, "jsonb_get_string_stringify": {"display":"->>","sqlfunc_decl":"#[sqlfunc(sqlname = \"->>\", is_infix_op = true)] fn jsonb_get_string_stringify<'a>(a: JsonbRef<'a>, k: &str, temp_storage: &'a RowArena,) -> Option<&'a str>","body_fingerprint":"7f27a524571dc983"}, "left": {"display":"left","sqlfunc_decl":"#[sqlfunc(propagates_nulls = true, is_monotone = (false, false))] fn left<'a>(string: &'a str, b: i32) -> Result<&'a str, EvalError>","body_fingerprint":"01be9696c362dfc9"}, - "like_escape": {"display":"like_escape","sqlfunc_decl":"#[sqlfunc(propagates_nulls = true, introduces_nulls = false)] fn like_escape<'a>(pattern: &str, b: &str, temp_storage: &'a RowArena,) -> Result<&'a str, EvalError>","body_fingerprint":"761f2691cbcaee5c"}, + "like_escape": {"display":"like_escape","sqlfunc_decl":"#[sqlfunc(propagates_nulls = true)] fn like_escape<'a>(pattern: &str, b: &str, temp_storage: &'a RowArena,) -> Result<&'a str, EvalError>","body_fingerprint":"761f2691cbcaee5c"}, "list_contains_list": {"display":"@>","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"@>\")] fn list_contains_list<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool","body_fingerprint":"9b911bace0fe1e31"}, "list_contains_list_rev": {"display":"<@","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"<@\")] fn list_contains_list_rev<'a>(a: ExcludeNull>, b: ExcludeNull>,) -> bool","body_fingerprint":"1317829caf34be82"}, "list_element_concat": {"display":"||","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"||\", propagates_nulls = false)] fn list_element_concat<'a, T: FromDatum<'a>>(a: Option>, b: T, temp_storage: &'a RowArena,) -> DatumList<'a, T>","body_fingerprint":"71fed4e933e22e71"}, @@ -117,8 +117,8 @@ "list_list_concat": {"display":"||","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"||\", propagates_nulls = false, introduces_nulls = false)] fn list_list_concat<'a, T: FromDatum<'a>>(a: Option>, b: Option>, temp_storage: &'a RowArena,) -> Option>","body_fingerprint":"8e64667161369169"}, "list_remove": {"display":"list_remove","sqlfunc_decl":"#[sqlfunc(sqlname = \"list_remove\")] fn list_remove<'a, T: FromDatum<'a>>(a: DatumList<'a, T>, b: T, temp_storage: &'a RowArena,) -> DatumList<'a, T>","body_fingerprint":"685cdd219b48e8fb"}, "log_base_numeric": {"display":"log","sqlfunc_decl":"#[sqlfunc(sqlname = \"log\", propagates_nulls = true)] fn log_base_numeric(mut a: Numeric, mut b: Numeric) -> Result","body_fingerprint":"34396d686148c173"}, - "lt": {"display":"<","sqlfunc_decl":"#[sqlfunc(is_monotone = \"(true, true)\", is_infix_op = true, sqlname = \"<\", negate = \"Some(Gte.into())\")] fn lt<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool","body_fingerprint":"33e35c6214c55a46"}, - "lte": {"display":"<=","sqlfunc_decl":"#[sqlfunc(is_monotone = \"(true, true)\", is_infix_op = true, sqlname = \"<=\", negate = \"Some(Gt.into())\")] fn lte<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool","body_fingerprint":"9662e0716e38349f"}, + "lt": {"display":"<","sqlfunc_decl":"#[sqlfunc(is_monotone = \"(true, true)\", is_infix_op = true, sqlname = \"<\", negate = Gte)] fn lt<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool","body_fingerprint":"33e35c6214c55a46"}, + "lte": {"display":"<=","sqlfunc_decl":"#[sqlfunc(is_monotone = \"(true, true)\", is_infix_op = true, sqlname = \"<=\", negate = Gt)] fn lte<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool","body_fingerprint":"9662e0716e38349f"}, "map_contains_all_keys": {"display":"?&","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"?&\")] fn map_contains_all_keys<'a>(map: DatumMap<'a>, keys: Array<'a>) -> bool","body_fingerprint":"098fa1816021b8c9"}, "map_contains_any_keys": {"display":"?|","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"?|\", propagates_nulls = true)] fn map_contains_any_keys<'a>(map: DatumMap<'a>, keys: Array<'a>) -> bool","body_fingerprint":"99dd5d1bde4e90ea"}, "map_contains_key": {"display":"?","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"?\", propagates_nulls = true)] fn map_contains_key<'a>(map: DatumMap<'a>, k: &str) -> bool","body_fingerprint":"a3c666058c532732"}, @@ -146,7 +146,7 @@ "mz_acl_item_contains_privilege": {"display":"mz_aclitem_contains_privilege","sqlfunc_decl":"#[sqlfunc(sqlname = \"mz_aclitem_contains_privilege\", output_type = \"bool\", propagates_nulls = true)] fn mz_acl_item_contains_privilege(mz_acl_item: MzAclItem, privileges: &str,) -> Result","body_fingerprint":"da42b543c89f25ee"}, "mz_render_typmod": {"display":"mz_render_typmod","sqlfunc_decl":"#[sqlfunc] fn mz_render_typmod(oid: u32, typmod: i32) -> String","body_fingerprint":"c59c5905a68081a1"}, "normalize": {"display":"normalize","sqlfunc_decl":"#[sqlfunc] fn normalize(text: &str, form_str: &str) -> Result","body_fingerprint":"5c7622700646707b"}, - "not_eq": {"display":"!=","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"!=\", negate = \"Some(Eq.into())\")] fn not_eq<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool","body_fingerprint":"3c0c1af3605d6148"}, + "not_eq": {"display":"!=","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"!=\", negate = Eq)] fn not_eq<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool","body_fingerprint":"3c0c1af3605d6148"}, "parse_ident": {"display":"parse_ident","sqlfunc_decl":"#[sqlfunc] fn parse_ident<'a>(ident: &'a str, strict: bool) -> Result>, EvalError>","body_fingerprint":"3c1a3ead179e37f1"}, "position": {"display":"position","sqlfunc_decl":"#[sqlfunc] fn position(substring: &str, string: &str) -> Result","body_fingerprint":"b795aa808765e4f6"}, "power": {"display":"power","sqlfunc_decl":"#[sqlfunc(propagates_nulls = true)] fn power(a: f64, b: f64) -> Result","body_fingerprint":"2456483c2b457bdb"}, @@ -169,7 +169,7 @@ "range_contains_timestamp_rev": {"display":"<@","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"<@\", propagates_nulls = true)] fn range_contains_timestamp_rev<'a>(a: Range>, elem: CheckedTimestamp,) -> bool","body_fingerprint":"753872bbc33a41f9"}, "range_contains_timestamp_tz": {"display":"@>","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"@>\", propagates_nulls = true)] fn range_contains_timestamp_tz<'a>(a: Range>, elem: CheckedTimestamp>,) -> bool","body_fingerprint":"753872bbc33a41f9"}, "range_contains_timestamp_tz_rev": {"display":"<@","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"<@\", propagates_nulls = true)] fn range_contains_timestamp_tz_rev<'a>(a: Range>, elem: CheckedTimestamp>,) -> bool","body_fingerprint":"753872bbc33a41f9"}, - "range_difference": {"display":"-","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"input_types[0].scalar_type.without_modifiers().nullable(true)\", is_infix_op = true, sqlname = \"-\", propagates_nulls = true, introduces_nulls = false)] fn range_difference<'a>(l: Range>, r: Range>,) -> Result>, EvalError>","body_fingerprint":"9e565b1aa199a975"}, + "range_difference": {"display":"-","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"input_types[0].scalar_type.without_modifiers().nullable(true)\", is_infix_op = true, sqlname = \"-\", propagates_nulls = true)] fn range_difference<'a>(l: Range>, r: Range>,) -> Result>, EvalError>","body_fingerprint":"9e565b1aa199a975"}, "range_intersection": {"display":"*","sqlfunc_decl":"#[sqlfunc(is_infix_op = true, sqlname = \"*\")] fn range_intersection(l: Range, r: Range) -> Range","body_fingerprint":"03b9790d658da21d"}, "range_overlaps": {"display":"&&","sqlfunc_decl":"#[sqlfunc(output_type = \"bool\", is_infix_op = true, sqlname = \"&&\", propagates_nulls = true)] fn range_overlaps<'a>(a: Datum<'a>, b: Datum<'a>) -> Datum<'a>","body_fingerprint":"fa1814e86cee0aef"}, "range_overleft": {"display":"&<","sqlfunc_decl":"#[sqlfunc(output_type = \"bool\", is_infix_op = true, sqlname = \"&<\", propagates_nulls = true)] fn range_overleft<'a>(a: Datum<'a>, b: Datum<'a>) -> Datum<'a>","body_fingerprint":"46a271100deeb160"}, @@ -238,91 +238,91 @@ "bit_count_bytes": {"display":"bit_count","sqlfunc_decl":"#[sqlfunc(sqlname = \"bit_count\")] fn bit_count_bytes<'a>(a: &'a[u8]) -> Result","body_fingerprint":"5f7403bf914230df"}, "bit_length_bytes": {"display":"bit_length","sqlfunc_decl":"#[sqlfunc(sqlname = \"bit_length\")] fn bit_length_bytes<'a>(a: &'a[u8]) -> Result","body_fingerprint":"52e45f0cc4320f34"}, "bit_length_string": {"display":"bit_length","sqlfunc_decl":"#[sqlfunc(sqlname = \"bit_length\")] fn bit_length_string<'a>(a: &'a str) -> Result","body_fingerprint":"2c1bd580cf4dff9a"}, - "bit_not_int16": {"display":"~","sqlfunc_decl":"#[sqlfunc(sqlname = \"~\", preserves_uniqueness = true, inverse = to_unary!(BitNotInt16))] fn bit_not_int16(a: i16) -> i16","body_fingerprint":"de26b65a3695fb67"}, - "bit_not_int32": {"display":"~","sqlfunc_decl":"#[sqlfunc(sqlname = \"~\", preserves_uniqueness = true, inverse = to_unary!(BitNotInt32))] fn bit_not_int32(a: i32) -> i32","body_fingerprint":"de26b65a3695fb67"}, - "bit_not_int64": {"display":"~","sqlfunc_decl":"#[sqlfunc(sqlname = \"~\", preserves_uniqueness = true, inverse = to_unary!(BitNotInt64))] fn bit_not_int64(a: i64) -> i64","body_fingerprint":"de26b65a3695fb67"}, - "bit_not_uint16": {"display":"~","sqlfunc_decl":"#[sqlfunc(sqlname = \"~\", preserves_uniqueness = true, inverse = to_unary!(super::BitNotUint16))] fn bit_not_uint16(a: u16) -> u16","body_fingerprint":"de26b65a3695fb67"}, - "bit_not_uint32": {"display":"~","sqlfunc_decl":"#[sqlfunc(sqlname = \"~\", preserves_uniqueness = true, inverse = to_unary!(super::BitNotUint32))] fn bit_not_uint32(a: u32) -> u32","body_fingerprint":"de26b65a3695fb67"}, - "bit_not_uint64": {"display":"~","sqlfunc_decl":"#[sqlfunc(sqlname = \"~\", preserves_uniqueness = true, inverse = to_unary!(super::BitNotUint64))] fn bit_not_uint64(a: u64) -> u64","body_fingerprint":"de26b65a3695fb67"}, + "bit_not_int16": {"display":"~","sqlfunc_decl":"#[sqlfunc(sqlname = \"~\", preserves_uniqueness = true, inverse = BitNotInt16)] fn bit_not_int16(a: i16) -> i16","body_fingerprint":"de26b65a3695fb67"}, + "bit_not_int32": {"display":"~","sqlfunc_decl":"#[sqlfunc(sqlname = \"~\", preserves_uniqueness = true, inverse = BitNotInt32)] fn bit_not_int32(a: i32) -> i32","body_fingerprint":"de26b65a3695fb67"}, + "bit_not_int64": {"display":"~","sqlfunc_decl":"#[sqlfunc(sqlname = \"~\", preserves_uniqueness = true, inverse = BitNotInt64)] fn bit_not_int64(a: i64) -> i64","body_fingerprint":"de26b65a3695fb67"}, + "bit_not_uint16": {"display":"~","sqlfunc_decl":"#[sqlfunc(sqlname = \"~\", preserves_uniqueness = true, inverse = super::BitNotUint16)] fn bit_not_uint16(a: u16) -> u16","body_fingerprint":"de26b65a3695fb67"}, + "bit_not_uint32": {"display":"~","sqlfunc_decl":"#[sqlfunc(sqlname = \"~\", preserves_uniqueness = true, inverse = super::BitNotUint32)] fn bit_not_uint32(a: u32) -> u32","body_fingerprint":"de26b65a3695fb67"}, + "bit_not_uint64": {"display":"~","sqlfunc_decl":"#[sqlfunc(sqlname = \"~\", preserves_uniqueness = true, inverse = super::BitNotUint64)] fn bit_not_uint64(a: u64) -> u64","body_fingerprint":"de26b65a3695fb67"}, "byte_length_bytes": {"display":"octet_length","sqlfunc_decl":"#[sqlfunc(sqlname = \"octet_length\")] fn byte_length_bytes<'a>(a: &'a[u8]) -> Result","body_fingerprint":"792df15254a6dd62"}, "byte_length_string": {"display":"octet_length","sqlfunc_decl":"#[sqlfunc(sqlname = \"octet_length\")] fn byte_length_string<'a>(a: &'a str) -> Result","body_fingerprint":"b0e2845f0e625884"}, "cast_array_to_array": {"display":"arraytoarray","sqlfunc_decl":null,"body_fingerprint":null}, "cast_array_to_jsonb": {"display":"arraytojsonb","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_array_to_list_one_dim": {"display":"arraytolist","sqlfunc_decl":"#[sqlfunc(sqlname = \"arraytolist\", introduces_nulls = false)] fn cast_array_to_list_one_dim<'a, T>(a: Array<'a, T>) -> Result, EvalError>","body_fingerprint":"62ae04042cf94d79"}, + "cast_array_to_list_one_dim": {"display":"arraytolist","sqlfunc_decl":"#[sqlfunc(sqlname = \"arraytolist\")] fn cast_array_to_list_one_dim<'a, T>(a: Array<'a, T>) -> Result, EvalError>","body_fingerprint":"62ae04042cf94d79"}, "cast_array_to_string": {"display":"arraytostr","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_bool_to_int32": {"display":"boolean_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"boolean_to_integer\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt32ToBool), is_monotone = true)] fn cast_bool_to_int32(a: bool) -> i32","body_fingerprint":"593c44fb5565e5fd"}, - "cast_bool_to_int64": {"display":"boolean_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"boolean_to_bigint\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt64ToBool), is_monotone = true)] fn cast_bool_to_int64(a: bool) -> i64","body_fingerprint":"593c44fb5565e5fd"}, - "cast_bool_to_string": {"display":"boolean_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"boolean_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToBool), is_monotone = true)] fn cast_bool_to_string<'a>(a: bool) -> &'a str","body_fingerprint":"c7aac8105dc5f1c7"}, - "cast_bool_to_string_nonstandard": {"display":"boolean_to_nonstandard_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"boolean_to_nonstandard_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToBool), is_monotone = true)] fn cast_bool_to_string_nonstandard<'a>(a: bool) -> &'a str","body_fingerprint":"d01277a32fb3a68d"}, - "cast_bytes_to_string": {"display":"bytea_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"bytea_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToBytes))] fn cast_bytes_to_string(a: &[u8]) -> String","body_fingerprint":"358eaa033dcd2a74"}, - "cast_char_to_string": {"display":"char_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"char_to_text\", preserves_uniqueness = true, is_eliminable_cast = true, inverse = to_unary!(super::CastStringToChar {length: None, fail_on_len: false,}))] fn cast_char_to_string<'a>(a: Char<&'a str>) -> &'a str","body_fingerprint":"ba6f1e61cfed1bde"}, + "cast_bool_to_int32": {"display":"boolean_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"boolean_to_integer\", preserves_uniqueness = true, inverse = super::CastInt32ToBool, is_monotone = true)] fn cast_bool_to_int32(a: bool) -> i32","body_fingerprint":"593c44fb5565e5fd"}, + "cast_bool_to_int64": {"display":"boolean_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"boolean_to_bigint\", preserves_uniqueness = true, inverse = super::CastInt64ToBool, is_monotone = true)] fn cast_bool_to_int64(a: bool) -> i64","body_fingerprint":"593c44fb5565e5fd"}, + "cast_bool_to_string": {"display":"boolean_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"boolean_to_text\", preserves_uniqueness = true, inverse = super::CastStringToBool, is_monotone = true)] fn cast_bool_to_string<'a>(a: bool) -> &'a str","body_fingerprint":"c7aac8105dc5f1c7"}, + "cast_bool_to_string_nonstandard": {"display":"boolean_to_nonstandard_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"boolean_to_nonstandard_text\", preserves_uniqueness = true, inverse = super::CastStringToBool, is_monotone = true)] fn cast_bool_to_string_nonstandard<'a>(a: bool) -> &'a str","body_fingerprint":"d01277a32fb3a68d"}, + "cast_bytes_to_string": {"display":"bytea_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"bytea_to_text\", preserves_uniqueness = true, inverse = super::CastStringToBytes)] fn cast_bytes_to_string(a: &[u8]) -> String","body_fingerprint":"358eaa033dcd2a74"}, + "cast_char_to_string": {"display":"char_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"char_to_text\", preserves_uniqueness = true, is_eliminable_cast = true, inverse = super::CastStringToChar {length: None, fail_on_len: false,})] fn cast_char_to_string<'a>(a: Char<&'a str>) -> &'a str","body_fingerprint":"ba6f1e61cfed1bde"}, "cast_date_to_mz_timestamp": {"display":"date_to_mz_timestamp","sqlfunc_decl":"#[sqlfunc(sqlname = \"date_to_mz_timestamp\", preserves_uniqueness = true, is_monotone = true)] fn cast_date_to_mz_timestamp(a: Date) -> Result","body_fingerprint":"a3e1799a8f942589"}, - "cast_date_to_string": {"display":"date_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"date_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToDate))] fn cast_date_to_string(a: Date) -> String","body_fingerprint":"cb0468f6ec8a742b"}, + "cast_date_to_string": {"display":"date_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"date_to_text\", preserves_uniqueness = true, inverse = super::CastStringToDate)] fn cast_date_to_string(a: Date) -> String","body_fingerprint":"cb0468f6ec8a742b"}, "cast_date_to_timestamp": {"display":"date_to_timestamp","sqlfunc_decl":null,"body_fingerprint":null}, "cast_date_to_timestamp[precision]": {"display":"date_to_timestamp","sqlfunc_decl":null,"body_fingerprint":null}, "cast_date_to_timestamp_tz": {"display":"date_to_timestamp_with_timezone","sqlfunc_decl":null,"body_fingerprint":null}, "cast_date_to_timestamp_tz[precision]": {"display":"date_to_timestamp_with_timezone","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_float32_to_float64": {"display":"real_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_double\", preserves_uniqueness = false, inverse = to_unary!(super::CastFloat64ToFloat32), is_monotone = true)] fn cast_float32_to_float64(a: f32) -> f64","body_fingerprint":"bc3f020571556877"}, - "cast_float32_to_int16": {"display":"real_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_smallint\", preserves_uniqueness = false, inverse = to_unary!(super::CastInt16ToFloat32), is_monotone = true)] fn cast_float32_to_int16(a: f32) -> Result","body_fingerprint":"a61b0421f5cc5491"}, - "cast_float32_to_int32": {"display":"real_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_integer\", preserves_uniqueness = false, inverse = to_unary!(super::CastInt32ToFloat32), is_monotone = true)] fn cast_float32_to_int32(a: f32) -> Result","body_fingerprint":"2c382a851af2f38d"}, - "cast_float32_to_int64": {"display":"real_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_bigint\", preserves_uniqueness = false, inverse = to_unary!(super::CastInt64ToFloat32), is_monotone = true)] fn cast_float32_to_int64(a: f32) -> Result","body_fingerprint":"80675472dd05ab57"}, + "cast_float32_to_float64": {"display":"real_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_double\", preserves_uniqueness = false, inverse = super::CastFloat64ToFloat32, is_monotone = true)] fn cast_float32_to_float64(a: f32) -> f64","body_fingerprint":"bc3f020571556877"}, + "cast_float32_to_int16": {"display":"real_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_smallint\", preserves_uniqueness = false, inverse = super::CastInt16ToFloat32, is_monotone = true)] fn cast_float32_to_int16(a: f32) -> Result","body_fingerprint":"a61b0421f5cc5491"}, + "cast_float32_to_int32": {"display":"real_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_integer\", preserves_uniqueness = false, inverse = super::CastInt32ToFloat32, is_monotone = true)] fn cast_float32_to_int32(a: f32) -> Result","body_fingerprint":"2c382a851af2f38d"}, + "cast_float32_to_int64": {"display":"real_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_bigint\", preserves_uniqueness = false, inverse = super::CastInt64ToFloat32, is_monotone = true)] fn cast_float32_to_int64(a: f32) -> Result","body_fingerprint":"80675472dd05ab57"}, "cast_float32_to_numeric": {"display":"real_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, "cast_float32_to_numeric[scale]": {"display":"real_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_float32_to_string": {"display":"real_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_text\", preserves_uniqueness = false, inverse = to_unary!(super::CastStringToFloat32))] fn cast_float32_to_string(a: f32) -> String","body_fingerprint":"afb99469117094e4"}, - "cast_float32_to_uint16": {"display":"real_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_uint2\", preserves_uniqueness = false, inverse = to_unary!(super::CastUint16ToFloat32), is_monotone = true)] fn cast_float32_to_uint16(a: f32) -> Result","body_fingerprint":"f6e8bfea3622de5c"}, - "cast_float32_to_uint32": {"display":"real_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_uint4\", preserves_uniqueness = false, inverse = to_unary!(super::CastUint32ToFloat32), is_monotone = true)] fn cast_float32_to_uint32(a: f32) -> Result","body_fingerprint":"69f3d9020edb4fb3"}, - "cast_float32_to_uint64": {"display":"real_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_uint8\", preserves_uniqueness = false, inverse = to_unary!(super::CastUint64ToFloat32), is_monotone = true)] fn cast_float32_to_uint64(a: f32) -> Result","body_fingerprint":"adde45c7e5b5b59c"}, - "cast_float64_to_float32": {"display":"double_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"double_to_real\", preserves_uniqueness = false, inverse = to_unary!(super::CastFloat32ToFloat64), is_monotone = true)] fn cast_float64_to_float32(a: f64) -> Result","body_fingerprint":"3f343e5a91c3f24a"}, - "cast_float64_to_int16": {"display":"double_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"double_to_smallint\", preserves_uniqueness = false, inverse = to_unary!(super::CastInt16ToFloat64), is_monotone = true)] fn cast_float64_to_int16(a: f64) -> Result","body_fingerprint":"7b6999b1278d8e02"}, - "cast_float64_to_int32": {"display":"double_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"double_to_integer\", preserves_uniqueness = false, inverse = to_unary!(super::CastInt32ToFloat64), is_monotone = true)] fn cast_float64_to_int32(a: f64) -> Result","body_fingerprint":"3e35668ff852238a"}, - "cast_float64_to_int64": {"display":"f64toi64","sqlfunc_decl":"#[sqlfunc(sqlname = \"f64toi64\", preserves_uniqueness = false, inverse = to_unary!(super::CastInt64ToFloat64), is_monotone = true)] fn cast_float64_to_int64(a: f64) -> Result","body_fingerprint":"eff404954eb17e94"}, + "cast_float32_to_string": {"display":"real_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_text\", preserves_uniqueness = false, inverse = super::CastStringToFloat32)] fn cast_float32_to_string(a: f32) -> String","body_fingerprint":"afb99469117094e4"}, + "cast_float32_to_uint16": {"display":"real_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_uint2\", preserves_uniqueness = false, inverse = super::CastUint16ToFloat32, is_monotone = true)] fn cast_float32_to_uint16(a: f32) -> Result","body_fingerprint":"f6e8bfea3622de5c"}, + "cast_float32_to_uint32": {"display":"real_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_uint4\", preserves_uniqueness = false, inverse = super::CastUint32ToFloat32, is_monotone = true)] fn cast_float32_to_uint32(a: f32) -> Result","body_fingerprint":"69f3d9020edb4fb3"}, + "cast_float32_to_uint64": {"display":"real_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"real_to_uint8\", preserves_uniqueness = false, inverse = super::CastUint64ToFloat32, is_monotone = true)] fn cast_float32_to_uint64(a: f32) -> Result","body_fingerprint":"adde45c7e5b5b59c"}, + "cast_float64_to_float32": {"display":"double_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"double_to_real\", preserves_uniqueness = false, inverse = super::CastFloat32ToFloat64, is_monotone = true)] fn cast_float64_to_float32(a: f64) -> Result","body_fingerprint":"3f343e5a91c3f24a"}, + "cast_float64_to_int16": {"display":"double_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"double_to_smallint\", preserves_uniqueness = false, inverse = super::CastInt16ToFloat64, is_monotone = true)] fn cast_float64_to_int16(a: f64) -> Result","body_fingerprint":"7b6999b1278d8e02"}, + "cast_float64_to_int32": {"display":"double_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"double_to_integer\", preserves_uniqueness = false, inverse = super::CastInt32ToFloat64, is_monotone = true)] fn cast_float64_to_int32(a: f64) -> Result","body_fingerprint":"3e35668ff852238a"}, + "cast_float64_to_int64": {"display":"f64toi64","sqlfunc_decl":"#[sqlfunc(sqlname = \"f64toi64\", preserves_uniqueness = false, inverse = super::CastInt64ToFloat64, is_monotone = true)] fn cast_float64_to_int64(a: f64) -> Result","body_fingerprint":"eff404954eb17e94"}, "cast_float64_to_numeric": {"display":"double_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, "cast_float64_to_numeric[scale]": {"display":"double_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_float64_to_string": {"display":"double_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"double_to_text\", preserves_uniqueness = false, inverse = to_unary!(super::CastStringToFloat64))] fn cast_float64_to_string(a: f64) -> String","body_fingerprint":"ed1946d3de6a3555"}, - "cast_float64_to_uint16": {"display":"double_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"double_to_uint2\", preserves_uniqueness = false, inverse = to_unary!(super::CastUint16ToFloat64), is_monotone = true)] fn cast_float64_to_uint16(a: f64) -> Result","body_fingerprint":"9cdb81df1272809a"}, - "cast_float64_to_uint32": {"display":"double_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"double_to_uint4\", preserves_uniqueness = false, inverse = to_unary!(super::CastUint32ToFloat64), is_monotone = true)] fn cast_float64_to_uint32(a: f64) -> Result","body_fingerprint":"1fd96b053ec266ec"}, - "cast_float64_to_uint64": {"display":"double_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"double_to_uint8\", preserves_uniqueness = false, inverse = to_unary!(super::CastUint64ToFloat64), is_monotone = true)] fn cast_float64_to_uint64(a: f64) -> Result","body_fingerprint":"98051c71e907f216"}, - "cast_int16_to_float32": {"display":"smallint_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_real\", preserves_uniqueness = true, inverse = to_unary!(super::CastFloat32ToInt16), is_monotone = true)] fn cast_int16_to_float32(a: i16) -> f32","body_fingerprint":"6ad73befb8ca2a34"}, - "cast_int16_to_float64": {"display":"smallint_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_double\", preserves_uniqueness = true, inverse = to_unary!(super::CastFloat64ToInt16), is_monotone = true)] fn cast_int16_to_float64(a: i16) -> f64","body_fingerprint":"8a67f84e8764d837"}, - "cast_int16_to_int32": {"display":"smallint_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_integer\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt32ToInt16), is_monotone = true)] fn cast_int16_to_int32(a: i16) -> i32","body_fingerprint":"bc65045bb6754831"}, - "cast_int16_to_int64": {"display":"smallint_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_bigint\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt64ToInt16), is_monotone = true)] fn cast_int16_to_int64(a: i16) -> i64","body_fingerprint":"9c62c1a559decc6e"}, + "cast_float64_to_string": {"display":"double_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"double_to_text\", preserves_uniqueness = false, inverse = super::CastStringToFloat64)] fn cast_float64_to_string(a: f64) -> String","body_fingerprint":"ed1946d3de6a3555"}, + "cast_float64_to_uint16": {"display":"double_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"double_to_uint2\", preserves_uniqueness = false, inverse = super::CastUint16ToFloat64, is_monotone = true)] fn cast_float64_to_uint16(a: f64) -> Result","body_fingerprint":"9cdb81df1272809a"}, + "cast_float64_to_uint32": {"display":"double_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"double_to_uint4\", preserves_uniqueness = false, inverse = super::CastUint32ToFloat64, is_monotone = true)] fn cast_float64_to_uint32(a: f64) -> Result","body_fingerprint":"1fd96b053ec266ec"}, + "cast_float64_to_uint64": {"display":"double_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"double_to_uint8\", preserves_uniqueness = false, inverse = super::CastUint64ToFloat64, is_monotone = true)] fn cast_float64_to_uint64(a: f64) -> Result","body_fingerprint":"98051c71e907f216"}, + "cast_int16_to_float32": {"display":"smallint_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_real\", preserves_uniqueness = true, inverse = super::CastFloat32ToInt16, is_monotone = true)] fn cast_int16_to_float32(a: i16) -> f32","body_fingerprint":"6ad73befb8ca2a34"}, + "cast_int16_to_float64": {"display":"smallint_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_double\", preserves_uniqueness = true, inverse = super::CastFloat64ToInt16, is_monotone = true)] fn cast_int16_to_float64(a: i16) -> f64","body_fingerprint":"8a67f84e8764d837"}, + "cast_int16_to_int32": {"display":"smallint_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_integer\", preserves_uniqueness = true, inverse = super::CastInt32ToInt16, is_monotone = true)] fn cast_int16_to_int32(a: i16) -> i32","body_fingerprint":"bc65045bb6754831"}, + "cast_int16_to_int64": {"display":"smallint_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_bigint\", preserves_uniqueness = true, inverse = super::CastInt64ToInt16, is_monotone = true)] fn cast_int16_to_int64(a: i16) -> i64","body_fingerprint":"9c62c1a559decc6e"}, "cast_int16_to_numeric": {"display":"smallint_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, "cast_int16_to_numeric[scale]": {"display":"smallint_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_int16_to_string": {"display":"smallint_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToInt16))] fn cast_int16_to_string(a: i16) -> String","body_fingerprint":"1317bc80b391426b"}, - "cast_int16_to_uint16": {"display":"smallint_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_uint2\", preserves_uniqueness = true, inverse = to_unary!(super::CastUint16ToInt16), is_monotone = true)] fn cast_int16_to_uint16(a: i16) -> Result","body_fingerprint":"6e1da3fc5a1ea1ec"}, - "cast_int16_to_uint32": {"display":"smallint_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_uint4\", preserves_uniqueness = true, inverse = to_unary!(super::CastUint32ToInt16), is_monotone = true)] fn cast_int16_to_uint32(a: i16) -> Result","body_fingerprint":"a030b0b7248d6b70"}, - "cast_int16_to_uint64": {"display":"smallint_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_uint8\", preserves_uniqueness = true, inverse = to_unary!(super::CastUint64ToInt16), is_monotone = true)] fn cast_int16_to_uint64(a: i16) -> Result","body_fingerprint":"47756d82c581e442"}, - "cast_int2_vector_to_array": {"display":"int2vectortoarray","sqlfunc_decl":"#[sqlfunc(sqlname = \"int2vectortoarray\", is_monotone = true, introduces_nulls = false, output_type_expr = SqlScalarType::Array(Box::from(SqlScalarType::Int16)).nullable(input_type.nullable))] fn cast_int2_vector_to_array<'a>(a: Int2Vector<'a>) -> Array<'a>","body_fingerprint":"ba6f1e61cfed1bde"}, - "cast_int2_vector_to_string": {"display":"int2vectortostr","sqlfunc_decl":"#[sqlfunc(sqlname = \"int2vectortostr\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToInt2Vector))] fn cast_int2_vector_to_string<'a>(a: Int2Vector<'a>) -> Result","body_fingerprint":"aaf9f407dda56cb6"}, - "cast_int32_to_bool": {"display":"integer_to_boolean","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_boolean\", preserves_uniqueness = false, inverse = to_unary!(super::CastBoolToInt32))] fn cast_int32_to_bool(a: i32) -> bool","body_fingerprint":"3d151ef3613e7c96"}, - "cast_int32_to_float32": {"display":"integer_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_real\", preserves_uniqueness = false, inverse = to_unary!(super::CastFloat32ToInt32), is_monotone = true)] fn cast_int32_to_float32(a: i32) -> f32","body_fingerprint":"5cbae9f61a68b6b7"}, - "cast_int32_to_float64": {"display":"integer_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_double\", preserves_uniqueness = true, inverse = to_unary!(super::CastFloat64ToInt32), is_monotone = true)] fn cast_int32_to_float64(a: i32) -> f64","body_fingerprint":"8a67f84e8764d837"}, - "cast_int32_to_int16": {"display":"integer_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_smallint\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt16ToInt32), is_monotone = true)] fn cast_int32_to_int16(a: i32) -> Result","body_fingerprint":"106c56bf06c2a245"}, - "cast_int32_to_int64": {"display":"integer_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_bigint\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt64ToInt32), is_monotone = true)] fn cast_int32_to_int64(a: i32) -> i64","body_fingerprint":"9c62c1a559decc6e"}, + "cast_int16_to_string": {"display":"smallint_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_text\", preserves_uniqueness = true, inverse = super::CastStringToInt16)] fn cast_int16_to_string(a: i16) -> String","body_fingerprint":"1317bc80b391426b"}, + "cast_int16_to_uint16": {"display":"smallint_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_uint2\", preserves_uniqueness = true, inverse = super::CastUint16ToInt16, is_monotone = true)] fn cast_int16_to_uint16(a: i16) -> Result","body_fingerprint":"6e1da3fc5a1ea1ec"}, + "cast_int16_to_uint32": {"display":"smallint_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_uint4\", preserves_uniqueness = true, inverse = super::CastUint32ToInt16, is_monotone = true)] fn cast_int16_to_uint32(a: i16) -> Result","body_fingerprint":"a030b0b7248d6b70"}, + "cast_int16_to_uint64": {"display":"smallint_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"smallint_to_uint8\", preserves_uniqueness = true, inverse = super::CastUint64ToInt16, is_monotone = true)] fn cast_int16_to_uint64(a: i16) -> Result","body_fingerprint":"47756d82c581e442"}, + "cast_int2_vector_to_array": {"display":"int2vectortoarray","sqlfunc_decl":"#[sqlfunc(sqlname = \"int2vectortoarray\", is_monotone = true, output_type_expr = SqlScalarType::Array(Box::from(SqlScalarType::Int16)).nullable(input_type.nullable))] fn cast_int2_vector_to_array<'a>(a: Int2Vector<'a>) -> Array<'a>","body_fingerprint":"ba6f1e61cfed1bde"}, + "cast_int2_vector_to_string": {"display":"int2vectortostr","sqlfunc_decl":"#[sqlfunc(sqlname = \"int2vectortostr\", preserves_uniqueness = true, inverse = super::CastStringToInt2Vector)] fn cast_int2_vector_to_string<'a>(a: Int2Vector<'a>) -> Result","body_fingerprint":"aaf9f407dda56cb6"}, + "cast_int32_to_bool": {"display":"integer_to_boolean","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_boolean\", preserves_uniqueness = false, inverse = super::CastBoolToInt32)] fn cast_int32_to_bool(a: i32) -> bool","body_fingerprint":"3d151ef3613e7c96"}, + "cast_int32_to_float32": {"display":"integer_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_real\", preserves_uniqueness = false, inverse = super::CastFloat32ToInt32, is_monotone = true)] fn cast_int32_to_float32(a: i32) -> f32","body_fingerprint":"5cbae9f61a68b6b7"}, + "cast_int32_to_float64": {"display":"integer_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_double\", preserves_uniqueness = true, inverse = super::CastFloat64ToInt32, is_monotone = true)] fn cast_int32_to_float64(a: i32) -> f64","body_fingerprint":"8a67f84e8764d837"}, + "cast_int32_to_int16": {"display":"integer_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_smallint\", preserves_uniqueness = true, inverse = super::CastInt16ToInt32, is_monotone = true)] fn cast_int32_to_int16(a: i32) -> Result","body_fingerprint":"106c56bf06c2a245"}, + "cast_int32_to_int64": {"display":"integer_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_bigint\", preserves_uniqueness = true, inverse = super::CastInt64ToInt32, is_monotone = true)] fn cast_int32_to_int64(a: i32) -> i64","body_fingerprint":"9c62c1a559decc6e"}, "cast_int32_to_mz_timestamp": {"display":"integer_to_mz_timestamp","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_mz_timestamp\", preserves_uniqueness = true, is_monotone = true)] fn cast_int32_to_mz_timestamp(a: i32) -> Result","body_fingerprint":"fc5c2486a53be81d"}, "cast_int32_to_numeric": {"display":"integer_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, "cast_int32_to_numeric[scale]": {"display":"integer_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_int32_to_oid": {"display":"integer_to_oid","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_oid\", preserves_uniqueness = true, inverse = to_unary!(super::CastOidToInt32))] fn cast_int32_to_oid(a: i32) -> Oid","body_fingerprint":"d1031e0d6bd03d94"}, - "cast_int32_to_pg_legacy_char": {"display":"integer_to_\"char\"","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_\\\"char\\\"\", preserves_uniqueness = true, inverse = to_unary!(super::CastPgLegacyCharToInt32))] fn cast_int32_to_pg_legacy_char(a: i32) -> Result","body_fingerprint":"1e3bbcb1147be1fe"}, - "cast_int32_to_string": {"display":"integer_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToInt32))] fn cast_int32_to_string(a: i32) -> String","body_fingerprint":"05e6cfd50d0297f5"}, - "cast_int32_to_uint16": {"display":"integer_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_uint2\", preserves_uniqueness = true, inverse = to_unary!(super::CastUint16ToInt32), is_monotone = true)] fn cast_int32_to_uint16(a: i32) -> Result","body_fingerprint":"6e1da3fc5a1ea1ec"}, - "cast_int32_to_uint32": {"display":"integer_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_uint4\", preserves_uniqueness = true, inverse = to_unary!(super::CastUint32ToInt32), is_monotone = true)] fn cast_int32_to_uint32(a: i32) -> Result","body_fingerprint":"a030b0b7248d6b70"}, - "cast_int32_to_uint64": {"display":"integer_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_uint8\", preserves_uniqueness = true, inverse = to_unary!(super::CastUint64ToInt32), is_monotone = true)] fn cast_int32_to_uint64(a: i32) -> Result","body_fingerprint":"47756d82c581e442"}, - "cast_int64_to_bool": {"display":"bigint_to_boolean","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_boolean\", preserves_uniqueness = false, inverse = to_unary!(super::CastBoolToInt64))] fn cast_int64_to_bool(a: i64) -> bool","body_fingerprint":"3d151ef3613e7c96"}, - "cast_int64_to_float32": {"display":"bigint_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_real\", preserves_uniqueness = false, inverse = to_unary!(super::CastFloat32ToInt64), is_monotone = true)] fn cast_int64_to_float32(a: i64) -> f32","body_fingerprint":"9e9699b80ff60ed3"}, - "cast_int64_to_float64": {"display":"bigint_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_double\", preserves_uniqueness = false, inverse = to_unary!(super::CastFloat64ToInt64), is_monotone = true)] fn cast_int64_to_float64(a: i64) -> f64","body_fingerprint":"c9df6ab828789d9a"}, - "cast_int64_to_int16": {"display":"bigint_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_smallint\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt16ToInt64), is_monotone = true)] fn cast_int64_to_int16(a: i64) -> Result","body_fingerprint":"106c56bf06c2a245"}, - "cast_int64_to_int32": {"display":"bigint_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_integer\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt32ToInt64), is_monotone = true)] fn cast_int64_to_int32(a: i64) -> Result","body_fingerprint":"de1123491c3c9cf9"}, + "cast_int32_to_oid": {"display":"integer_to_oid","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_oid\", preserves_uniqueness = true, inverse = super::CastOidToInt32)] fn cast_int32_to_oid(a: i32) -> Oid","body_fingerprint":"d1031e0d6bd03d94"}, + "cast_int32_to_pg_legacy_char": {"display":"integer_to_\"char\"","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_\\\"char\\\"\", preserves_uniqueness = true, inverse = super::CastPgLegacyCharToInt32)] fn cast_int32_to_pg_legacy_char(a: i32) -> Result","body_fingerprint":"1e3bbcb1147be1fe"}, + "cast_int32_to_string": {"display":"integer_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_text\", preserves_uniqueness = true, inverse = super::CastStringToInt32)] fn cast_int32_to_string(a: i32) -> String","body_fingerprint":"05e6cfd50d0297f5"}, + "cast_int32_to_uint16": {"display":"integer_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_uint2\", preserves_uniqueness = true, inverse = super::CastUint16ToInt32, is_monotone = true)] fn cast_int32_to_uint16(a: i32) -> Result","body_fingerprint":"6e1da3fc5a1ea1ec"}, + "cast_int32_to_uint32": {"display":"integer_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_uint4\", preserves_uniqueness = true, inverse = super::CastUint32ToInt32, is_monotone = true)] fn cast_int32_to_uint32(a: i32) -> Result","body_fingerprint":"a030b0b7248d6b70"}, + "cast_int32_to_uint64": {"display":"integer_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"integer_to_uint8\", preserves_uniqueness = true, inverse = super::CastUint64ToInt32, is_monotone = true)] fn cast_int32_to_uint64(a: i32) -> Result","body_fingerprint":"47756d82c581e442"}, + "cast_int64_to_bool": {"display":"bigint_to_boolean","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_boolean\", preserves_uniqueness = false, inverse = super::CastBoolToInt64)] fn cast_int64_to_bool(a: i64) -> bool","body_fingerprint":"3d151ef3613e7c96"}, + "cast_int64_to_float32": {"display":"bigint_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_real\", preserves_uniqueness = false, inverse = super::CastFloat32ToInt64, is_monotone = true)] fn cast_int64_to_float32(a: i64) -> f32","body_fingerprint":"9e9699b80ff60ed3"}, + "cast_int64_to_float64": {"display":"bigint_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_double\", preserves_uniqueness = false, inverse = super::CastFloat64ToInt64, is_monotone = true)] fn cast_int64_to_float64(a: i64) -> f64","body_fingerprint":"c9df6ab828789d9a"}, + "cast_int64_to_int16": {"display":"bigint_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_smallint\", preserves_uniqueness = true, inverse = super::CastInt16ToInt64, is_monotone = true)] fn cast_int64_to_int16(a: i64) -> Result","body_fingerprint":"106c56bf06c2a245"}, + "cast_int64_to_int32": {"display":"bigint_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_integer\", preserves_uniqueness = true, inverse = super::CastInt32ToInt64, is_monotone = true)] fn cast_int64_to_int32(a: i64) -> Result","body_fingerprint":"de1123491c3c9cf9"}, "cast_int64_to_mz_timestamp": {"display":"bigint_to_mz_timestamp","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_mz_timestamp\", preserves_uniqueness = true, is_monotone = true)] fn cast_int64_to_mz_timestamp(a: i64) -> Result","body_fingerprint":"b76dcbb5b02bb973"}, "cast_int64_to_numeric": {"display":"bigint_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, "cast_int64_to_numeric[scale]": {"display":"bigint_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_int64_to_oid": {"display":"bigint_to_oid","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_oid\", preserves_uniqueness = true, inverse = to_unary!(super::CastOidToInt64))] fn cast_int64_to_oid(a: i64) -> Result","body_fingerprint":"bf43df4e3acd363e"}, - "cast_int64_to_string": {"display":"bigint_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToInt64))] fn cast_int64_to_string(a: i64) -> String","body_fingerprint":"87b2e153d32bdbe4"}, - "cast_int64_to_uint16": {"display":"bigint_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_uint2\", preserves_uniqueness = true, inverse = to_unary!(super::CastUint16ToInt64), is_monotone = true)] fn cast_int64_to_uint16(a: i64) -> Result","body_fingerprint":"6e1da3fc5a1ea1ec"}, - "cast_int64_to_uint32": {"display":"bigint_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_uint4\", preserves_uniqueness = true, inverse = to_unary!(super::CastUint32ToInt64), is_monotone = true)] fn cast_int64_to_uint32(a: i64) -> Result","body_fingerprint":"a030b0b7248d6b70"}, - "cast_int64_to_uint64": {"display":"bigint_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_uint8\", preserves_uniqueness = true, inverse = to_unary!(super::CastUint64ToInt64), is_monotone = true)] fn cast_int64_to_uint64(a: i64) -> Result","body_fingerprint":"47756d82c581e442"}, - "cast_interval_to_string": {"display":"interval_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"interval_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToInterval))] fn cast_interval_to_string(a: Interval) -> String","body_fingerprint":"669273f352f36790"}, - "cast_interval_to_time": {"display":"interval_to_time","sqlfunc_decl":"#[sqlfunc(sqlname = \"interval_to_time\", preserves_uniqueness = false, inverse = to_unary!(super::CastTimeToInterval))] fn cast_interval_to_time(i: Interval) -> NaiveTime","body_fingerprint":"10491f8adbe1a0c8"}, + "cast_int64_to_oid": {"display":"bigint_to_oid","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_oid\", preserves_uniqueness = true, inverse = super::CastOidToInt64)] fn cast_int64_to_oid(a: i64) -> Result","body_fingerprint":"bf43df4e3acd363e"}, + "cast_int64_to_string": {"display":"bigint_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_text\", preserves_uniqueness = true, inverse = super::CastStringToInt64)] fn cast_int64_to_string(a: i64) -> String","body_fingerprint":"87b2e153d32bdbe4"}, + "cast_int64_to_uint16": {"display":"bigint_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_uint2\", preserves_uniqueness = true, inverse = super::CastUint16ToInt64, is_monotone = true)] fn cast_int64_to_uint16(a: i64) -> Result","body_fingerprint":"6e1da3fc5a1ea1ec"}, + "cast_int64_to_uint32": {"display":"bigint_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_uint4\", preserves_uniqueness = true, inverse = super::CastUint32ToInt64, is_monotone = true)] fn cast_int64_to_uint32(a: i64) -> Result","body_fingerprint":"a030b0b7248d6b70"}, + "cast_int64_to_uint64": {"display":"bigint_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"bigint_to_uint8\", preserves_uniqueness = true, inverse = super::CastUint64ToInt64, is_monotone = true)] fn cast_int64_to_uint64(a: i64) -> Result","body_fingerprint":"47756d82c581e442"}, + "cast_interval_to_string": {"display":"interval_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"interval_to_text\", preserves_uniqueness = true, inverse = super::CastStringToInterval)] fn cast_interval_to_string(a: Interval) -> String","body_fingerprint":"669273f352f36790"}, + "cast_interval_to_time": {"display":"interval_to_time","sqlfunc_decl":"#[sqlfunc(sqlname = \"interval_to_time\", preserves_uniqueness = false, inverse = super::CastTimeToInterval)] fn cast_interval_to_time(i: Interval) -> NaiveTime","body_fingerprint":"10491f8adbe1a0c8"}, "cast_jsonb_to_bool": {"display":"jsonb_to_boolean","sqlfunc_decl":"#[sqlfunc(sqlname = \"jsonb_to_boolean\", is_monotone = true)] fn cast_jsonb_to_bool<'a>(a: JsonbRef<'a>) -> Result","body_fingerprint":"54d24ddd4a4d16dd"}, "cast_jsonb_to_float32": {"display":"jsonb_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"jsonb_to_real\", is_monotone = true)] fn cast_jsonb_to_float32<'a>(a: JsonbRef<'a>) -> Result","body_fingerprint":"a360187e1f3c2707"}, "cast_jsonb_to_float64": {"display":"jsonb_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"jsonb_to_double\", is_monotone = true)] fn cast_jsonb_to_float64<'a>(a: JsonbRef<'a>) -> Result","body_fingerprint":"28f4795819c8834b"}, @@ -331,123 +331,123 @@ "cast_jsonb_to_int64": {"display":"jsonb_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"jsonb_to_bigint\", is_monotone = true)] fn cast_jsonb_to_int64<'a>(a: JsonbRef<'a>) -> Result","body_fingerprint":"56297e8c84111140"}, "cast_jsonb_to_numeric": {"display":"jsonb_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, "cast_jsonb_to_numeric[scale]": {"display":"jsonb_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_jsonb_to_string": {"display":"jsonb_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"jsonb_to_text\", preserves_uniqueness = false, inverse = to_unary!(super::CastStringToJsonb))] fn cast_jsonb_to_string<'a>(a: JsonbRef<'a>) -> String","body_fingerprint":"d2322a46ec145a41"}, + "cast_jsonb_to_string": {"display":"jsonb_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"jsonb_to_text\", preserves_uniqueness = false, inverse = super::CastStringToJsonb)] fn cast_jsonb_to_string<'a>(a: JsonbRef<'a>) -> String","body_fingerprint":"d2322a46ec145a41"}, "cast_jsonbable_to_jsonb": {"display":"jsonbable_to_jsonb","sqlfunc_decl":"#[sqlfunc(sqlname = \"jsonbable_to_jsonb\")] fn cast_jsonbable_to_jsonb<'a>(a: JsonbRef<'a>) -> JsonbRef<'a>","body_fingerprint":"ae0d031ddd541adf"}, "cast_list1_to_list2": {"display":"list1tolist2","sqlfunc_decl":null,"body_fingerprint":null}, "cast_list_to_jsonb": {"display":"listtojsonb","sqlfunc_decl":null,"body_fingerprint":null}, "cast_list_to_string": {"display":"listtostr","sqlfunc_decl":null,"body_fingerprint":null}, "cast_map_to_string": {"display":"maptostr","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_mz_timestamp_to_string": {"display":"mz_timestamp_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"mz_timestamp_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToMzTimestamp))] fn cast_mz_timestamp_to_string(a: Timestamp) -> String","body_fingerprint":"01add8290ea7f57b"}, - "cast_mz_timestamp_to_timestamp": {"display":"mz_timestamp_to_timestamp","sqlfunc_decl":"#[sqlfunc(sqlname = \"mz_timestamp_to_timestamp\", preserves_uniqueness = true, inverse = to_unary!(super::CastTimestampToMzTimestamp))] fn cast_mz_timestamp_to_timestamp(a: Timestamp,) -> Result, EvalError>","body_fingerprint":"bdf56920d511c8d8"}, - "cast_mz_timestamp_to_timestamp_tz": {"display":"mz_timestamp_to_timestamp_tz","sqlfunc_decl":"#[sqlfunc(sqlname = \"mz_timestamp_to_timestamp_tz\", preserves_uniqueness = true, inverse = to_unary!(super::CastTimestampTzToMzTimestamp))] fn cast_mz_timestamp_to_timestamp_tz(a: Timestamp,) -> Result>, EvalError>","body_fingerprint":"caec6fa728d2239c"}, - "cast_numeric_to_float32": {"display":"numeric_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_real\", preserves_uniqueness = false, inverse = to_unary!(super::CastFloat32ToNumeric(None)), is_monotone = true)] fn cast_numeric_to_float32(a: Numeric) -> Result","body_fingerprint":"6c06659074de0284"}, - "cast_numeric_to_float64": {"display":"numeric_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_double\", preserves_uniqueness = false, inverse = to_unary!(super::CastFloat64ToNumeric(None)), is_monotone = true)] fn cast_numeric_to_float64(a: Numeric) -> Result","body_fingerprint":"871d8812540ac370"}, - "cast_numeric_to_int16": {"display":"numeric_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_smallint\", preserves_uniqueness = false, inverse = to_unary!(super::CastInt16ToNumeric(None)), is_monotone = true)] fn cast_numeric_to_int16(mut a: Numeric) -> Result","body_fingerprint":"77481fedc95d9314"}, - "cast_numeric_to_int32": {"display":"numeric_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_integer\", preserves_uniqueness = false, inverse = to_unary!(super::CastInt32ToNumeric(None)), is_monotone = true)] fn cast_numeric_to_int32(mut a: Numeric) -> Result","body_fingerprint":"ed75fe1c956193df"}, - "cast_numeric_to_int64": {"display":"numeric_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_bigint\", preserves_uniqueness = false, inverse = to_unary!(super::CastInt64ToNumeric(None)), is_monotone = true)] fn cast_numeric_to_int64(mut a: Numeric) -> Result","body_fingerprint":"cdde936dce05d663"}, + "cast_mz_timestamp_to_string": {"display":"mz_timestamp_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"mz_timestamp_to_text\", preserves_uniqueness = true, inverse = super::CastStringToMzTimestamp)] fn cast_mz_timestamp_to_string(a: Timestamp) -> String","body_fingerprint":"01add8290ea7f57b"}, + "cast_mz_timestamp_to_timestamp": {"display":"mz_timestamp_to_timestamp","sqlfunc_decl":"#[sqlfunc(sqlname = \"mz_timestamp_to_timestamp\", preserves_uniqueness = true, inverse = super::CastTimestampToMzTimestamp)] fn cast_mz_timestamp_to_timestamp(a: Timestamp,) -> Result, EvalError>","body_fingerprint":"bdf56920d511c8d8"}, + "cast_mz_timestamp_to_timestamp_tz": {"display":"mz_timestamp_to_timestamp_tz","sqlfunc_decl":"#[sqlfunc(sqlname = \"mz_timestamp_to_timestamp_tz\", preserves_uniqueness = true, inverse = super::CastTimestampTzToMzTimestamp)] fn cast_mz_timestamp_to_timestamp_tz(a: Timestamp,) -> Result>, EvalError>","body_fingerprint":"caec6fa728d2239c"}, + "cast_numeric_to_float32": {"display":"numeric_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_real\", preserves_uniqueness = false, inverse = super::CastFloat32ToNumeric(None), is_monotone = true)] fn cast_numeric_to_float32(a: Numeric) -> Result","body_fingerprint":"6c06659074de0284"}, + "cast_numeric_to_float64": {"display":"numeric_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_double\", preserves_uniqueness = false, inverse = super::CastFloat64ToNumeric(None), is_monotone = true)] fn cast_numeric_to_float64(a: Numeric) -> Result","body_fingerprint":"871d8812540ac370"}, + "cast_numeric_to_int16": {"display":"numeric_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_smallint\", preserves_uniqueness = false, inverse = super::CastInt16ToNumeric(None), is_monotone = true)] fn cast_numeric_to_int16(mut a: Numeric) -> Result","body_fingerprint":"77481fedc95d9314"}, + "cast_numeric_to_int32": {"display":"numeric_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_integer\", preserves_uniqueness = false, inverse = super::CastInt32ToNumeric(None), is_monotone = true)] fn cast_numeric_to_int32(mut a: Numeric) -> Result","body_fingerprint":"ed75fe1c956193df"}, + "cast_numeric_to_int64": {"display":"numeric_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_bigint\", preserves_uniqueness = false, inverse = super::CastInt64ToNumeric(None), is_monotone = true)] fn cast_numeric_to_int64(mut a: Numeric) -> Result","body_fingerprint":"cdde936dce05d663"}, "cast_numeric_to_mz_timestamp": {"display":"numeric_to_mz_timestamp","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_mz_timestamp\", preserves_uniqueness = true, is_monotone = true)] fn cast_numeric_to_mz_timestamp(a: Numeric) -> Result","body_fingerprint":"b76dcbb5b02bb973"}, - "cast_numeric_to_string": {"display":"numeric_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_text\", preserves_uniqueness = false, inverse = to_unary!(super::CastStringToNumeric(None)))] fn cast_numeric_to_string(a: Numeric) -> String","body_fingerprint":"6d385094e70a788b"}, - "cast_numeric_to_uint16": {"display":"numeric_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_uint2\", preserves_uniqueness = false, inverse = to_unary!(super::CastUint16ToNumeric(None)), is_monotone = true)] fn cast_numeric_to_uint16(mut a: Numeric) -> Result","body_fingerprint":"760b6946b443d612"}, - "cast_numeric_to_uint32": {"display":"numeric_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_uint4\", preserves_uniqueness = false, inverse = to_unary!(super::CastUint32ToNumeric(None)), is_monotone = true)] fn cast_numeric_to_uint32(mut a: Numeric) -> Result","body_fingerprint":"8c42b9414d05f99a"}, - "cast_numeric_to_uint64": {"display":"numeric_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_uint8\", preserves_uniqueness = false, inverse = to_unary!(super::CastUint64ToNumeric(None)), is_monotone = true)] fn cast_numeric_to_uint64(mut a: Numeric) -> Result","body_fingerprint":"02049691c2bf3324"}, - "cast_oid_to_int32": {"display":"oid_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"oid_to_integer\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt32ToOid))] fn cast_oid_to_int32(a: Oid) -> i32","body_fingerprint":"8640976e71e6dabf"}, - "cast_oid_to_int64": {"display":"oid_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"oid_to_bigint\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt64ToOid))] fn cast_oid_to_int64(a: Oid) -> i64","body_fingerprint":"51f755d07bbeb47c"}, - "cast_oid_to_reg_class": {"display":"oidtoregclass","sqlfunc_decl":"#[sqlfunc(sqlname = \"oidtoregclass\", preserves_uniqueness = true, inverse = to_unary!(super::CastRegClassToOid))] fn cast_oid_to_reg_class(a: Oid) -> RegClass","body_fingerprint":"e7da11fedd409f5b"}, - "cast_oid_to_reg_proc": {"display":"oidtoregproc","sqlfunc_decl":"#[sqlfunc(sqlname = \"oidtoregproc\", preserves_uniqueness = true, inverse = to_unary!(super::CastRegProcToOid))] fn cast_oid_to_reg_proc(a: Oid) -> RegProc","body_fingerprint":"f32b107253804abb"}, - "cast_oid_to_reg_type": {"display":"oidtoregtype","sqlfunc_decl":"#[sqlfunc(sqlname = \"oidtoregtype\", preserves_uniqueness = true, inverse = to_unary!(super::CastRegTypeToOid))] fn cast_oid_to_reg_type(a: Oid) -> RegType","body_fingerprint":"c75d209f0f3ed94b"}, - "cast_oid_to_string": {"display":"oid_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"oid_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToOid))] fn cast_oid_to_string(a: Oid) -> String","body_fingerprint":"27da34f3f80e857a"}, - "cast_pg_legacy_char_to_char": {"display":"\"char\"_to_char","sqlfunc_decl":"#[sqlfunc(sqlname = \"\\\"char\\\"_to_char\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToPgLegacyChar))] fn cast_pg_legacy_char_to_char(a: PgLegacyChar) -> Result, EvalError>","body_fingerprint":"a03aad3766040029"}, - "cast_pg_legacy_char_to_int32": {"display":"\"char\"_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"\\\"char\\\"_to_integer\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt32ToPgLegacyChar))] fn cast_pg_legacy_char_to_int32(a: PgLegacyChar) -> i32","body_fingerprint":"1976be6ad62975f3"}, - "cast_pg_legacy_char_to_string": {"display":"\"char\"_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"\\\"char\\\"_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToPgLegacyChar))] fn cast_pg_legacy_char_to_string(a: PgLegacyChar) -> Result","body_fingerprint":"a6ca26834f5d02bc"}, - "cast_pg_legacy_char_to_var_char": {"display":"\"char\"_to_varchar","sqlfunc_decl":"#[sqlfunc(sqlname = \"\\\"char\\\"_to_varchar\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToVarChar {fail_on_len: false, length: Some(VarCharMaxLength::try_from(1).unwrap()),}))] fn cast_pg_legacy_char_to_var_char(a: PgLegacyChar) -> Result, EvalError>","body_fingerprint":"757b2f161f95b71c"}, + "cast_numeric_to_string": {"display":"numeric_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_text\", preserves_uniqueness = false, inverse = super::CastStringToNumeric(None))] fn cast_numeric_to_string(a: Numeric) -> String","body_fingerprint":"6d385094e70a788b"}, + "cast_numeric_to_uint16": {"display":"numeric_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_uint2\", preserves_uniqueness = false, inverse = super::CastUint16ToNumeric(None), is_monotone = true)] fn cast_numeric_to_uint16(mut a: Numeric) -> Result","body_fingerprint":"760b6946b443d612"}, + "cast_numeric_to_uint32": {"display":"numeric_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_uint4\", preserves_uniqueness = false, inverse = super::CastUint32ToNumeric(None), is_monotone = true)] fn cast_numeric_to_uint32(mut a: Numeric) -> Result","body_fingerprint":"8c42b9414d05f99a"}, + "cast_numeric_to_uint64": {"display":"numeric_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"numeric_to_uint8\", preserves_uniqueness = false, inverse = super::CastUint64ToNumeric(None), is_monotone = true)] fn cast_numeric_to_uint64(mut a: Numeric) -> Result","body_fingerprint":"02049691c2bf3324"}, + "cast_oid_to_int32": {"display":"oid_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"oid_to_integer\", preserves_uniqueness = true, inverse = super::CastInt32ToOid)] fn cast_oid_to_int32(a: Oid) -> i32","body_fingerprint":"8640976e71e6dabf"}, + "cast_oid_to_int64": {"display":"oid_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"oid_to_bigint\", preserves_uniqueness = true, inverse = super::CastInt64ToOid)] fn cast_oid_to_int64(a: Oid) -> i64","body_fingerprint":"51f755d07bbeb47c"}, + "cast_oid_to_reg_class": {"display":"oidtoregclass","sqlfunc_decl":"#[sqlfunc(sqlname = \"oidtoregclass\", preserves_uniqueness = true, inverse = super::CastRegClassToOid)] fn cast_oid_to_reg_class(a: Oid) -> RegClass","body_fingerprint":"e7da11fedd409f5b"}, + "cast_oid_to_reg_proc": {"display":"oidtoregproc","sqlfunc_decl":"#[sqlfunc(sqlname = \"oidtoregproc\", preserves_uniqueness = true, inverse = super::CastRegProcToOid)] fn cast_oid_to_reg_proc(a: Oid) -> RegProc","body_fingerprint":"f32b107253804abb"}, + "cast_oid_to_reg_type": {"display":"oidtoregtype","sqlfunc_decl":"#[sqlfunc(sqlname = \"oidtoregtype\", preserves_uniqueness = true, inverse = super::CastRegTypeToOid)] fn cast_oid_to_reg_type(a: Oid) -> RegType","body_fingerprint":"c75d209f0f3ed94b"}, + "cast_oid_to_string": {"display":"oid_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"oid_to_text\", preserves_uniqueness = true, inverse = super::CastStringToOid)] fn cast_oid_to_string(a: Oid) -> String","body_fingerprint":"27da34f3f80e857a"}, + "cast_pg_legacy_char_to_char": {"display":"\"char\"_to_char","sqlfunc_decl":"#[sqlfunc(sqlname = \"\\\"char\\\"_to_char\", preserves_uniqueness = true, inverse = super::CastStringToPgLegacyChar)] fn cast_pg_legacy_char_to_char(a: PgLegacyChar) -> Result, EvalError>","body_fingerprint":"a03aad3766040029"}, + "cast_pg_legacy_char_to_int32": {"display":"\"char\"_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"\\\"char\\\"_to_integer\", preserves_uniqueness = true, inverse = super::CastInt32ToPgLegacyChar)] fn cast_pg_legacy_char_to_int32(a: PgLegacyChar) -> i32","body_fingerprint":"1976be6ad62975f3"}, + "cast_pg_legacy_char_to_string": {"display":"\"char\"_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"\\\"char\\\"_to_text\", preserves_uniqueness = true, inverse = super::CastStringToPgLegacyChar)] fn cast_pg_legacy_char_to_string(a: PgLegacyChar) -> Result","body_fingerprint":"a6ca26834f5d02bc"}, + "cast_pg_legacy_char_to_var_char": {"display":"\"char\"_to_varchar","sqlfunc_decl":"#[sqlfunc(sqlname = \"\\\"char\\\"_to_varchar\", preserves_uniqueness = true, inverse = super::CastStringToVarChar {fail_on_len: false, length: Some(VarCharMaxLength::try_from(1).unwrap()),})] fn cast_pg_legacy_char_to_var_char(a: PgLegacyChar) -> Result, EvalError>","body_fingerprint":"757b2f161f95b71c"}, "cast_range_to_string": {"display":"rangetostr","sqlfunc_decl":null,"body_fingerprint":null}, "cast_record1_to_record2": {"display":"record1torecord2","sqlfunc_decl":null,"body_fingerprint":null}, "cast_record_to_string": {"display":"recordtostr","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_reg_class_to_oid": {"display":"regclasstooid","sqlfunc_decl":"#[sqlfunc(sqlname = \"regclasstooid\", preserves_uniqueness = true, inverse = to_unary!(super::CastOidToRegClass))] fn cast_reg_class_to_oid(a: RegClass) -> Oid","body_fingerprint":"0c8b6d41527aa7c1"}, - "cast_reg_proc_to_oid": {"display":"regproctooid","sqlfunc_decl":"#[sqlfunc(sqlname = \"regproctooid\", preserves_uniqueness = true, inverse = to_unary!(super::CastOidToRegProc))] fn cast_reg_proc_to_oid(a: RegProc) -> Oid","body_fingerprint":"0c8b6d41527aa7c1"}, - "cast_reg_type_to_oid": {"display":"regtypetooid","sqlfunc_decl":"#[sqlfunc(sqlname = \"regtypetooid\", preserves_uniqueness = true, inverse = to_unary!(super::CastOidToRegType))] fn cast_reg_type_to_oid(a: RegType) -> Oid","body_fingerprint":"0c8b6d41527aa7c1"}, + "cast_reg_class_to_oid": {"display":"regclasstooid","sqlfunc_decl":"#[sqlfunc(sqlname = \"regclasstooid\", preserves_uniqueness = true, inverse = super::CastOidToRegClass)] fn cast_reg_class_to_oid(a: RegClass) -> Oid","body_fingerprint":"0c8b6d41527aa7c1"}, + "cast_reg_proc_to_oid": {"display":"regproctooid","sqlfunc_decl":"#[sqlfunc(sqlname = \"regproctooid\", preserves_uniqueness = true, inverse = super::CastOidToRegProc)] fn cast_reg_proc_to_oid(a: RegProc) -> Oid","body_fingerprint":"0c8b6d41527aa7c1"}, + "cast_reg_type_to_oid": {"display":"regtypetooid","sqlfunc_decl":"#[sqlfunc(sqlname = \"regtypetooid\", preserves_uniqueness = true, inverse = super::CastOidToRegType)] fn cast_reg_type_to_oid(a: RegType) -> Oid","body_fingerprint":"0c8b6d41527aa7c1"}, "cast_string_to_array": {"display":"strtoarray","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_string_to_bool": {"display":"text_to_boolean","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_boolean\", preserves_uniqueness = false, inverse = to_unary!(super::CastBoolToString))] fn cast_string_to_bool<'a>(a: &'a str) -> Result","body_fingerprint":"638f838369c262c5"}, - "cast_string_to_bytes": {"display":"text_to_bytea","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_bytea\", preserves_uniqueness = false, inverse = to_unary!(super::CastBytesToString))] fn cast_string_to_bytes<'a>(a: &'a str) -> Result, EvalError>","body_fingerprint":"65b8f3875b482dc6"}, + "cast_string_to_bool": {"display":"text_to_boolean","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_boolean\", preserves_uniqueness = false, inverse = super::CastBoolToString)] fn cast_string_to_bool<'a>(a: &'a str) -> Result","body_fingerprint":"638f838369c262c5"}, + "cast_string_to_bytes": {"display":"text_to_bytea","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_bytea\", preserves_uniqueness = false, inverse = super::CastBytesToString)] fn cast_string_to_bytes<'a>(a: &'a str) -> Result, EvalError>","body_fingerprint":"65b8f3875b482dc6"}, "cast_string_to_char": {"display":"text_to_char[len=5, fail_on_len=true]","sqlfunc_decl":null,"body_fingerprint":null}, "cast_string_to_char[unbounded]": {"display":"text_to_char[len=unbounded]","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_string_to_date": {"display":"text_to_date","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_date\", preserves_uniqueness = false, inverse = to_unary!(super::CastDateToString))] fn cast_string_to_date<'a>(a: &'a str) -> Result","body_fingerprint":"f3e731f76cc73135"}, - "cast_string_to_float32": {"display":"text_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_real\", preserves_uniqueness = false, inverse = to_unary!(super::CastFloat32ToString))] fn cast_string_to_float32<'a>(a: &'a str) -> Result","body_fingerprint":"91409492a5e89f0a"}, - "cast_string_to_float64": {"display":"text_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_double\", preserves_uniqueness = false, inverse = to_unary!(super::CastFloat64ToString))] fn cast_string_to_float64<'a>(a: &'a str) -> Result","body_fingerprint":"2bc75019f794547f"}, - "cast_string_to_int16": {"display":"text_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_smallint\", preserves_uniqueness = false, inverse = to_unary!(super::CastInt16ToString))] fn cast_string_to_int16<'a>(a: &'a str) -> Result","body_fingerprint":"e0d58271c1dabb79"}, + "cast_string_to_date": {"display":"text_to_date","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_date\", preserves_uniqueness = false, inverse = super::CastDateToString)] fn cast_string_to_date<'a>(a: &'a str) -> Result","body_fingerprint":"f3e731f76cc73135"}, + "cast_string_to_float32": {"display":"text_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_real\", preserves_uniqueness = false, inverse = super::CastFloat32ToString)] fn cast_string_to_float32<'a>(a: &'a str) -> Result","body_fingerprint":"91409492a5e89f0a"}, + "cast_string_to_float64": {"display":"text_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_double\", preserves_uniqueness = false, inverse = super::CastFloat64ToString)] fn cast_string_to_float64<'a>(a: &'a str) -> Result","body_fingerprint":"2bc75019f794547f"}, + "cast_string_to_int16": {"display":"text_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_smallint\", preserves_uniqueness = false, inverse = super::CastInt16ToString)] fn cast_string_to_int16<'a>(a: &'a str) -> Result","body_fingerprint":"e0d58271c1dabb79"}, "cast_string_to_int2_vector": {"display":"strtoint2vector","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_string_to_int32": {"display":"text_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_integer\", preserves_uniqueness = false, inverse = to_unary!(super::CastInt32ToString))] fn cast_string_to_int32<'a>(a: &'a str) -> Result","body_fingerprint":"20e90d93b93143b7"}, - "cast_string_to_int64": {"display":"text_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_bigint\", preserves_uniqueness = false, inverse = to_unary!(super::CastInt64ToString))] fn cast_string_to_int64<'a>(a: &'a str) -> Result","body_fingerprint":"e1660dcc9eb69d4e"}, - "cast_string_to_interval": {"display":"text_to_interval","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_interval\", preserves_uniqueness = false, inverse = to_unary!(super::CastIntervalToString))] fn cast_string_to_interval<'a>(a: &'a str) -> Result","body_fingerprint":"116b4b312967b8fa"}, - "cast_string_to_jsonb": {"display":"text_to_jsonb","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_jsonb\", preserves_uniqueness = false, inverse = to_unary!(super::CastJsonbToString))] fn cast_string_to_jsonb<'a>(a: &'a str) -> Result","body_fingerprint":"a1052efe1c240d42"}, + "cast_string_to_int32": {"display":"text_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_integer\", preserves_uniqueness = false, inverse = super::CastInt32ToString)] fn cast_string_to_int32<'a>(a: &'a str) -> Result","body_fingerprint":"20e90d93b93143b7"}, + "cast_string_to_int64": {"display":"text_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_bigint\", preserves_uniqueness = false, inverse = super::CastInt64ToString)] fn cast_string_to_int64<'a>(a: &'a str) -> Result","body_fingerprint":"e1660dcc9eb69d4e"}, + "cast_string_to_interval": {"display":"text_to_interval","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_interval\", preserves_uniqueness = false, inverse = super::CastIntervalToString)] fn cast_string_to_interval<'a>(a: &'a str) -> Result","body_fingerprint":"116b4b312967b8fa"}, + "cast_string_to_jsonb": {"display":"text_to_jsonb","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_jsonb\", preserves_uniqueness = false, inverse = super::CastJsonbToString)] fn cast_string_to_jsonb<'a>(a: &'a str) -> Result","body_fingerprint":"a1052efe1c240d42"}, "cast_string_to_list": {"display":"strtolist","sqlfunc_decl":null,"body_fingerprint":null}, "cast_string_to_map": {"display":"strtomap","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_string_to_mz_timestamp": {"display":"text_to_mz_timestamp","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_mz_timestamp\", preserves_uniqueness = false, inverse = to_unary!(super::CastMzTimestampToString))] fn cast_string_to_mz_timestamp(a: String) -> Result","body_fingerprint":"cb6d03fd3862d585"}, + "cast_string_to_mz_timestamp": {"display":"text_to_mz_timestamp","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_mz_timestamp\", preserves_uniqueness = false, inverse = super::CastMzTimestampToString)] fn cast_string_to_mz_timestamp(a: String) -> Result","body_fingerprint":"cb6d03fd3862d585"}, "cast_string_to_numeric": {"display":"text_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, "cast_string_to_numeric[scale]": {"display":"text_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_string_to_oid": {"display":"text_to_oid","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_oid\", preserves_uniqueness = false, inverse = to_unary!(super::CastOidToString))] fn cast_string_to_oid<'a>(a: &'a str) -> Result","body_fingerprint":"6abdb6ec1a51e421"}, - "cast_string_to_pg_legacy_char": {"display":"text_to_\"char\"","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_\\\"char\\\"\", preserves_uniqueness = false, inverse = to_unary!(super::CastPgLegacyCharToString))] fn cast_string_to_pg_legacy_char<'a>(a: &'a str) -> PgLegacyChar","body_fingerprint":"0bc2951a9224cbfe"}, + "cast_string_to_oid": {"display":"text_to_oid","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_oid\", preserves_uniqueness = false, inverse = super::CastOidToString)] fn cast_string_to_oid<'a>(a: &'a str) -> Result","body_fingerprint":"6abdb6ec1a51e421"}, + "cast_string_to_pg_legacy_char": {"display":"text_to_\"char\"","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_\\\"char\\\"\", preserves_uniqueness = false, inverse = super::CastPgLegacyCharToString)] fn cast_string_to_pg_legacy_char<'a>(a: &'a str) -> PgLegacyChar","body_fingerprint":"0bc2951a9224cbfe"}, "cast_string_to_pg_legacy_name": {"display":"text_to_name","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_name\", preserves_uniqueness = false)] fn cast_string_to_pg_legacy_name<'a>(a: &'a str) -> PgLegacyName","body_fingerprint":"5e8a8033c5923679"}, "cast_string_to_range": {"display":"strtorange","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_string_to_time": {"display":"text_to_time","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_time\", preserves_uniqueness = false, inverse = to_unary!(super::CastTimeToString))] fn cast_string_to_time<'a>(a: &'a str) -> Result","body_fingerprint":"a77b2ae1f754733a"}, + "cast_string_to_time": {"display":"text_to_time","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_time\", preserves_uniqueness = false, inverse = super::CastTimeToString)] fn cast_string_to_time<'a>(a: &'a str) -> Result","body_fingerprint":"a77b2ae1f754733a"}, "cast_string_to_timestamp": {"display":"text_to_timestamp","sqlfunc_decl":null,"body_fingerprint":null}, "cast_string_to_timestamp[precision]": {"display":"text_to_timestamp","sqlfunc_decl":null,"body_fingerprint":null}, "cast_string_to_timestamp_tz": {"display":"text_to_timestamp_with_time_zone","sqlfunc_decl":null,"body_fingerprint":null}, "cast_string_to_timestamp_tz[precision]": {"display":"text_to_timestamp_with_time_zone","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_string_to_uint16": {"display":"text_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_uint2\", preserves_uniqueness = false, inverse = to_unary!(super::CastUint16ToString))] fn cast_string_to_uint16(a: &str) -> Result","body_fingerprint":"24acd39037db6a64"}, - "cast_string_to_uint32": {"display":"text_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_uint4\", preserves_uniqueness = false, inverse = to_unary!(super::CastUint32ToString))] fn cast_string_to_uint32(a: &str) -> Result","body_fingerprint":"b2084e6a8db500de"}, - "cast_string_to_uint64": {"display":"text_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_uint8\", preserves_uniqueness = false, inverse = to_unary!(super::CastUint64ToString))] fn cast_string_to_uint64(a: &str) -> Result","body_fingerprint":"13e9c822b8db2fd3"}, - "cast_string_to_uuid": {"display":"text_to_uuid","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_uuid\", preserves_uniqueness = false, inverse = to_unary!(super::CastUuidToString))] fn cast_string_to_uuid<'a>(a: &'a str) -> Result","body_fingerprint":"1b842d70222444b4"}, + "cast_string_to_uint16": {"display":"text_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_uint2\", preserves_uniqueness = false, inverse = super::CastUint16ToString)] fn cast_string_to_uint16(a: &str) -> Result","body_fingerprint":"24acd39037db6a64"}, + "cast_string_to_uint32": {"display":"text_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_uint4\", preserves_uniqueness = false, inverse = super::CastUint32ToString)] fn cast_string_to_uint32(a: &str) -> Result","body_fingerprint":"b2084e6a8db500de"}, + "cast_string_to_uint64": {"display":"text_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_uint8\", preserves_uniqueness = false, inverse = super::CastUint64ToString)] fn cast_string_to_uint64(a: &str) -> Result","body_fingerprint":"13e9c822b8db2fd3"}, + "cast_string_to_uuid": {"display":"text_to_uuid","sqlfunc_decl":"#[sqlfunc(sqlname = \"text_to_uuid\", preserves_uniqueness = false, inverse = super::CastUuidToString)] fn cast_string_to_uuid<'a>(a: &'a str) -> Result","body_fingerprint":"1b842d70222444b4"}, "cast_string_to_var_char": {"display":"text_to_varchar[len=5, fail_on_len=true]","sqlfunc_decl":null,"body_fingerprint":null}, "cast_string_to_var_char[unbounded]": {"display":"text_to_varchar[len=unbounded]","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_time_to_interval": {"display":"time_to_interval","sqlfunc_decl":"#[sqlfunc(sqlname = \"time_to_interval\", preserves_uniqueness = true, inverse = to_unary!(super::CastIntervalToTime))] fn cast_time_to_interval(t: NaiveTime) -> Interval","body_fingerprint":"ccddb65c5498d02b"}, - "cast_time_to_string": {"display":"time_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"time_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToTime))] fn cast_time_to_string(a: NaiveTime) -> String","body_fingerprint":"202bf0607fb79334"}, - "cast_timestamp_to_date": {"display":"timestamp_to_date","sqlfunc_decl":"#[sqlfunc(sqlname = \"timestamp_to_date\", preserves_uniqueness = false, inverse = to_unary!(super::CastDateToTimestamp(None)), is_monotone = true)] fn cast_timestamp_to_date(a: CheckedTimestamp) -> Result","body_fingerprint":"a26913c563b50828"}, + "cast_time_to_interval": {"display":"time_to_interval","sqlfunc_decl":"#[sqlfunc(sqlname = \"time_to_interval\", preserves_uniqueness = true, inverse = super::CastIntervalToTime)] fn cast_time_to_interval(t: NaiveTime) -> Interval","body_fingerprint":"ccddb65c5498d02b"}, + "cast_time_to_string": {"display":"time_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"time_to_text\", preserves_uniqueness = true, inverse = super::CastStringToTime)] fn cast_time_to_string(a: NaiveTime) -> String","body_fingerprint":"202bf0607fb79334"}, + "cast_timestamp_to_date": {"display":"timestamp_to_date","sqlfunc_decl":"#[sqlfunc(sqlname = \"timestamp_to_date\", preserves_uniqueness = false, inverse = super::CastDateToTimestamp(None), is_monotone = true)] fn cast_timestamp_to_date(a: CheckedTimestamp) -> Result","body_fingerprint":"a26913c563b50828"}, "cast_timestamp_to_mz_timestamp": {"display":"timestamp_to_mz_timestamp","sqlfunc_decl":"#[sqlfunc(sqlname = \"timestamp_to_mz_timestamp\", is_monotone = true)] fn cast_timestamp_to_mz_timestamp(a: CheckedTimestamp,) -> Result","body_fingerprint":"9f93bf228455a7ac"}, - "cast_timestamp_to_string": {"display":"timestamp_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"timestamp_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToTimestamp(None)))] fn cast_timestamp_to_string(a: CheckedTimestamp) -> String","body_fingerprint":"b46f9f4f8a380d77"}, + "cast_timestamp_to_string": {"display":"timestamp_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"timestamp_to_text\", preserves_uniqueness = true, inverse = super::CastStringToTimestamp(None))] fn cast_timestamp_to_string(a: CheckedTimestamp) -> String","body_fingerprint":"b46f9f4f8a380d77"}, "cast_timestamp_to_time": {"display":"timestamp_to_time","sqlfunc_decl":"#[sqlfunc(sqlname = \"timestamp_to_time\", preserves_uniqueness = false)] fn cast_timestamp_to_time(a: CheckedTimestamp) -> NaiveTime","body_fingerprint":"53eb4feb26b05af6"}, "cast_timestamp_to_timestamp_tz": {"display":"timestamp_to_timestamp_with_time_zone","sqlfunc_decl":null,"body_fingerprint":null}, "cast_timestamp_to_timestamp_tz[widening]": {"display":"timestamp_to_timestamp_with_time_zone","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_timestamp_tz_to_date": {"display":"timestamp_with_time_zone_to_date","sqlfunc_decl":"#[sqlfunc(sqlname = \"timestamp_with_time_zone_to_date\", preserves_uniqueness = false, inverse = to_unary!(super::CastDateToTimestampTz(None)), is_monotone = true)] fn cast_timestamp_tz_to_date(a: CheckedTimestamp>) -> Result","body_fingerprint":"ccad20bb42ff4c4b"}, + "cast_timestamp_tz_to_date": {"display":"timestamp_with_time_zone_to_date","sqlfunc_decl":"#[sqlfunc(sqlname = \"timestamp_with_time_zone_to_date\", preserves_uniqueness = false, inverse = super::CastDateToTimestampTz(None), is_monotone = true)] fn cast_timestamp_tz_to_date(a: CheckedTimestamp>) -> Result","body_fingerprint":"ccad20bb42ff4c4b"}, "cast_timestamp_tz_to_mz_timestamp": {"display":"timestamp_tz_to_mz_timestamp","sqlfunc_decl":"#[sqlfunc(sqlname = \"timestamp_tz_to_mz_timestamp\", is_monotone = true)] fn cast_timestamp_tz_to_mz_timestamp(a: CheckedTimestamp>,) -> Result","body_fingerprint":"21967251d0e3e387"}, - "cast_timestamp_tz_to_string": {"display":"timestamp_with_time_zone_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"timestamp_with_time_zone_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToTimestampTz(None)))] fn cast_timestamp_tz_to_string(a: CheckedTimestamp>) -> String","body_fingerprint":"89e85f6ed441bf9d"}, + "cast_timestamp_tz_to_string": {"display":"timestamp_with_time_zone_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"timestamp_with_time_zone_to_text\", preserves_uniqueness = true, inverse = super::CastStringToTimestampTz(None))] fn cast_timestamp_tz_to_string(a: CheckedTimestamp>) -> String","body_fingerprint":"89e85f6ed441bf9d"}, "cast_timestamp_tz_to_time": {"display":"timestamp_with_time_zone_to_time","sqlfunc_decl":"#[sqlfunc(sqlname = \"timestamp_with_time_zone_to_time\", preserves_uniqueness = false)] fn cast_timestamp_tz_to_time(a: CheckedTimestamp>) -> NaiveTime","body_fingerprint":"37aa04a94ad6e09f"}, "cast_timestamp_tz_to_timestamp": {"display":"timestamp_with_time_zone_to_timestamp","sqlfunc_decl":null,"body_fingerprint":null}, "cast_timestamp_tz_to_timestamp[widening]": {"display":"timestamp_with_time_zone_to_timestamp","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_uint16_to_float32": {"display":"uint2_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_real\", preserves_uniqueness = true, inverse = to_unary!(super::CastFloat32ToUint16), is_monotone = true)] fn cast_uint16_to_float32(a: u16) -> f32","body_fingerprint":"6ad73befb8ca2a34"}, - "cast_uint16_to_float64": {"display":"uint2_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_double\", preserves_uniqueness = true, inverse = to_unary!(super::CastFloat64ToUint16), is_monotone = true)] fn cast_uint16_to_float64(a: u16) -> f64","body_fingerprint":"8a67f84e8764d837"}, - "cast_uint16_to_int16": {"display":"uint2_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_smallint\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt16ToUint16), is_monotone = true)] fn cast_uint16_to_int16(a: u16) -> Result","body_fingerprint":"106c56bf06c2a245"}, - "cast_uint16_to_int32": {"display":"uint2_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_integer\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt32ToUint16), is_monotone = true)] fn cast_uint16_to_int32(a: u16) -> i32","body_fingerprint":"bc65045bb6754831"}, - "cast_uint16_to_int64": {"display":"uint2_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_bigint\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt64ToUint16), is_monotone = true)] fn cast_uint16_to_int64(a: u16) -> i64","body_fingerprint":"9c62c1a559decc6e"}, + "cast_uint16_to_float32": {"display":"uint2_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_real\", preserves_uniqueness = true, inverse = super::CastFloat32ToUint16, is_monotone = true)] fn cast_uint16_to_float32(a: u16) -> f32","body_fingerprint":"6ad73befb8ca2a34"}, + "cast_uint16_to_float64": {"display":"uint2_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_double\", preserves_uniqueness = true, inverse = super::CastFloat64ToUint16, is_monotone = true)] fn cast_uint16_to_float64(a: u16) -> f64","body_fingerprint":"8a67f84e8764d837"}, + "cast_uint16_to_int16": {"display":"uint2_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_smallint\", preserves_uniqueness = true, inverse = super::CastInt16ToUint16, is_monotone = true)] fn cast_uint16_to_int16(a: u16) -> Result","body_fingerprint":"106c56bf06c2a245"}, + "cast_uint16_to_int32": {"display":"uint2_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_integer\", preserves_uniqueness = true, inverse = super::CastInt32ToUint16, is_monotone = true)] fn cast_uint16_to_int32(a: u16) -> i32","body_fingerprint":"bc65045bb6754831"}, + "cast_uint16_to_int64": {"display":"uint2_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_bigint\", preserves_uniqueness = true, inverse = super::CastInt64ToUint16, is_monotone = true)] fn cast_uint16_to_int64(a: u16) -> i64","body_fingerprint":"9c62c1a559decc6e"}, "cast_uint16_to_numeric": {"display":"uint2_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, "cast_uint16_to_numeric[scale]": {"display":"uint2_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_uint16_to_string": {"display":"uint2_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToUint16))] fn cast_uint16_to_string(a: u16) -> String","body_fingerprint":"9bf1ab0aacc40d76"}, - "cast_uint16_to_uint32": {"display":"uint2_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_uint4\", preserves_uniqueness = true, inverse = to_unary!(super::CastUint32ToUint16), is_monotone = true)] fn cast_uint16_to_uint32(a: u16) -> u32","body_fingerprint":"dea4c81a20b621c5"}, - "cast_uint16_to_uint64": {"display":"uint2_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_uint8\", preserves_uniqueness = true, inverse = to_unary!(super::CastUint64ToUint16), is_monotone = true)] fn cast_uint16_to_uint64(a: u16) -> u64","body_fingerprint":"b2d1e1ed622bf362"}, - "cast_uint32_to_float32": {"display":"uint4_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_real\", preserves_uniqueness = false, inverse = to_unary!(super::CastFloat32ToUint32), is_monotone = true)] fn cast_uint32_to_float32(a: u32) -> f32","body_fingerprint":"5cbae9f61a68b6b7"}, - "cast_uint32_to_float64": {"display":"uint4_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_double\", preserves_uniqueness = true, inverse = to_unary!(super::CastFloat64ToUint32), is_monotone = true)] fn cast_uint32_to_float64(a: u32) -> f64","body_fingerprint":"8a67f84e8764d837"}, - "cast_uint32_to_int16": {"display":"uint4_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_smallint\", preserves_uniqueness = false, inverse = to_unary!(super::CastInt16ToUint32), is_monotone = true)] fn cast_uint32_to_int16(a: u32) -> Result","body_fingerprint":"106c56bf06c2a245"}, - "cast_uint32_to_int32": {"display":"uint4_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_integer\", preserves_uniqueness = false, inverse = to_unary!(super::CastInt32ToUint32), is_monotone = true)] fn cast_uint32_to_int32(a: u32) -> Result","body_fingerprint":"de1123491c3c9cf9"}, - "cast_uint32_to_int64": {"display":"uint4_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_bigint\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt64ToUint32), is_monotone = true)] fn cast_uint32_to_int64(a: u32) -> i64","body_fingerprint":"9c62c1a559decc6e"}, + "cast_uint16_to_string": {"display":"uint2_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_text\", preserves_uniqueness = true, inverse = super::CastStringToUint16)] fn cast_uint16_to_string(a: u16) -> String","body_fingerprint":"9bf1ab0aacc40d76"}, + "cast_uint16_to_uint32": {"display":"uint2_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_uint4\", preserves_uniqueness = true, inverse = super::CastUint32ToUint16, is_monotone = true)] fn cast_uint16_to_uint32(a: u16) -> u32","body_fingerprint":"dea4c81a20b621c5"}, + "cast_uint16_to_uint64": {"display":"uint2_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint2_to_uint8\", preserves_uniqueness = true, inverse = super::CastUint64ToUint16, is_monotone = true)] fn cast_uint16_to_uint64(a: u16) -> u64","body_fingerprint":"b2d1e1ed622bf362"}, + "cast_uint32_to_float32": {"display":"uint4_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_real\", preserves_uniqueness = false, inverse = super::CastFloat32ToUint32, is_monotone = true)] fn cast_uint32_to_float32(a: u32) -> f32","body_fingerprint":"5cbae9f61a68b6b7"}, + "cast_uint32_to_float64": {"display":"uint4_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_double\", preserves_uniqueness = true, inverse = super::CastFloat64ToUint32, is_monotone = true)] fn cast_uint32_to_float64(a: u32) -> f64","body_fingerprint":"8a67f84e8764d837"}, + "cast_uint32_to_int16": {"display":"uint4_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_smallint\", preserves_uniqueness = false, inverse = super::CastInt16ToUint32, is_monotone = true)] fn cast_uint32_to_int16(a: u32) -> Result","body_fingerprint":"106c56bf06c2a245"}, + "cast_uint32_to_int32": {"display":"uint4_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_integer\", preserves_uniqueness = false, inverse = super::CastInt32ToUint32, is_monotone = true)] fn cast_uint32_to_int32(a: u32) -> Result","body_fingerprint":"de1123491c3c9cf9"}, + "cast_uint32_to_int64": {"display":"uint4_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_bigint\", preserves_uniqueness = true, inverse = super::CastInt64ToUint32, is_monotone = true)] fn cast_uint32_to_int64(a: u32) -> i64","body_fingerprint":"9c62c1a559decc6e"}, "cast_uint32_to_mz_timestamp": {"display":"uint4_to_mz_timestamp","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_mz_timestamp\", preserves_uniqueness = true, is_monotone = true)] fn cast_uint32_to_mz_timestamp(a: u32) -> Timestamp","body_fingerprint":"b0767bc58896866d"}, "cast_uint32_to_numeric": {"display":"uint4_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, "cast_uint32_to_numeric[scale]": {"display":"uint4_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_uint32_to_string": {"display":"uint4_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToUint32))] fn cast_uint32_to_string(a: u32) -> String","body_fingerprint":"6eaa3daac575f51c"}, - "cast_uint32_to_uint16": {"display":"uint4_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_uint2\", preserves_uniqueness = false, inverse = to_unary!(super::CastUint16ToUint32), is_monotone = true)] fn cast_uint32_to_uint16(a: u32) -> Result","body_fingerprint":"6e1da3fc5a1ea1ec"}, - "cast_uint32_to_uint64": {"display":"uint4_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_uint8\", preserves_uniqueness = true, inverse = to_unary!(super::CastUint64ToUint32), is_monotone = true)] fn cast_uint32_to_uint64(a: u32) -> u64","body_fingerprint":"b2d1e1ed622bf362"}, - "cast_uint64_to_float32": {"display":"uint8_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_real\", preserves_uniqueness = false, inverse = to_unary!(super::CastFloat32ToUint64), is_monotone = true)] fn cast_uint64_to_float32(a: u64) -> f32","body_fingerprint":"5cbae9f61a68b6b7"}, - "cast_uint64_to_float64": {"display":"uint8_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_double\", preserves_uniqueness = false, inverse = to_unary!(super::CastFloat64ToUint64), is_monotone = true)] fn cast_uint64_to_float64(a: u64) -> f64","body_fingerprint":"08e345cc4df1813c"}, - "cast_uint64_to_int16": {"display":"uint8_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_smallint\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt16ToUint64), is_monotone = true)] fn cast_uint64_to_int16(a: u64) -> Result","body_fingerprint":"106c56bf06c2a245"}, - "cast_uint64_to_int32": {"display":"uint8_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_integer\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt32ToUint64), is_monotone = true)] fn cast_uint64_to_int32(a: u64) -> Result","body_fingerprint":"de1123491c3c9cf9"}, - "cast_uint64_to_int64": {"display":"uint8_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_bigint\", preserves_uniqueness = true, inverse = to_unary!(super::CastInt64ToUint64), is_monotone = true)] fn cast_uint64_to_int64(a: u64) -> Result","body_fingerprint":"0ad4c02482cd3a4d"}, + "cast_uint32_to_string": {"display":"uint4_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_text\", preserves_uniqueness = true, inverse = super::CastStringToUint32)] fn cast_uint32_to_string(a: u32) -> String","body_fingerprint":"6eaa3daac575f51c"}, + "cast_uint32_to_uint16": {"display":"uint4_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_uint2\", preserves_uniqueness = false, inverse = super::CastUint16ToUint32, is_monotone = true)] fn cast_uint32_to_uint16(a: u32) -> Result","body_fingerprint":"6e1da3fc5a1ea1ec"}, + "cast_uint32_to_uint64": {"display":"uint4_to_uint8","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint4_to_uint8\", preserves_uniqueness = true, inverse = super::CastUint64ToUint32, is_monotone = true)] fn cast_uint32_to_uint64(a: u32) -> u64","body_fingerprint":"b2d1e1ed622bf362"}, + "cast_uint64_to_float32": {"display":"uint8_to_real","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_real\", preserves_uniqueness = false, inverse = super::CastFloat32ToUint64, is_monotone = true)] fn cast_uint64_to_float32(a: u64) -> f32","body_fingerprint":"5cbae9f61a68b6b7"}, + "cast_uint64_to_float64": {"display":"uint8_to_double","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_double\", preserves_uniqueness = false, inverse = super::CastFloat64ToUint64, is_monotone = true)] fn cast_uint64_to_float64(a: u64) -> f64","body_fingerprint":"08e345cc4df1813c"}, + "cast_uint64_to_int16": {"display":"uint8_to_smallint","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_smallint\", preserves_uniqueness = true, inverse = super::CastInt16ToUint64, is_monotone = true)] fn cast_uint64_to_int16(a: u64) -> Result","body_fingerprint":"106c56bf06c2a245"}, + "cast_uint64_to_int32": {"display":"uint8_to_integer","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_integer\", preserves_uniqueness = true, inverse = super::CastInt32ToUint64, is_monotone = true)] fn cast_uint64_to_int32(a: u64) -> Result","body_fingerprint":"de1123491c3c9cf9"}, + "cast_uint64_to_int64": {"display":"uint8_to_bigint","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_bigint\", preserves_uniqueness = true, inverse = super::CastInt64ToUint64, is_monotone = true)] fn cast_uint64_to_int64(a: u64) -> Result","body_fingerprint":"0ad4c02482cd3a4d"}, "cast_uint64_to_mz_timestamp": {"display":"uint8_to_mz_timestamp","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_mz_timestamp\", preserves_uniqueness = true, is_monotone = true)] fn cast_uint64_to_mz_timestamp(a: u64) -> Timestamp","body_fingerprint":"bc3f020571556877"}, "cast_uint64_to_numeric": {"display":"uint8_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, "cast_uint64_to_numeric[scale]": {"display":"uint8_to_numeric","sqlfunc_decl":null,"body_fingerprint":null}, - "cast_uint64_to_string": {"display":"uint8_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToUint64))] fn cast_uint64_to_string(a: u64) -> String","body_fingerprint":"d3ae11258ec41861"}, - "cast_uint64_to_uint16": {"display":"uint8_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_uint2\", preserves_uniqueness = true, inverse = to_unary!(super::CastUint16ToUint64), is_monotone = true)] fn cast_uint64_to_uint16(a: u64) -> Result","body_fingerprint":"6e1da3fc5a1ea1ec"}, - "cast_uint64_to_uint32": {"display":"uint8_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_uint4\", preserves_uniqueness = true, inverse = to_unary!(super::CastUint32ToUint64), is_monotone = true)] fn cast_uint64_to_uint32(a: u64) -> Result","body_fingerprint":"a030b0b7248d6b70"}, - "cast_uuid_to_string": {"display":"uuid_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"uuid_to_text\", preserves_uniqueness = true, inverse = to_unary!(super::CastStringToUuid))] fn cast_uuid_to_string(u: Uuid) -> String","body_fingerprint":"3c6c65885274db4a"}, - "cast_var_char_to_string": {"display":"varchar_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"varchar_to_text\", preserves_uniqueness = true, is_eliminable_cast = true, inverse = to_unary!(super::CastStringToVarChar {length: None, fail_on_len: false,}),)] fn cast_var_char_to_string<'a>(a: VarChar<&'a str>) -> &'a str","body_fingerprint":"ba6f1e61cfed1bde"}, + "cast_uint64_to_string": {"display":"uint8_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_text\", preserves_uniqueness = true, inverse = super::CastStringToUint64)] fn cast_uint64_to_string(a: u64) -> String","body_fingerprint":"d3ae11258ec41861"}, + "cast_uint64_to_uint16": {"display":"uint8_to_uint2","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_uint2\", preserves_uniqueness = true, inverse = super::CastUint16ToUint64, is_monotone = true)] fn cast_uint64_to_uint16(a: u64) -> Result","body_fingerprint":"6e1da3fc5a1ea1ec"}, + "cast_uint64_to_uint32": {"display":"uint8_to_uint4","sqlfunc_decl":"#[sqlfunc(sqlname = \"uint8_to_uint4\", preserves_uniqueness = true, inverse = super::CastUint32ToUint64, is_monotone = true)] fn cast_uint64_to_uint32(a: u64) -> Result","body_fingerprint":"a030b0b7248d6b70"}, + "cast_uuid_to_string": {"display":"uuid_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"uuid_to_text\", preserves_uniqueness = true, inverse = super::CastStringToUuid)] fn cast_uuid_to_string(u: Uuid) -> String","body_fingerprint":"3c6c65885274db4a"}, + "cast_var_char_to_string": {"display":"varchar_to_text","sqlfunc_decl":"#[sqlfunc(sqlname = \"varchar_to_text\", preserves_uniqueness = true, is_eliminable_cast = true, inverse = super::CastStringToVarChar {length: None, fail_on_len: false,},)] fn cast_var_char_to_string<'a>(a: VarChar<&'a str>) -> &'a str","body_fingerprint":"ba6f1e61cfed1bde"}, "cbrt_float64": {"display":"cbrtf64","sqlfunc_decl":"#[sqlfunc(sqlname = \"cbrtf64\")] fn cbrt_float64(a: f64) -> f64","body_fingerprint":"ab1bb0604756c134"}, "ceil_float32": {"display":"ceilf32","sqlfunc_decl":"#[sqlfunc(sqlname = \"ceilf32\")] fn ceil_float32(a: f32) -> f32","body_fingerprint":"e901321fe4b5135a"}, "ceil_float64": {"display":"ceilf64","sqlfunc_decl":"#[sqlfunc(sqlname = \"ceilf64\")] fn ceil_float64(a: f64) -> f64","body_fingerprint":"e901321fe4b5135a"}, @@ -516,14 +516,14 @@ "mz_type_name": {"display":"mz_type_name","sqlfunc_decl":"#[sqlfunc] fn mz_type_name(oid: Oid) -> Option","body_fingerprint":"783f0ac69e525f59"}, "mz_validate_privileges": {"display":"mz_validate_privileges","sqlfunc_decl":"#[sqlfunc(sqlname = \"mz_validate_privileges\")] fn mz_validate_privileges(privileges: String) -> Result","body_fingerprint":"24f14aa3d6084878"}, "mz_validate_role_privilege": {"display":"mz_validate_role_privilege","sqlfunc_decl":"#[sqlfunc(sqlname = \"mz_validate_role_privilege\")] fn mz_validate_role_privilege(privilege: String) -> Result","body_fingerprint":"dfa620601fa04e4a"}, - "neg_float32": {"display":"-","sqlfunc_decl":"#[sqlfunc(sqlname = \"-\", preserves_uniqueness = false, inverse = to_unary!(NegFloat32), is_monotone = true)] fn neg_float32(a: f32) -> f32","body_fingerprint":"eed9462be0936615"}, - "neg_float64": {"display":"-","sqlfunc_decl":"#[sqlfunc(sqlname = \"-\", preserves_uniqueness = false, inverse = to_unary!(NegFloat64), is_monotone = true)] fn neg_float64(a: f64) -> f64","body_fingerprint":"eed9462be0936615"}, - "neg_int16": {"display":"-","sqlfunc_decl":"#[sqlfunc(sqlname = \"-\", preserves_uniqueness = true, inverse = to_unary!(NegInt16), is_monotone = true)] fn neg_int16(a: i16) -> Result","body_fingerprint":"c79dbf2b948ca575"}, - "neg_int32": {"display":"-","sqlfunc_decl":"#[sqlfunc(sqlname = \"-\", preserves_uniqueness = true, inverse = to_unary!(NegInt32), is_monotone = true)] fn neg_int32(a: i32) -> Result","body_fingerprint":"9e8a3d8b774e9eb7"}, - "neg_int64": {"display":"-","sqlfunc_decl":"#[sqlfunc(sqlname = \"-\", preserves_uniqueness = true, inverse = to_unary!(NegInt64), is_monotone = true)] fn neg_int64(a: i64) -> Result","body_fingerprint":"1fda6bdf25c3ba84"}, - "neg_interval": {"display":"-","sqlfunc_decl":"#[sqlfunc(sqlname = \"-\", preserves_uniqueness = true, inverse = to_unary!(super::NegInterval))] fn neg_interval(i: Interval) -> Result","body_fingerprint":"8103afafe9ec2da2"}, - "neg_numeric": {"display":"-","sqlfunc_decl":"#[sqlfunc(sqlname = \"-\", preserves_uniqueness = true, inverse = to_unary!(NegNumeric), is_monotone = true)] fn neg_numeric(mut a: Numeric) -> Numeric","body_fingerprint":"e8ea884374a674de"}, - "not": {"display":"NOT","sqlfunc_decl":"#[sqlfunc(sqlname = \"NOT\", preserves_uniqueness = true, inverse = to_unary!(Not), is_monotone = true)] fn not(a: bool) -> bool","body_fingerprint":"de26b65a3695fb67"}, + "neg_float32": {"display":"-","sqlfunc_decl":"#[sqlfunc(sqlname = \"-\", preserves_uniqueness = false, inverse = NegFloat32, is_monotone = true)] fn neg_float32(a: f32) -> f32","body_fingerprint":"eed9462be0936615"}, + "neg_float64": {"display":"-","sqlfunc_decl":"#[sqlfunc(sqlname = \"-\", preserves_uniqueness = false, inverse = NegFloat64, is_monotone = true)] fn neg_float64(a: f64) -> f64","body_fingerprint":"eed9462be0936615"}, + "neg_int16": {"display":"-","sqlfunc_decl":"#[sqlfunc(sqlname = \"-\", preserves_uniqueness = true, inverse = NegInt16, is_monotone = true)] fn neg_int16(a: i16) -> Result","body_fingerprint":"c79dbf2b948ca575"}, + "neg_int32": {"display":"-","sqlfunc_decl":"#[sqlfunc(sqlname = \"-\", preserves_uniqueness = true, inverse = NegInt32, is_monotone = true)] fn neg_int32(a: i32) -> Result","body_fingerprint":"9e8a3d8b774e9eb7"}, + "neg_int64": {"display":"-","sqlfunc_decl":"#[sqlfunc(sqlname = \"-\", preserves_uniqueness = true, inverse = NegInt64, is_monotone = true)] fn neg_int64(a: i64) -> Result","body_fingerprint":"1fda6bdf25c3ba84"}, + "neg_interval": {"display":"-","sqlfunc_decl":"#[sqlfunc(sqlname = \"-\", preserves_uniqueness = true, inverse = super::NegInterval)] fn neg_interval(i: Interval) -> Result","body_fingerprint":"8103afafe9ec2da2"}, + "neg_numeric": {"display":"-","sqlfunc_decl":"#[sqlfunc(sqlname = \"-\", preserves_uniqueness = true, inverse = NegNumeric, is_monotone = true)] fn neg_numeric(mut a: Numeric) -> Numeric","body_fingerprint":"e8ea884374a674de"}, + "not": {"display":"NOT","sqlfunc_decl":"#[sqlfunc(sqlname = \"NOT\", preserves_uniqueness = true, inverse = Not, is_monotone = true)] fn not(a: bool) -> bool","body_fingerprint":"de26b65a3695fb67"}, "pad_char": {"display":"padchar","sqlfunc_decl":null,"body_fingerprint":null}, "pad_char[unbounded]": {"display":"padchar","sqlfunc_decl":null,"body_fingerprint":null}, "panic": {"display":"mz_panic","sqlfunc_decl":"#[sqlfunc(sqlname = \"mz_panic\")] fn panic<'a>(a: &'a str) -> String","body_fingerprint":"95469a722cc261a6"}, @@ -552,7 +552,7 @@ "redact_sql": {"display":"redact_sql","sqlfunc_decl":"#[sqlfunc] fn redact_sql(sql: &str) -> Result","body_fingerprint":"e51c438a74bfae3f"}, "regexp_match": {"display":"regexp_match[\"a+\", case_insensitive=false]","sqlfunc_decl":null,"body_fingerprint":null}, "regexp_split_to_array": {"display":"regexp_split_to_array[\"a+\", case_insensitive=false]","sqlfunc_decl":null,"body_fingerprint":null}, - "reverse": {"display":"reverse","sqlfunc_decl":"#[sqlfunc(preserves_uniqueness = true, inverse = to_unary!(Reverse))] fn reverse<'a>(a: &'a str) -> String","body_fingerprint":"0c1c74222afa8291"}, + "reverse": {"display":"reverse","sqlfunc_decl":"#[sqlfunc(preserves_uniqueness = true, inverse = Reverse)] fn reverse<'a>(a: &'a str) -> String","body_fingerprint":"0c1c74222afa8291"}, "round_float32": {"display":"roundf32","sqlfunc_decl":"#[sqlfunc(sqlname = \"roundf32\")] fn round_float32(a: f32) -> f32","body_fingerprint":"726a414c9bcfc312"}, "round_float64": {"display":"roundf64","sqlfunc_decl":"#[sqlfunc(sqlname = \"roundf64\")] fn round_float64(a: f64) -> f64","body_fingerprint":"726a414c9bcfc312"}, "round_numeric": {"display":"roundnumeric","sqlfunc_decl":"#[sqlfunc(sqlname = \"roundnumeric\", is_monotone = true)] fn round_numeric(mut a: Numeric) -> Numeric","body_fingerprint":"9f85cf9238995ce1"}, @@ -590,7 +590,7 @@ "and": {"display":"AND","sqlfunc_decl":null,"body_fingerprint":null}, "array_create": {"display":"array_create","sqlfunc_decl":"#[sqlfunc(ArrayCreate, output_type_expr = \"match &self.elem_type { SqlScalarType::Array(_) => self.elem_type.clone().nullable(false), _ => SqlScalarType::Array(Box::new(self.elem_type.clone())).nullable(false) }\", introduces_nulls = false)] fn array_create<'a>(&self, datums: Variadic>, temp_storage: &'a RowArena,) -> Result, EvalError>","body_fingerprint":"37bebf040b665d37"}, "array_fill": {"display":"array_fill","sqlfunc_decl":"#[sqlfunc(ArrayFill, output_type_expr = \"SqlScalarType::Array(Box::new(self.elem_type.clone())).nullable(false)\", introduces_nulls = false)] fn array_fill<'a>(&self, fill: Datum<'a>, dims: Option>, lower_bounds: OptionalArg>>, temp_storage: &'a RowArena,) -> Result, EvalError>","body_fingerprint":"56cda42de9453873"}, - "array_index": {"display":"array_index","sqlfunc_decl":"#[sqlfunc(ArrayIndex, sqlname = \"array_index\", introduces_nulls = true)] fn array_index<'a, T: FromDatum<'a>>(&self, array: Array<'a, T>, indices: Variadic,) -> Option","body_fingerprint":"840ba88214eccddb"}, + "array_index": {"display":"array_index","sqlfunc_decl":"#[sqlfunc(ArrayIndex, sqlname = \"array_index\")] fn array_index<'a, T: FromDatum<'a>>(&self, array: Array<'a, T>, indices: Variadic,) -> Option","body_fingerprint":"840ba88214eccddb"}, "array_position": {"display":"array_position","sqlfunc_decl":"#[sqlfunc] fn array_position<'a>(array: Array<'a>, search: Datum<'a>, initial_pos: OptionalArg>,) -> Result, EvalError>","body_fingerprint":"f9edd12fe2a084fe"}, "array_to_string": {"display":"array_to_string","sqlfunc_decl":"#[sqlfunc] fn array_to_string<'a>(&self, array: Array<'a>, delimiter: &str, null_str_arg: OptionalArg>, temp_storage: &RowArena,) -> Result","body_fingerprint":"549bbda8f04d8a10"}, "case_literal": {"display":"case_literal[1 cases]","sqlfunc_decl":null,"body_fingerprint":null}, @@ -611,7 +611,7 @@ "jsonb_build_object": {"display":"jsonb_build_object","sqlfunc_decl":"#[sqlfunc] fn jsonb_build_object<'a>(mut kvs: Variadic<(Datum<'a>, Datum<'a>)>, temp_storage: &'a RowArena,) -> Result, EvalError>","body_fingerprint":"3cad7e59cbb01abb"}, "least": {"display":"least","sqlfunc_decl":null,"body_fingerprint":null}, "list_create": {"display":"list_create","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"SqlScalarType::List { element_type: Box::new(self.elem_type.clone()), custom_id: None }.nullable(false)\", introduces_nulls = false)] fn list_create<'a>(&self, datums: Variadic>, temp_storage: &'a RowArena,) -> Result, EvalError>","body_fingerprint":"33c53edba0b092fb"}, - "list_index": {"display":"list_index","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"input_types[0].scalar_type.unwrap_list_nth_layer_type(input_types.len() - 1).clone().nullable(true)\", introduces_nulls = true)] fn list_index<'a>(buf: DatumList<'a>, indices: Variadic) -> Datum<'a>","body_fingerprint":"57fb9087cef6d141"}, + "list_index": {"display":"list_index","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"input_types[0].scalar_type.unwrap_list_nth_layer_type(input_types.len() - 1).clone().nullable(true)\")] fn list_index<'a>(buf: DatumList<'a>, indices: Variadic) -> Datum<'a>","body_fingerprint":"57fb9087cef6d141"}, "list_slice_linear": {"display":"list_slice_linear","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"input_types[0].scalar_type.clone().nullable(false)\", introduces_nulls = false)] fn list_slice_linear<'a>(list: DatumList<'a>, first: (i64, i64), remainder: Variadic<(i64, i64)>, temp_storage: &'a RowArena,) -> Datum<'a>","body_fingerprint":"cf2a665e88d1643d"}, "make_acl_item": {"display":"makeaclitem","sqlfunc_decl":"#[sqlfunc(sqlname = \"makeaclitem\")] fn make_acl_item(grantee_oid: u32, grantor_oid: u32, privileges: &str, is_grantable: bool,) -> Result","body_fingerprint":"1c05a3379c7fda3b"}, "make_mz_acl_item": {"display":"make_mz_aclitem","sqlfunc_decl":"#[sqlfunc(sqlname = \"make_mz_aclitem\")] fn make_mz_acl_item(grantee_str: &str, grantor_str: &str, privileges: &str,) -> Result","body_fingerprint":"b641508330ebb598"}, @@ -619,9 +619,9 @@ "map_build": {"display":"map_build","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"SqlScalarType::Map { value_type: Box::new(self.value_type.clone()), custom_id: None }.nullable(false)\", introduces_nulls = false)] fn map_build<'a>(&self, datums: Variadic<(Option<&str>, Datum<'a>)>, temp_storage: &'a RowArena,) -> Result, EvalError>","body_fingerprint":"3cf673accdd42c16"}, "or": {"display":"OR","sqlfunc_decl":null,"body_fingerprint":null}, "pad_leading": {"display":"lpad","sqlfunc_decl":"#[sqlfunc(sqlname = \"lpad\")] fn pad_leading(string: &str, raw_len: i32, pad: OptionalArg<&str>, temp_storage: &RowArena,) -> Result","body_fingerprint":"029efffef6e8305f"}, - "range_create": {"display":"int4range","sqlfunc_decl":"#[sqlfunc(RangeCreate, skip_display = true, output_type_expr = \"SqlScalarType::Range { element_type: Box::new(self.elem_type.clone()) }.nullable(false)\", introduces_nulls = false)] fn range_create<'a>(&self, lower: Datum<'a>, upper: Datum<'a>, flags_datum: Datum<'a>, temp_storage: &'a RowArena,) -> Result, EvalError>","body_fingerprint":"034a866eda0ee6d4"}, + "range_create": {"display":"int4range","sqlfunc_decl":"#[sqlfunc(RangeCreate, skip_display = true, output_type_expr = \"SqlScalarType::Range { element_type: Box::new(self.elem_type.clone()) }.nullable(false)\")] fn range_create<'a>(&self, lower: Datum<'a>, upper: Datum<'a>, flags_datum: Datum<'a>,) -> Result>, EvalError>","body_fingerprint":"c21eff7867791358"}, "record_create": {"display":"record_create","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"SqlScalarType::Record { fields: self.field_names.clone().into_iter().zip_eq(input_types.iter().cloned()).collect(), custom_id: None }.nullable(false)\", introduces_nulls = false)] fn record_create<'a>(&self, datums: Variadic>, temp_storage: &'a RowArena,) -> Result, EvalError>","body_fingerprint":"92fe79be327ac13b"}, - "regexp_match": {"display":"regexp_match","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true)\", introduces_nulls = true)] fn regexp_match<'a>(haystack: &'a str, needle: &str, flags: OptionalArg<&str>, temp_storage: &'a RowArena,) -> Result, EvalError>","body_fingerprint":"b4cc3a20cfd56b95"}, + "regexp_match": {"display":"regexp_match","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true)\")] fn regexp_match<'a>(haystack: &'a str, needle: &str, flags: OptionalArg<&str>, temp_storage: &'a RowArena,) -> Result, EvalError>","body_fingerprint":"b4cc3a20cfd56b95"}, "regexp_replace": {"display":"regexp_replace","sqlfunc_decl":"#[sqlfunc] fn regexp_replace<'a>(source: &'a str, pattern: &str, replacement: &str, flags_opt: OptionalArg<&str>, temp_storage: &RowArena,) -> Result, EvalError>","body_fingerprint":"b90fa61dbc761b25"}, "regexp_split_to_array": {"display":"regexp_split_to_array","sqlfunc_decl":"#[sqlfunc(output_type_expr = \"SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(false)\", introduces_nulls = false)] fn regexp_split_to_array<'a>(text: &str, regexp_str: &str, flags: OptionalArg<&str>, temp_storage: &'a RowArena,) -> Result, EvalError>","body_fingerprint":"ef10171f781288f9"}, "replace": {"display":"replace","sqlfunc_decl":"#[sqlfunc] fn replace(text: &str, from: &str, to: &str, temp_storage: &RowArena) -> Result","body_fingerprint":"bfaeb5ab9715cbb8"}, diff --git a/src/expr-derive-impl/src/generate.rs b/src/expr-derive-impl/src/generate.rs index f880b0617498a..fee056c08d7c4 100644 --- a/src/expr-derive-impl/src/generate.rs +++ b/src/expr-derive-impl/src/generate.rs @@ -41,10 +41,11 @@ fn override_methods( .find(|(candidate, _)| candidate == modifier) .map(|(_, expr)| *expr)?; let name = Ident::new(modifier.name(), proc_macro2::Span::call_site()); + let body = ret.body(expr); let ret = ret.to_tokens(); Some(quote! { fn #name(&self) -> #ret { - #expr + #body } }) }) @@ -246,11 +247,6 @@ pub(crate) fn generate( "sqlname has no effect with skip_display, which suppresses the only impl that reads it", )); } - if output_type_expr.is_some() && introduces_nulls.is_none() { - return Err(darling::Error::unknown_field( - "output_type_expr requires introduces_nulls", - )); - } // Replace generic type params with `Datum<'a>` for use in the trait impl's associated // types, where the function's parameters are not in scope. diff --git a/src/expr-derive-impl/src/lib.rs b/src/expr-derive-impl/src/lib.rs index 5c76b3d3bf9e8..60ed3843c1c4f 100644 --- a/src/expr-derive-impl/src/lib.rs +++ b/src/expr-derive-impl/src/lib.rs @@ -306,7 +306,7 @@ mod test { let attr = quote! { could_error = true, output_type = i16, - inverse = None, + inverse = UnaryAllModifiers, is_monotone = true, preserves_uniqueness = true, is_eliminable_cast = false, @@ -330,7 +330,7 @@ mod test { is_infix_op = true, is_monotone = (true, true), is_infinity_monotone = false, - negate = None, + negate = BinaryAllModifiers, propagates_nulls = true, sqlname = "binary_all_modifiers", }; @@ -383,7 +383,7 @@ mod test { #[mz_ore::test] fn unary_rejects_negate_by_name() { let (output, _input) = super::test_sqlfunc( - quote! { negate = to_unary!(super::Foo) }, + quote! { negate = super::Foo }, quote! { fn some_unary<'a>(a: i32) -> i32 { a } }, diff --git a/src/expr-derive-impl/src/shape.rs b/src/expr-derive-impl/src/shape.rs index 11be6223b6c15..19f93c24267e7 100644 --- a/src/expr-derive-impl/src/shape.rs +++ b/src/expr-derive-impl/src/shape.rs @@ -81,6 +81,16 @@ impl ReturnTy { ReturnTy::OptBinaryFunc => quote! { Option }, } } + + /// The method body for a modifier whose value is `expr`. A function-valued + /// modifier names the function, and the body converts it into the enum. + pub(crate) fn body(&self, expr: &syn::Expr) -> TokenStream { + match self { + ReturnTy::Bool | ReturnTy::BoolPair => quote! { #expr }, + ReturnTy::OptUnaryFunc => quote! { Some(crate::UnaryFunc::from(#expr)) }, + ReturnTy::OptBinaryFunc => quote! { Some(crate::BinaryFunc::from(#expr)) }, + } + } } // `crate::generate::override_methods` walks these tables in order to generate each diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__binary_all_modifiers.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__binary_all_modifiers.snap index fed9d6ed84766..12f2c2cfa8696 100644 --- a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__binary_all_modifiers.snap +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__binary_all_modifiers.snap @@ -1,6 +1,6 @@ --- source: src/expr-derive-impl/src/lib.rs -expression: "#[sqlfunc(\n could_error = true,\n output_type = i16,\n is_infix_op = true,\n is_monotone = (true, true),\n is_infinity_monotone = false,\n negate = None,\n propagates_nulls = true,\n sqlname = \"binary_all_modifiers\",\n)]\nfn binary_all_modifiers<'a>(a: Datum<'a>, b: Datum<'a>) -> Datum<'a> {\n a\n}\n" +expression: "#[sqlfunc(\n could_error = true,\n output_type = i16,\n is_infix_op = true,\n is_monotone = (true, true),\n is_infinity_monotone = false,\n negate = BinaryAllModifiers,\n propagates_nulls = true,\n sqlname = \"binary_all_modifiers\",\n)]\nfn binary_all_modifiers<'a>(a: Datum<'a>, b: Datum<'a>) -> Datum<'a> {\n a\n}\n" --- #[derive( Ord, @@ -57,7 +57,7 @@ impl crate::func::binary::EagerBinaryFunc for BinaryAllModifiers { false } fn negate(&self) -> Option { - None + Some(crate::BinaryFunc::from(BinaryAllModifiers)) } fn propagates_nulls(&self) -> bool { true @@ -72,7 +72,7 @@ impl crate::func::FuncName for BinaryAllModifiers { const NAME: &'static str = stringify!(binary_all_modifiers); #[cfg(feature = "func-registry")] const SQLFUNC: Option = Some(crate::func::SqlFuncSource { - decl: "#[sqlfunc(could_error = true, output_type = i16, is_infix_op = true, is_monotone = (true, true), is_infinity_monotone = false, negate = None, propagates_nulls = true, sqlname = \"binary_all_modifiers\",)] fn binary_all_modifiers<'a>(a: Datum<'a>, b: Datum<'a>) -> Datum<'a>", + decl: "#[sqlfunc(could_error = true, output_type = i16, is_infix_op = true, is_monotone = (true, true), is_infinity_monotone = false, negate = BinaryAllModifiers, propagates_nulls = true, sqlname = \"binary_all_modifiers\",)] fn binary_all_modifiers<'a>(a: Datum<'a>, b: Datum<'a>) -> Datum<'a>", signature: "fn(Datum<'a>, Datum<'a>) -> Datum<'a>", body_fingerprint: 14373755090808246356u64, }); diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_all_modifiers.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_all_modifiers.snap index eff8fd6a32783..0f50a3f5275b6 100644 --- a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_all_modifiers.snap +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_all_modifiers.snap @@ -1,6 +1,6 @@ --- source: src/expr-derive-impl/src/lib.rs -expression: "#[sqlfunc(\n could_error = true,\n output_type = i16,\n inverse = None,\n is_monotone = true,\n preserves_uniqueness = true,\n is_eliminable_cast = false,\n sqlname = \"unary_all_modifiers\",\n)]\nfn unary_all_modifiers<'a>(a: Datum<'a>) -> Datum<'a> {\n a\n}\n" +expression: "#[sqlfunc(\n could_error = true,\n output_type = i16,\n inverse = UnaryAllModifiers,\n is_monotone = true,\n preserves_uniqueness = true,\n is_eliminable_cast = false,\n sqlname = \"unary_all_modifiers\",\n)]\nfn unary_all_modifiers<'a>(a: Datum<'a>) -> Datum<'a> {\n a\n}\n" --- #[derive( Ord, @@ -42,7 +42,7 @@ impl crate::func::EagerUnaryFunc for UnaryAllModifiers { >::nullable() } fn inverse(&self) -> Option { - None + Some(crate::UnaryFunc::from(UnaryAllModifiers)) } fn is_monotone(&self) -> bool { true @@ -63,7 +63,7 @@ impl crate::func::FuncName for UnaryAllModifiers { const NAME: &'static str = stringify!(unary_all_modifiers); #[cfg(feature = "func-registry")] const SQLFUNC: Option = Some(crate::func::SqlFuncSource { - decl: "#[sqlfunc(could_error = true, output_type = i16, inverse = None, is_monotone = true, preserves_uniqueness = true, is_eliminable_cast = false, sqlname = \"unary_all_modifiers\",)] fn unary_all_modifiers<'a>(a: Datum<'a>) -> Datum<'a>", + decl: "#[sqlfunc(could_error = true, output_type = i16, inverse = UnaryAllModifiers, is_monotone = true, preserves_uniqueness = true, is_eliminable_cast = false, sqlname = \"unary_all_modifiers\",)] fn unary_all_modifiers<'a>(a: Datum<'a>) -> Datum<'a>", signature: "fn(Datum<'a>) -> Datum<'a>", body_fingerprint: 14373755090808246356u64, }); diff --git a/src/expr/src/scalar/func.rs b/src/expr/src/scalar/func.rs index db16018e2dd32..ccc17749acf2d 100644 --- a/src/expr/src/scalar/func.rs +++ b/src/expr/src/scalar/func.rs @@ -1809,8 +1809,7 @@ fn range_intersection(l: Range, r: Range) -> Range { output_type_expr = "input_types[0].scalar_type.without_modifiers().nullable(true)", is_infix_op = true, sqlname = "-", - propagates_nulls = true, - introduces_nulls = false + propagates_nulls = true )] fn range_difference<'a>( l: Range>, @@ -1819,7 +1818,7 @@ fn range_difference<'a>( Ok(l.difference(&r)?) } -#[sqlfunc(is_infix_op = true, sqlname = "=", negate = "Some(NotEq.into())")] +#[sqlfunc(is_infix_op = true, sqlname = "=", negate = NotEq)] fn eq<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool { // SQL equality demands that if either input is null, then the result should be null. However, // we don't need to handle this case here; it is handled when `BinaryFunc::eval` checks @@ -1827,7 +1826,7 @@ fn eq<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool { a == b } -#[sqlfunc(is_infix_op = true, sqlname = "!=", negate = "Some(Eq.into())")] +#[sqlfunc(is_infix_op = true, sqlname = "!=", negate = Eq)] fn not_eq<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool { a != b } @@ -1836,7 +1835,7 @@ fn not_eq<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool { is_monotone = "(true, true)", is_infix_op = true, sqlname = "<", - negate = "Some(Gte.into())" + negate = Gte )] fn lt<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool { a < b @@ -1846,7 +1845,7 @@ fn lt<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool { is_monotone = "(true, true)", is_infix_op = true, sqlname = "<=", - negate = "Some(Gt.into())" + negate = Gt )] fn lte<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool { a <= b @@ -1856,7 +1855,7 @@ fn lte<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool { is_monotone = "(true, true)", is_infix_op = true, sqlname = ">", - negate = "Some(Lte.into())" + negate = Lte )] fn gt<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool { a > b @@ -1866,7 +1865,7 @@ fn gt<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool { is_monotone = "(true, true)", is_infix_op = true, sqlname = ">=", - negate = "Some(Lt.into())" + negate = Lt )] fn gte<'a>(a: ExcludeNull>, b: ExcludeNull>) -> bool { a >= b @@ -2112,8 +2111,7 @@ fn jsonb_concat<'a>( output_type_expr = "SqlScalarType::Jsonb.nullable(true)", is_infix_op = true, sqlname = "-", - propagates_nulls = true, - introduces_nulls = true + propagates_nulls = true )] fn jsonb_delete_int64<'a>(a: Datum<'a>, i: i64, temp_storage: &'a RowArena) -> Datum<'a> { match a { @@ -2140,8 +2138,7 @@ fn jsonb_delete_int64<'a>(a: Datum<'a>, i: i64, temp_storage: &'a RowArena) -> D output_type_expr = "SqlScalarType::Jsonb.nullable(true)", is_infix_op = true, sqlname = "-", - propagates_nulls = true, - introduces_nulls = true + propagates_nulls = true )] fn jsonb_delete_string<'a>(a: Datum<'a>, k: &str, temp_storage: &'a RowArena) -> Datum<'a> { match a { @@ -2157,11 +2154,7 @@ fn jsonb_delete_string<'a>(a: Datum<'a>, k: &str, temp_storage: &'a RowArena) -> } } -#[sqlfunc( - sqlname = "extractiv", - propagates_nulls = true, - introduces_nulls = false -)] +#[sqlfunc(sqlname = "extractiv", propagates_nulls = true)] fn date_part_interval_numeric(units: &str, b: Interval) -> Result { match units.parse() { Ok(units) => Ok(date_part_interval_inner::(units, b)?), @@ -2169,11 +2162,7 @@ fn date_part_interval_numeric(units: &str, b: Interval) -> Result Result { match units.parse() { Ok(units) => Ok(date_part_interval_inner::(units, b)?), @@ -2181,11 +2170,7 @@ fn date_part_interval_f64(units: &str, b: Interval) -> Result { } } -#[sqlfunc( - sqlname = "extractt", - propagates_nulls = true, - introduces_nulls = false -)] +#[sqlfunc(sqlname = "extractt", propagates_nulls = true)] fn date_part_time_numeric(units: &str, b: chrono::NaiveTime) -> Result { match units.parse() { Ok(units) => Ok(date_part_time_inner::(units, b)?), @@ -2193,11 +2178,7 @@ fn date_part_time_numeric(units: &str, b: chrono::NaiveTime) -> Result Result { match units.parse() { Ok(units) => Ok(date_part_time_inner::(units, b)?), @@ -2679,7 +2660,7 @@ fn text_concat_binary(a: &str, b: &str, temp_storage: &RowArena) -> Result( pattern: &str, b: &str, @@ -3004,11 +2985,7 @@ fn trim_trailing<'a>(a: &'a str, trim_chars: &str) -> &'a str { a.trim_end_matches(|c| trim_chars.contains(c)) } -#[sqlfunc( - sqlname = "array_length", - propagates_nulls = true, - introduces_nulls = true -)] +#[sqlfunc(sqlname = "array_length", propagates_nulls = true)] fn array_length<'a>(a: Array<'a>, b: i64) -> Result, EvalError> { let i = match usize::try_from(b) { Ok(0) | Err(_) => return Ok(None), @@ -3105,8 +3082,7 @@ fn array_upper<'a>(a: Array<'a>, i: i64) -> Result, EvalError> { #[sqlfunc( is_infix_op = true, sqlname = "array_contains", - propagates_nulls = true, - introduces_nulls = false + propagates_nulls = true )] fn array_contains<'a>(a: Datum<'a>, array: Array<'a>) -> bool { array.elements().iter().any(|e| e == a) diff --git a/src/expr/src/scalar/func/impls/array.rs b/src/expr/src/scalar/func/impls/array.rs index fc38c40f4f260..b475563a643bc 100644 --- a/src/expr/src/scalar/func/impls/array.rs +++ b/src/expr/src/scalar/func/impls/array.rs @@ -20,7 +20,7 @@ use crate::{Eval, EvalError, MirScalarExpr}; // NOTE: This cast does not preserve uniqueness: It returns only the array's elements and drops the // dimension metadata, so arrays that differ only in their lower bounds (e.g. `[1:1]={42}` and // `[2:2]={42}`) collapse to the same list. -#[sqlfunc(sqlname = "arraytolist", introduces_nulls = false)] +#[sqlfunc(sqlname = "arraytolist")] fn cast_array_to_list_one_dim<'a, T>(a: Array<'a, T>) -> Result, EvalError> { let ndims = a.dims().ndims(); if ndims > 1 { diff --git a/src/expr/src/scalar/func/impls/boolean.rs b/src/expr/src/scalar/func/impls/boolean.rs index 171ecddacac47..1dff09e549b98 100644 --- a/src/expr/src/scalar/func/impls/boolean.rs +++ b/src/expr/src/scalar/func/impls/boolean.rs @@ -13,7 +13,7 @@ use mz_repr::strconv; #[sqlfunc( sqlname = "NOT", preserves_uniqueness = true, - inverse = to_unary!(Not), + inverse = Not, is_monotone = true )] fn not(a: bool) -> bool { @@ -23,7 +23,7 @@ fn not(a: bool) -> bool { #[sqlfunc( sqlname = "boolean_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToBool), + inverse = super::CastStringToBool, is_monotone = true )] fn cast_bool_to_string<'a>(a: bool) -> &'a str { @@ -36,7 +36,7 @@ fn cast_bool_to_string<'a>(a: bool) -> &'a str { #[sqlfunc( sqlname = "boolean_to_nonstandard_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToBool), + inverse = super::CastStringToBool, is_monotone = true )] fn cast_bool_to_string_nonstandard<'a>(a: bool) -> &'a str { @@ -50,7 +50,7 @@ fn cast_bool_to_string_nonstandard<'a>(a: bool) -> &'a str { #[sqlfunc( sqlname = "boolean_to_integer", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt32ToBool), + inverse = super::CastInt32ToBool, is_monotone = true )] fn cast_bool_to_int32(a: bool) -> i32 { @@ -63,7 +63,7 @@ fn cast_bool_to_int32(a: bool) -> i32 { #[sqlfunc( sqlname = "boolean_to_bigint", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt64ToBool), + inverse = super::CastInt64ToBool, is_monotone = true )] fn cast_bool_to_int64(a: bool) -> i64 { diff --git a/src/expr/src/scalar/func/impls/byte.rs b/src/expr/src/scalar/func/impls/byte.rs index 2dbeb4947aaa6..0971e7d5afa02 100644 --- a/src/expr/src/scalar/func/impls/byte.rs +++ b/src/expr/src/scalar/func/impls/byte.rs @@ -16,7 +16,7 @@ use crate::EvalError; #[sqlfunc( sqlname = "bytea_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToBytes) + inverse = super::CastStringToBytes )] fn cast_bytes_to_string(a: &[u8]) -> String { let mut buf = String::new(); diff --git a/src/expr/src/scalar/func/impls/char.rs b/src/expr/src/scalar/func/impls/char.rs index 2a6f81b4c870c..475e320fe024e 100644 --- a/src/expr/src/scalar/func/impls/char.rs +++ b/src/expr/src/scalar/func/impls/char.rs @@ -62,10 +62,10 @@ impl fmt::Display for PadChar { sqlname = "char_to_text", preserves_uniqueness = true, is_eliminable_cast = true, - inverse = to_unary!(super::CastStringToChar{ + inverse = super::CastStringToChar{ length: None, fail_on_len: false, - }) + } )] fn cast_char_to_string<'a>(a: Char<&'a str>) -> &'a str { a.0 diff --git a/src/expr/src/scalar/func/impls/date.rs b/src/expr/src/scalar/func/impls/date.rs index 4402dc52572b7..689b8fb05489a 100644 --- a/src/expr/src/scalar/func/impls/date.rs +++ b/src/expr/src/scalar/func/impls/date.rs @@ -25,7 +25,7 @@ use crate::scalar::func::EagerUnaryFunc; #[sqlfunc( sqlname = "date_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToDate) + inverse = super::CastStringToDate )] fn cast_date_to_string(a: Date) -> String { let mut buf = String::new(); diff --git a/src/expr/src/scalar/func/impls/float32.rs b/src/expr/src/scalar/func/impls/float32.rs index 1be0ac9f8d763..d4930d171d7d4 100644 --- a/src/expr/src/scalar/func/impls/float32.rs +++ b/src/expr/src/scalar/func/impls/float32.rs @@ -20,7 +20,7 @@ use crate::scalar::func::EagerUnaryFunc; #[sqlfunc( sqlname = "-", preserves_uniqueness = false, - inverse = to_unary!(NegFloat32), + inverse = NegFloat32, is_monotone = true )] fn neg_float32(a: f32) -> f32 { @@ -55,7 +55,7 @@ fn floor_float32(a: f32) -> f32 { #[sqlfunc( sqlname = "real_to_smallint", preserves_uniqueness = false, - inverse = to_unary!(super::CastInt16ToFloat32), + inverse = super::CastInt16ToFloat32, is_monotone = true )] fn cast_float32_to_int16(a: f32) -> Result { @@ -72,7 +72,7 @@ fn cast_float32_to_int16(a: f32) -> Result { #[sqlfunc( sqlname = "real_to_integer", preserves_uniqueness = false, - inverse = to_unary!(super::CastInt32ToFloat32), + inverse = super::CastInt32ToFloat32, is_monotone = true )] fn cast_float32_to_int32(a: f32) -> Result { @@ -93,7 +93,7 @@ fn cast_float32_to_int32(a: f32) -> Result { #[sqlfunc( sqlname = "real_to_bigint", preserves_uniqueness = false, - inverse = to_unary!(super::CastInt64ToFloat32), + inverse = super::CastInt64ToFloat32, is_monotone = true )] fn cast_float32_to_int64(a: f32) -> Result { @@ -114,7 +114,7 @@ fn cast_float32_to_int64(a: f32) -> Result { #[sqlfunc( sqlname = "real_to_double", preserves_uniqueness = false, - inverse = to_unary!(super::CastFloat64ToFloat32), + inverse = super::CastFloat64ToFloat32, is_monotone = true )] fn cast_float32_to_float64(a: f32) -> f64 { @@ -124,7 +124,7 @@ fn cast_float32_to_float64(a: f32) -> f64 { #[sqlfunc( sqlname = "real_to_text", preserves_uniqueness = false, - inverse = to_unary!(super::CastStringToFloat32) + inverse = super::CastStringToFloat32 )] fn cast_float32_to_string(a: f32) -> String { let mut s = String::new(); @@ -135,7 +135,7 @@ fn cast_float32_to_string(a: f32) -> String { #[sqlfunc( sqlname = "real_to_uint2", preserves_uniqueness = false, - inverse = to_unary!(super::CastUint16ToFloat32), + inverse = super::CastUint16ToFloat32, is_monotone = true )] fn cast_float32_to_uint16(a: f32) -> Result { @@ -152,7 +152,7 @@ fn cast_float32_to_uint16(a: f32) -> Result { #[sqlfunc( sqlname = "real_to_uint4", preserves_uniqueness = false, - inverse = to_unary!(super::CastUint32ToFloat32), + inverse = super::CastUint32ToFloat32, is_monotone = true )] fn cast_float32_to_uint32(a: f32) -> Result { @@ -172,7 +172,7 @@ fn cast_float32_to_uint32(a: f32) -> Result { #[sqlfunc( sqlname = "real_to_uint8", preserves_uniqueness = false, - inverse = to_unary!(super::CastUint64ToFloat32), + inverse = super::CastUint64ToFloat32, is_monotone = true )] fn cast_float32_to_uint64(a: f32) -> Result { diff --git a/src/expr/src/scalar/func/impls/float64.rs b/src/expr/src/scalar/func/impls/float64.rs index feb716016cb92..d616898494f51 100644 --- a/src/expr/src/scalar/func/impls/float64.rs +++ b/src/expr/src/scalar/func/impls/float64.rs @@ -24,7 +24,7 @@ use crate::scalar::func::EagerUnaryFunc; #[sqlfunc( sqlname = "-", preserves_uniqueness = false, - inverse = to_unary!(NegFloat64), + inverse = NegFloat64, is_monotone = true )] fn neg_float64(a: f64) -> f64 { @@ -59,7 +59,7 @@ fn floor_float64(a: f64) -> f64 { #[sqlfunc( sqlname = "double_to_smallint", preserves_uniqueness = false, - inverse = to_unary!(super::CastInt16ToFloat64), + inverse = super::CastInt16ToFloat64, is_monotone = true )] fn cast_float64_to_int16(a: f64) -> Result { @@ -76,7 +76,7 @@ fn cast_float64_to_int16(a: f64) -> Result { #[sqlfunc( sqlname = "double_to_integer", preserves_uniqueness = false, - inverse = to_unary!(super::CastInt32ToFloat64), + inverse = super::CastInt32ToFloat64, is_monotone = true )] fn cast_float64_to_int32(a: f64) -> Result { @@ -97,7 +97,7 @@ fn cast_float64_to_int32(a: f64) -> Result { #[sqlfunc( sqlname = "f64toi64", preserves_uniqueness = false, - inverse = to_unary!(super::CastInt64ToFloat64), + inverse = super::CastInt64ToFloat64, is_monotone = true )] fn cast_float64_to_int64(a: f64) -> Result { @@ -118,7 +118,7 @@ fn cast_float64_to_int64(a: f64) -> Result { #[sqlfunc( sqlname = "double_to_real", preserves_uniqueness = false, - inverse = to_unary!(super::CastFloat32ToFloat64), + inverse = super::CastFloat32ToFloat64, is_monotone = true )] fn cast_float64_to_float32(a: f64) -> Result { @@ -137,7 +137,7 @@ fn cast_float64_to_float32(a: f64) -> Result { #[sqlfunc( sqlname = "double_to_text", preserves_uniqueness = false, - inverse = to_unary!(super::CastStringToFloat64) + inverse = super::CastStringToFloat64 )] fn cast_float64_to_string(a: f64) -> String { let mut s = String::new(); @@ -148,7 +148,7 @@ fn cast_float64_to_string(a: f64) -> String { #[sqlfunc( sqlname = "double_to_uint2", preserves_uniqueness = false, - inverse = to_unary!(super::CastUint16ToFloat64), + inverse = super::CastUint16ToFloat64, is_monotone = true )] fn cast_float64_to_uint16(a: f64) -> Result { @@ -165,7 +165,7 @@ fn cast_float64_to_uint16(a: f64) -> Result { #[sqlfunc( sqlname = "double_to_uint4", preserves_uniqueness = false, - inverse = to_unary!(super::CastUint32ToFloat64), + inverse = super::CastUint32ToFloat64, is_monotone = true )] fn cast_float64_to_uint32(a: f64) -> Result { @@ -182,7 +182,7 @@ fn cast_float64_to_uint32(a: f64) -> Result { #[sqlfunc( sqlname = "double_to_uint8", preserves_uniqueness = false, - inverse = to_unary!(super::CastUint64ToFloat64), + inverse = super::CastUint64ToFloat64, is_monotone = true )] fn cast_float64_to_uint64(a: f64) -> Result { diff --git a/src/expr/src/scalar/func/impls/int16.rs b/src/expr/src/scalar/func/impls/int16.rs index 711424f16267b..b7ae0cb476535 100644 --- a/src/expr/src/scalar/func/impls/int16.rs +++ b/src/expr/src/scalar/func/impls/int16.rs @@ -20,7 +20,7 @@ use crate::scalar::func::EagerUnaryFunc; #[sqlfunc( sqlname = "-", preserves_uniqueness = true, - inverse = to_unary!(NegInt16), + inverse = NegInt16, is_monotone = true )] fn neg_int16(a: i16) -> Result { @@ -31,7 +31,7 @@ fn neg_int16(a: i16) -> Result { #[sqlfunc( sqlname = "~", preserves_uniqueness = true, - inverse = to_unary!(BitNotInt16) + inverse = BitNotInt16 )] fn bit_not_int16(a: i16) -> i16 { !a @@ -46,7 +46,7 @@ fn abs_int16(a: i16) -> Result { #[sqlfunc( sqlname = "smallint_to_real", preserves_uniqueness = true, - inverse = to_unary!(super::CastFloat32ToInt16), + inverse = super::CastFloat32ToInt16, is_monotone = true )] fn cast_int16_to_float32(a: i16) -> f32 { @@ -56,7 +56,7 @@ fn cast_int16_to_float32(a: i16) -> f32 { #[sqlfunc( sqlname = "smallint_to_double", preserves_uniqueness = true, - inverse = to_unary!(super::CastFloat64ToInt16), + inverse = super::CastFloat64ToInt16, is_monotone = true )] fn cast_int16_to_float64(a: i16) -> f64 { @@ -66,7 +66,7 @@ fn cast_int16_to_float64(a: i16) -> f64 { #[sqlfunc( sqlname = "smallint_to_integer", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt32ToInt16), + inverse = super::CastInt32ToInt16, is_monotone = true )] fn cast_int16_to_int32(a: i16) -> i32 { @@ -76,7 +76,7 @@ fn cast_int16_to_int32(a: i16) -> i32 { #[sqlfunc( sqlname = "smallint_to_bigint", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt64ToInt16), + inverse = super::CastInt64ToInt16, is_monotone = true )] fn cast_int16_to_int64(a: i16) -> i64 { @@ -86,7 +86,7 @@ fn cast_int16_to_int64(a: i16) -> i64 { #[sqlfunc( sqlname = "smallint_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToInt16) + inverse = super::CastStringToInt16 )] fn cast_int16_to_string(a: i16) -> String { let mut buf = String::new(); @@ -97,7 +97,7 @@ fn cast_int16_to_string(a: i16) -> String { #[sqlfunc( sqlname = "smallint_to_uint2", preserves_uniqueness = true, - inverse = to_unary!(super::CastUint16ToInt16), + inverse = super::CastUint16ToInt16, is_monotone = true )] fn cast_int16_to_uint16(a: i16) -> Result { @@ -107,7 +107,7 @@ fn cast_int16_to_uint16(a: i16) -> Result { #[sqlfunc( sqlname = "smallint_to_uint4", preserves_uniqueness = true, - inverse = to_unary!(super::CastUint32ToInt16), + inverse = super::CastUint32ToInt16, is_monotone = true )] fn cast_int16_to_uint32(a: i16) -> Result { @@ -117,7 +117,7 @@ fn cast_int16_to_uint32(a: i16) -> Result { #[sqlfunc( sqlname = "smallint_to_uint8", preserves_uniqueness = true, - inverse = to_unary!(super::CastUint64ToInt16), + inverse = super::CastUint64ToInt16, is_monotone = true )] fn cast_int16_to_uint64(a: i16) -> Result { diff --git a/src/expr/src/scalar/func/impls/int2vector.rs b/src/expr/src/scalar/func/impls/int2vector.rs index 1f16072232780..ea8d60becc89c 100644 --- a/src/expr/src/scalar/func/impls/int2vector.rs +++ b/src/expr/src/scalar/func/impls/int2vector.rs @@ -17,7 +17,6 @@ use crate::scalar::func::stringify_datum; #[sqlfunc( sqlname = "int2vectortoarray", is_monotone = true, - introduces_nulls = false, output_type_expr = SqlScalarType::Array(Box::from(SqlScalarType::Int16)) .nullable(input_type.nullable) )] @@ -28,7 +27,7 @@ fn cast_int2_vector_to_array<'a>(a: Int2Vector<'a>) -> Array<'a> { #[sqlfunc( sqlname = "int2vectortostr", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToInt2Vector) + inverse = super::CastStringToInt2Vector )] fn cast_int2_vector_to_string<'a>(a: Int2Vector<'a>) -> Result { let mut buf = String::new(); diff --git a/src/expr/src/scalar/func/impls/int32.rs b/src/expr/src/scalar/func/impls/int32.rs index fc895deda3306..a4a5f2746eee3 100644 --- a/src/expr/src/scalar/func/impls/int32.rs +++ b/src/expr/src/scalar/func/impls/int32.rs @@ -22,7 +22,7 @@ use crate::scalar::func::EagerUnaryFunc; #[sqlfunc( sqlname = "-", preserves_uniqueness = true, - inverse = to_unary!(NegInt32), + inverse = NegInt32, is_monotone = true )] fn neg_int32(a: i32) -> Result { @@ -33,7 +33,7 @@ fn neg_int32(a: i32) -> Result { #[sqlfunc( sqlname = "~", preserves_uniqueness = true, - inverse = to_unary!(BitNotInt32) + inverse = BitNotInt32 )] fn bit_not_int32(a: i32) -> i32 { !a @@ -48,7 +48,7 @@ fn abs_int32(a: i32) -> Result { #[sqlfunc( sqlname = "integer_to_boolean", preserves_uniqueness = false, - inverse = to_unary!(super::CastBoolToInt32) + inverse = super::CastBoolToInt32 )] fn cast_int32_to_bool(a: i32) -> bool { a != 0 @@ -57,7 +57,7 @@ fn cast_int32_to_bool(a: i32) -> bool { #[sqlfunc( sqlname = "integer_to_real", preserves_uniqueness = false, - inverse = to_unary!(super::CastFloat32ToInt32), + inverse = super::CastFloat32ToInt32, is_monotone = true )] fn cast_int32_to_float32(a: i32) -> f32 { @@ -71,7 +71,7 @@ fn cast_int32_to_float32(a: i32) -> f32 { #[sqlfunc( sqlname = "integer_to_double", preserves_uniqueness = true, - inverse = to_unary!(super::CastFloat64ToInt32), + inverse = super::CastFloat64ToInt32, is_monotone = true )] fn cast_int32_to_float64(a: i32) -> f64 { @@ -81,7 +81,7 @@ fn cast_int32_to_float64(a: i32) -> f64 { #[sqlfunc( sqlname = "integer_to_smallint", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt16ToInt32), + inverse = super::CastInt16ToInt32, is_monotone = true )] fn cast_int32_to_int16(a: i32) -> Result { @@ -91,7 +91,7 @@ fn cast_int32_to_int16(a: i32) -> Result { #[sqlfunc( sqlname = "integer_to_bigint", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt64ToInt32), + inverse = super::CastInt64ToInt32, is_monotone = true )] fn cast_int32_to_int64(a: i32) -> i64 { @@ -101,7 +101,7 @@ fn cast_int32_to_int64(a: i32) -> i64 { #[sqlfunc( sqlname = "integer_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToInt32) + inverse = super::CastStringToInt32 )] fn cast_int32_to_string(a: i32) -> String { let mut buf = String::new(); @@ -112,7 +112,7 @@ fn cast_int32_to_string(a: i32) -> String { #[sqlfunc( sqlname = "integer_to_uint2", preserves_uniqueness = true, - inverse = to_unary!(super::CastUint16ToInt32), + inverse = super::CastUint16ToInt32, is_monotone = true )] fn cast_int32_to_uint16(a: i32) -> Result { @@ -122,7 +122,7 @@ fn cast_int32_to_uint16(a: i32) -> Result { #[sqlfunc( sqlname = "integer_to_uint4", preserves_uniqueness = true, - inverse = to_unary!(super::CastUint32ToInt32), + inverse = super::CastUint32ToInt32, is_monotone = true )] fn cast_int32_to_uint32(a: i32) -> Result { @@ -132,7 +132,7 @@ fn cast_int32_to_uint32(a: i32) -> Result { #[sqlfunc( sqlname = "integer_to_uint8", preserves_uniqueness = true, - inverse = to_unary!(super::CastUint64ToInt32), + inverse = super::CastUint64ToInt32, is_monotone = true )] fn cast_int32_to_uint64(a: i32) -> Result { @@ -193,7 +193,7 @@ impl fmt::Display for CastInt32ToNumeric { #[sqlfunc( sqlname = "integer_to_oid", preserves_uniqueness = true, - inverse = to_unary!(super::CastOidToInt32) + inverse = super::CastOidToInt32 )] fn cast_int32_to_oid(a: i32) -> Oid { // For historical reasons in PostgreSQL, the bytes of the `i32` are @@ -209,7 +209,7 @@ fn cast_int32_to_oid(a: i32) -> Oid { #[sqlfunc( sqlname = "integer_to_\"char\"", preserves_uniqueness = true, - inverse = to_unary!(super::CastPgLegacyCharToInt32) + inverse = super::CastPgLegacyCharToInt32 )] fn cast_int32_to_pg_legacy_char(a: i32) -> Result { // Per PostgreSQL, casts to `PgLegacyChar` are performed as if diff --git a/src/expr/src/scalar/func/impls/int64.rs b/src/expr/src/scalar/func/impls/int64.rs index 731f701e0e2ef..10cf7dc73b1e8 100644 --- a/src/expr/src/scalar/func/impls/int64.rs +++ b/src/expr/src/scalar/func/impls/int64.rs @@ -21,7 +21,7 @@ use crate::scalar::func::EagerUnaryFunc; #[sqlfunc( sqlname = "-", preserves_uniqueness = true, - inverse = to_unary!(NegInt64), + inverse = NegInt64, is_monotone = true )] fn neg_int64(a: i64) -> Result { @@ -32,7 +32,7 @@ fn neg_int64(a: i64) -> Result { #[sqlfunc( sqlname = "~", preserves_uniqueness = true, - inverse = to_unary!(BitNotInt64) + inverse = BitNotInt64 )] fn bit_not_int64(a: i64) -> i64 { !a @@ -47,7 +47,7 @@ fn abs_int64(a: i64) -> Result { #[sqlfunc( sqlname = "bigint_to_boolean", preserves_uniqueness = false, - inverse = to_unary!(super::CastBoolToInt64) + inverse = super::CastBoolToInt64 )] fn cast_int64_to_bool(a: i64) -> bool { a != 0 @@ -56,7 +56,7 @@ fn cast_int64_to_bool(a: i64) -> bool { #[sqlfunc( sqlname = "bigint_to_smallint", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt16ToInt64), + inverse = super::CastInt16ToInt64, is_monotone = true )] fn cast_int64_to_int16(a: i64) -> Result { @@ -66,7 +66,7 @@ fn cast_int64_to_int16(a: i64) -> Result { #[sqlfunc( sqlname = "bigint_to_integer", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt32ToInt64), + inverse = super::CastInt32ToInt64, is_monotone = true )] fn cast_int64_to_int32(a: i64) -> Result { @@ -76,7 +76,7 @@ fn cast_int64_to_int32(a: i64) -> Result { #[sqlfunc( sqlname = "bigint_to_oid", preserves_uniqueness = true, - inverse = to_unary!(super::CastOidToInt64) + inverse = super::CastOidToInt64 )] fn cast_int64_to_oid(a: i64) -> Result { // Unlike casting a 16-bit or 32-bit integers to OID, casting a 64-bit @@ -89,7 +89,7 @@ fn cast_int64_to_oid(a: i64) -> Result { #[sqlfunc( sqlname = "bigint_to_uint2", preserves_uniqueness = true, - inverse = to_unary!(super::CastUint16ToInt64), + inverse = super::CastUint16ToInt64, is_monotone = true )] fn cast_int64_to_uint16(a: i64) -> Result { @@ -99,7 +99,7 @@ fn cast_int64_to_uint16(a: i64) -> Result { #[sqlfunc( sqlname = "bigint_to_uint4", preserves_uniqueness = true, - inverse = to_unary!(super::CastUint32ToInt64), + inverse = super::CastUint32ToInt64, is_monotone = true )] fn cast_int64_to_uint32(a: i64) -> Result { @@ -109,7 +109,7 @@ fn cast_int64_to_uint32(a: i64) -> Result { #[sqlfunc( sqlname = "bigint_to_uint8", preserves_uniqueness = true, - inverse = to_unary!(super::CastUint64ToInt64), + inverse = super::CastUint64ToInt64, is_monotone = true )] fn cast_int64_to_uint64(a: i64) -> Result { @@ -170,7 +170,7 @@ impl fmt::Display for CastInt64ToNumeric { #[sqlfunc( sqlname = "bigint_to_real", preserves_uniqueness = false, - inverse = to_unary!(super::CastFloat32ToInt64), + inverse = super::CastFloat32ToInt64, is_monotone = true )] // TODO(benesch): remove potentially dangerous usage of `as`. @@ -182,7 +182,7 @@ fn cast_int64_to_float32(a: i64) -> f32 { #[sqlfunc( sqlname = "bigint_to_double", preserves_uniqueness = false, // Witness: (1111111111111111111, 1111111111111111112). - inverse = to_unary!(super::CastFloat64ToInt64), + inverse = super::CastFloat64ToInt64, is_monotone = true )] // TODO(benesch): remove potentially dangerous usage of `as`. @@ -194,7 +194,7 @@ fn cast_int64_to_float64(a: i64) -> f64 { #[sqlfunc( sqlname = "bigint_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToInt64) + inverse = super::CastStringToInt64 )] fn cast_int64_to_string(a: i64) -> String { let mut buf = String::new(); diff --git a/src/expr/src/scalar/func/impls/interval.rs b/src/expr/src/scalar/func/impls/interval.rs index a45d6f664b0a7..e8bad917bb776 100644 --- a/src/expr/src/scalar/func/impls/interval.rs +++ b/src/expr/src/scalar/func/impls/interval.rs @@ -19,7 +19,7 @@ use crate::EvalError; #[sqlfunc( sqlname = "interval_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToInterval) + inverse = super::CastStringToInterval )] fn cast_interval_to_string(a: Interval) -> String { let mut buf = String::new(); @@ -30,7 +30,7 @@ fn cast_interval_to_string(a: Interval) -> String { #[sqlfunc( sqlname = "interval_to_time", preserves_uniqueness = false, - inverse = to_unary!(super::CastTimeToInterval) + inverse = super::CastTimeToInterval )] fn cast_interval_to_time(i: Interval) -> NaiveTime { // Modeled after the PostgreSQL implementation: @@ -64,7 +64,7 @@ fn cast_interval_to_time(i: Interval) -> NaiveTime { #[sqlfunc( sqlname = "-", preserves_uniqueness = true, - inverse = to_unary!(super::NegInterval) + inverse = super::NegInterval )] fn neg_interval(i: Interval) -> Result { i.checked_neg() diff --git a/src/expr/src/scalar/func/impls/jsonb.rs b/src/expr/src/scalar/func/impls/jsonb.rs index f682658e85f55..947b492cae618 100644 --- a/src/expr/src/scalar/func/impls/jsonb.rs +++ b/src/expr/src/scalar/func/impls/jsonb.rs @@ -22,7 +22,7 @@ use crate::scalar::func::impls::numeric::*; #[sqlfunc( sqlname = "jsonb_to_text", preserves_uniqueness = false, - inverse = to_unary!(super::CastStringToJsonb) + inverse = super::CastStringToJsonb )] pub fn cast_jsonb_to_string<'a>(a: JsonbRef<'a>) -> String { let mut buf = String::new(); diff --git a/src/expr/src/scalar/func/impls/mz_timestamp.rs b/src/expr/src/scalar/func/impls/mz_timestamp.rs index 6f2d637752232..7eb37d079da1e 100644 --- a/src/expr/src/scalar/func/impls/mz_timestamp.rs +++ b/src/expr/src/scalar/func/impls/mz_timestamp.rs @@ -25,7 +25,7 @@ use crate::EvalError; #[sqlfunc( sqlname = "mz_timestamp_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToMzTimestamp) + inverse = super::CastStringToMzTimestamp )] fn cast_mz_timestamp_to_string(a: Timestamp) -> String { let mut buf = String::new(); @@ -36,7 +36,7 @@ fn cast_mz_timestamp_to_string(a: Timestamp) -> String { #[sqlfunc( sqlname = "text_to_mz_timestamp", preserves_uniqueness = false, - inverse = to_unary!(super::CastMzTimestampToString) + inverse = super::CastMzTimestampToString )] fn cast_string_to_mz_timestamp(a: String) -> Result { strconv::parse_mz_timestamp(&a).err_into() @@ -127,7 +127,7 @@ fn cast_date_to_mz_timestamp(a: Date) -> Result { #[sqlfunc( sqlname = "mz_timestamp_to_timestamp", preserves_uniqueness = true, - inverse = to_unary!(super::CastTimestampToMzTimestamp) + inverse = super::CastTimestampToMzTimestamp )] fn cast_mz_timestamp_to_timestamp( a: Timestamp, @@ -143,7 +143,7 @@ fn cast_mz_timestamp_to_timestamp( #[sqlfunc( sqlname = "mz_timestamp_to_timestamp_tz", preserves_uniqueness = true, - inverse = to_unary!(super::CastTimestampTzToMzTimestamp) + inverse = super::CastTimestampTzToMzTimestamp )] fn cast_mz_timestamp_to_timestamp_tz( a: Timestamp, diff --git a/src/expr/src/scalar/func/impls/numeric.rs b/src/expr/src/scalar/func/impls/numeric.rs index 2640a9f7844fa..a9e8788fcb3e2 100644 --- a/src/expr/src/scalar/func/impls/numeric.rs +++ b/src/expr/src/scalar/func/impls/numeric.rs @@ -21,7 +21,7 @@ use crate::scalar::func::EagerUnaryFunc; #[sqlfunc( sqlname = "-", preserves_uniqueness = true, - inverse = to_unary!(NegNumeric), + inverse = NegNumeric, is_monotone = true )] fn neg_numeric(mut a: Numeric) -> Numeric { @@ -154,7 +154,7 @@ fn sqrt_numeric(mut a: Numeric) -> Result { #[sqlfunc( sqlname = "numeric_to_smallint", preserves_uniqueness = false, - inverse = to_unary!(super::CastInt16ToNumeric(None)), + inverse = super::CastInt16ToNumeric(None), is_monotone = true )] pub fn cast_numeric_to_int16(mut a: Numeric) -> Result { @@ -170,7 +170,7 @@ pub fn cast_numeric_to_int16(mut a: Numeric) -> Result { #[sqlfunc( sqlname = "numeric_to_integer", preserves_uniqueness = false, - inverse = to_unary!(super::CastInt32ToNumeric(None)), + inverse = super::CastInt32ToNumeric(None), is_monotone = true )] pub fn cast_numeric_to_int32(mut a: Numeric) -> Result { @@ -184,7 +184,7 @@ pub fn cast_numeric_to_int32(mut a: Numeric) -> Result { #[sqlfunc( sqlname = "numeric_to_bigint", preserves_uniqueness = false, - inverse = to_unary!(super::CastInt64ToNumeric(None)), + inverse = super::CastInt64ToNumeric(None), is_monotone = true )] pub fn cast_numeric_to_int64(mut a: Numeric) -> Result { @@ -198,7 +198,7 @@ pub fn cast_numeric_to_int64(mut a: Numeric) -> Result { #[sqlfunc( sqlname = "numeric_to_real", preserves_uniqueness = false, - inverse = to_unary!(super::CastFloat32ToNumeric(None)), + inverse = super::CastFloat32ToNumeric(None), is_monotone = true )] pub fn cast_numeric_to_float32(a: Numeric) -> Result { @@ -213,7 +213,7 @@ pub fn cast_numeric_to_float32(a: Numeric) -> Result { #[sqlfunc( sqlname = "numeric_to_double", preserves_uniqueness = false, - inverse = to_unary!(super::CastFloat64ToNumeric(None)), + inverse = super::CastFloat64ToNumeric(None), is_monotone = true )] pub fn cast_numeric_to_float64(a: Numeric) -> Result { @@ -228,7 +228,7 @@ pub fn cast_numeric_to_float64(a: Numeric) -> Result { #[sqlfunc( sqlname = "numeric_to_text", preserves_uniqueness = false, - inverse = to_unary!(super::CastStringToNumeric(None)) + inverse = super::CastStringToNumeric(None) )] fn cast_numeric_to_string(a: Numeric) -> String { let mut buf = String::new(); @@ -239,7 +239,7 @@ fn cast_numeric_to_string(a: Numeric) -> String { #[sqlfunc( sqlname = "numeric_to_uint2", preserves_uniqueness = false, - inverse = to_unary!(super::CastUint16ToNumeric(None)), + inverse = super::CastUint16ToNumeric(None), is_monotone = true )] fn cast_numeric_to_uint16(mut a: Numeric) -> Result { @@ -255,7 +255,7 @@ fn cast_numeric_to_uint16(mut a: Numeric) -> Result { #[sqlfunc( sqlname = "numeric_to_uint4", preserves_uniqueness = false, - inverse = to_unary!(super::CastUint32ToNumeric(None)), + inverse = super::CastUint32ToNumeric(None), is_monotone = true )] fn cast_numeric_to_uint32(mut a: Numeric) -> Result { @@ -269,7 +269,7 @@ fn cast_numeric_to_uint32(mut a: Numeric) -> Result { #[sqlfunc( sqlname = "numeric_to_uint8", preserves_uniqueness = false, - inverse = to_unary!(super::CastUint64ToNumeric(None)), + inverse = super::CastUint64ToNumeric(None), is_monotone = true )] fn cast_numeric_to_uint64(mut a: Numeric) -> Result { diff --git a/src/expr/src/scalar/func/impls/oid.rs b/src/expr/src/scalar/func/impls/oid.rs index 8dc2459c20b61..1ac1698d48a38 100644 --- a/src/expr/src/scalar/func/impls/oid.rs +++ b/src/expr/src/scalar/func/impls/oid.rs @@ -16,7 +16,7 @@ use mz_repr::strconv; #[sqlfunc( sqlname = "oid_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToOid) + inverse = super::CastStringToOid )] fn cast_oid_to_string(a: Oid) -> String { let mut buf = String::new(); @@ -27,7 +27,7 @@ fn cast_oid_to_string(a: Oid) -> String { #[sqlfunc( sqlname = "oid_to_integer", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt32ToOid) + inverse = super::CastInt32ToOid )] fn cast_oid_to_int32(a: Oid) -> i32 { // For historical reasons in PostgreSQL, the bytes of the `u32` are @@ -42,7 +42,7 @@ fn cast_oid_to_int32(a: Oid) -> i32 { #[sqlfunc( sqlname = "oid_to_bigint", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt64ToOid) + inverse = super::CastInt64ToOid )] fn cast_oid_to_int64(a: Oid) -> i64 { i64::from(a.0) @@ -51,7 +51,7 @@ fn cast_oid_to_int64(a: Oid) -> i64 { #[sqlfunc( sqlname = "oidtoregclass", preserves_uniqueness = true, - inverse = to_unary!(super::CastRegClassToOid) + inverse = super::CastRegClassToOid )] fn cast_oid_to_reg_class(a: Oid) -> RegClass { RegClass(a.0) @@ -60,7 +60,7 @@ fn cast_oid_to_reg_class(a: Oid) -> RegClass { #[sqlfunc( sqlname = "oidtoregproc", preserves_uniqueness = true, - inverse = to_unary!(super::CastRegProcToOid) + inverse = super::CastRegProcToOid )] fn cast_oid_to_reg_proc(a: Oid) -> RegProc { RegProc(a.0) @@ -69,7 +69,7 @@ fn cast_oid_to_reg_proc(a: Oid) -> RegProc { #[sqlfunc( sqlname = "oidtoregtype", preserves_uniqueness = true, - inverse = to_unary!(super::CastRegTypeToOid) + inverse = super::CastRegTypeToOid )] fn cast_oid_to_reg_type(a: Oid) -> RegType { RegType(a.0) diff --git a/src/expr/src/scalar/func/impls/pg_legacy_char.rs b/src/expr/src/scalar/func/impls/pg_legacy_char.rs index fd24b6fa14110..91136e777dbf4 100644 --- a/src/expr/src/scalar/func/impls/pg_legacy_char.rs +++ b/src/expr/src/scalar/func/impls/pg_legacy_char.rs @@ -38,7 +38,7 @@ where #[sqlfunc( sqlname = "\"char\"_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToPgLegacyChar) + inverse = super::CastStringToPgLegacyChar )] fn cast_pg_legacy_char_to_string(a: PgLegacyChar) -> Result { let mut buf = String::new(); @@ -49,7 +49,7 @@ fn cast_pg_legacy_char_to_string(a: PgLegacyChar) -> Result { #[sqlfunc( sqlname = "\"char\"_to_char", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToPgLegacyChar) + inverse = super::CastStringToPgLegacyChar )] fn cast_pg_legacy_char_to_char(a: PgLegacyChar) -> Result, EvalError> { let mut buf = String::new(); @@ -60,10 +60,10 @@ fn cast_pg_legacy_char_to_char(a: PgLegacyChar) -> Result, EvalErro #[sqlfunc( sqlname = "\"char\"_to_varchar", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToVarChar{ + inverse = super::CastStringToVarChar{ fail_on_len: false, length: Some(VarCharMaxLength::try_from(1).unwrap()), - }) + } )] fn cast_pg_legacy_char_to_var_char(a: PgLegacyChar) -> Result, EvalError> { let mut buf = String::new(); @@ -74,7 +74,7 @@ fn cast_pg_legacy_char_to_var_char(a: PgLegacyChar) -> Result, E #[sqlfunc( sqlname = "\"char\"_to_integer", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt32ToPgLegacyChar) + inverse = super::CastInt32ToPgLegacyChar )] fn cast_pg_legacy_char_to_int32(a: PgLegacyChar) -> i32 { // Per PostgreSQL, casts to `i32` are performed as if `PgLegacyChar` is diff --git a/src/expr/src/scalar/func/impls/regproc.rs b/src/expr/src/scalar/func/impls/regproc.rs index f2f50269b0385..af2c33b3d96af 100644 --- a/src/expr/src/scalar/func/impls/regproc.rs +++ b/src/expr/src/scalar/func/impls/regproc.rs @@ -13,7 +13,7 @@ use mz_repr::adt::system::{Oid, RegClass, RegProc, RegType}; #[sqlfunc( sqlname = "regclasstooid", preserves_uniqueness = true, - inverse = to_unary!(super::CastOidToRegClass) + inverse = super::CastOidToRegClass )] fn cast_reg_class_to_oid(a: RegClass) -> Oid { Oid(a.0) @@ -22,7 +22,7 @@ fn cast_reg_class_to_oid(a: RegClass) -> Oid { #[sqlfunc( sqlname = "regproctooid", preserves_uniqueness = true, - inverse = to_unary!(super::CastOidToRegProc) + inverse = super::CastOidToRegProc )] fn cast_reg_proc_to_oid(a: RegProc) -> Oid { Oid(a.0) @@ -31,7 +31,7 @@ fn cast_reg_proc_to_oid(a: RegProc) -> Oid { #[sqlfunc( sqlname = "regtypetooid", preserves_uniqueness = true, - inverse = to_unary!(super::CastOidToRegType) + inverse = super::CastOidToRegType )] fn cast_reg_type_to_oid(a: RegType) -> Oid { Oid(a.0) diff --git a/src/expr/src/scalar/func/impls/string.rs b/src/expr/src/scalar/func/impls/string.rs index bfc99ff1e8a34..35fecbcccca74 100644 --- a/src/expr/src/scalar/func/impls/string.rs +++ b/src/expr/src/scalar/func/impls/string.rs @@ -39,7 +39,7 @@ use crate::{Eval, EvalError, MirScalarExpr, UnaryFunc, like_pattern}; #[sqlfunc( sqlname = "text_to_boolean", preserves_uniqueness = false, - inverse = to_unary!(super::CastBoolToString) + inverse = super::CastBoolToString )] fn cast_string_to_bool<'a>(a: &'a str) -> Result { strconv::parse_bool(a).err_into() @@ -51,7 +51,7 @@ fn cast_string_to_bool<'a>(a: &'a str) -> Result { // collapse to 'a'::"char"), so inverse-cast canonicalization of // `c::text = lit` would silently change results. preserves_uniqueness = false, - inverse = to_unary!(super::CastPgLegacyCharToString) + inverse = super::CastPgLegacyCharToString )] fn cast_string_to_pg_legacy_char<'a>(a: &'a str) -> PgLegacyChar { PgLegacyChar(a.as_bytes().get(0).copied().unwrap_or(0)) @@ -70,7 +70,7 @@ fn cast_string_to_pg_legacy_name<'a>(a: &'a str) -> PgLegacyName { // `b::text = lit` would otherwise rewrite to a comparison that ignores // the actual textual form. preserves_uniqueness = false, - inverse = to_unary!(super::CastBytesToString) + inverse = super::CastBytesToString )] fn cast_string_to_bytes<'a>(a: &'a str) -> Result, EvalError> { strconv::parse_bytes(a).err_into() @@ -79,7 +79,7 @@ fn cast_string_to_bytes<'a>(a: &'a str) -> Result, EvalError> { #[sqlfunc( sqlname = "text_to_smallint", preserves_uniqueness = false, - inverse = to_unary!(super::CastInt16ToString) + inverse = super::CastInt16ToString )] fn cast_string_to_int16<'a>(a: &'a str) -> Result { strconv::parse_int16(a).err_into() @@ -88,7 +88,7 @@ fn cast_string_to_int16<'a>(a: &'a str) -> Result { #[sqlfunc( sqlname = "text_to_integer", preserves_uniqueness = false, - inverse = to_unary!(super::CastInt32ToString) + inverse = super::CastInt32ToString )] fn cast_string_to_int32<'a>(a: &'a str) -> Result { strconv::parse_int32(a).err_into() @@ -97,7 +97,7 @@ fn cast_string_to_int32<'a>(a: &'a str) -> Result { #[sqlfunc( sqlname = "text_to_bigint", preserves_uniqueness = false, - inverse = to_unary!(super::CastInt64ToString) + inverse = super::CastInt64ToString )] fn cast_string_to_int64<'a>(a: &'a str) -> Result { strconv::parse_int64(a).err_into() @@ -106,7 +106,7 @@ fn cast_string_to_int64<'a>(a: &'a str) -> Result { #[sqlfunc( sqlname = "text_to_real", preserves_uniqueness = false, - inverse = to_unary!(super::CastFloat32ToString) + inverse = super::CastFloat32ToString )] fn cast_string_to_float32<'a>(a: &'a str) -> Result { strconv::parse_float32(a).err_into() @@ -115,7 +115,7 @@ fn cast_string_to_float32<'a>(a: &'a str) -> Result { #[sqlfunc( sqlname = "text_to_double", preserves_uniqueness = false, - inverse = to_unary!(super::CastFloat64ToString) + inverse = super::CastFloat64ToString )] fn cast_string_to_float64<'a>(a: &'a str) -> Result { strconv::parse_float64(a).err_into() @@ -124,7 +124,7 @@ fn cast_string_to_float64<'a>(a: &'a str) -> Result { #[sqlfunc( sqlname = "text_to_oid", preserves_uniqueness = false, - inverse = to_unary!(super::CastOidToString) + inverse = super::CastOidToString )] fn cast_string_to_oid<'a>(a: &'a str) -> Result { Ok(Oid(strconv::parse_oid(a)?)) @@ -133,7 +133,7 @@ fn cast_string_to_oid<'a>(a: &'a str) -> Result { #[sqlfunc( sqlname = "text_to_uint2", preserves_uniqueness = false, - inverse = to_unary!(super::CastUint16ToString) + inverse = super::CastUint16ToString )] fn cast_string_to_uint16(a: &str) -> Result { strconv::parse_uint16(a).err_into() @@ -142,7 +142,7 @@ fn cast_string_to_uint16(a: &str) -> Result { #[sqlfunc( sqlname = "text_to_uint4", preserves_uniqueness = false, - inverse = to_unary!(super::CastUint32ToString) + inverse = super::CastUint32ToString )] fn cast_string_to_uint32(a: &str) -> Result { strconv::parse_uint32(a).err_into() @@ -151,13 +151,13 @@ fn cast_string_to_uint32(a: &str) -> Result { #[sqlfunc( sqlname = "text_to_uint8", preserves_uniqueness = false, - inverse = to_unary!(super::CastUint64ToString) + inverse = super::CastUint64ToString )] fn cast_string_to_uint64(a: &str) -> Result { strconv::parse_uint64(a).err_into() } -#[sqlfunc(preserves_uniqueness = true, inverse = to_unary!(Reverse))] +#[sqlfunc(preserves_uniqueness = true, inverse = Reverse)] fn reverse<'a>(a: &'a str) -> String { a.chars().rev().collect() } @@ -207,7 +207,7 @@ impl fmt::Display for CastStringToNumeric { #[sqlfunc( sqlname = "text_to_date", preserves_uniqueness = false, - inverse = to_unary!(super::CastDateToString) + inverse = super::CastDateToString )] fn cast_string_to_date<'a>(a: &'a str) -> Result { strconv::parse_date(a).err_into() @@ -216,7 +216,7 @@ fn cast_string_to_date<'a>(a: &'a str) -> Result { #[sqlfunc( sqlname = "text_to_time", preserves_uniqueness = false, - inverse = to_unary!(super::CastTimeToString) + inverse = super::CastTimeToString )] fn cast_string_to_time<'a>(a: &'a str) -> Result { strconv::parse_time(a).err_into() @@ -316,7 +316,7 @@ impl fmt::Display for CastStringToTimestampTz { #[sqlfunc( sqlname = "text_to_interval", preserves_uniqueness = false, - inverse = to_unary!(super::CastIntervalToString) + inverse = super::CastIntervalToString )] fn cast_string_to_interval<'a>(a: &'a str) -> Result { strconv::parse_interval(a).err_into() @@ -325,7 +325,7 @@ fn cast_string_to_interval<'a>(a: &'a str) -> Result { #[sqlfunc( sqlname = "text_to_uuid", preserves_uniqueness = false, - inverse = to_unary!(super::CastUuidToString) + inverse = super::CastUuidToString )] fn cast_string_to_uuid<'a>(a: &'a str) -> Result { strconv::parse_uuid(a).err_into() @@ -1015,7 +1015,7 @@ impl fmt::Display for CastStringToInt2Vector { #[sqlfunc( sqlname = "text_to_jsonb", preserves_uniqueness = false, - inverse = to_unary!(super::CastJsonbToString) + inverse = super::CastJsonbToString )] // TODO(jamii): it would be much more efficient to skip the intermediate repr::jsonb::Jsonb. fn cast_string_to_jsonb<'a>(a: &'a str) -> Result { diff --git a/src/expr/src/scalar/func/impls/time.rs b/src/expr/src/scalar/func/impls/time.rs index 9b75479e20e8d..849d43dbe9f05 100644 --- a/src/expr/src/scalar/func/impls/time.rs +++ b/src/expr/src/scalar/func/impls/time.rs @@ -25,7 +25,7 @@ use crate::scalar::func::EagerUnaryFunc; #[sqlfunc( sqlname = "time_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToTime) + inverse = super::CastStringToTime )] fn cast_time_to_string(a: NaiveTime) -> String { let mut buf = String::new(); @@ -36,7 +36,7 @@ fn cast_time_to_string(a: NaiveTime) -> String { #[sqlfunc( sqlname = "time_to_interval", preserves_uniqueness = true, - inverse = to_unary!(super::CastIntervalToTime) + inverse = super::CastIntervalToTime )] fn cast_time_to_interval(t: NaiveTime) -> Interval { // wont overflow because value can't exceed 24 hrs + 1_000_000 ns = 86_400 seconds + 1_000_000 ns = 86_400_001_000 us diff --git a/src/expr/src/scalar/func/impls/timestamp.rs b/src/expr/src/scalar/func/impls/timestamp.rs index 625ce9f94f107..7ecda69066a6d 100644 --- a/src/expr/src/scalar/func/impls/timestamp.rs +++ b/src/expr/src/scalar/func/impls/timestamp.rs @@ -32,7 +32,7 @@ use crate::scalar::func::{EagerUnaryFunc, TimestampLike}; #[sqlfunc( sqlname = "timestamp_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToTimestamp(None)) + inverse = super::CastStringToTimestamp(None) )] fn cast_timestamp_to_string(a: CheckedTimestamp) -> String { let mut buf = String::new(); @@ -43,7 +43,7 @@ fn cast_timestamp_to_string(a: CheckedTimestamp) -> String { #[sqlfunc( sqlname = "timestamp_with_time_zone_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToTimestampTz(None)) + inverse = super::CastStringToTimestampTz(None) )] fn cast_timestamp_tz_to_string(a: CheckedTimestamp>) -> String { let mut buf = String::new(); @@ -54,7 +54,7 @@ fn cast_timestamp_tz_to_string(a: CheckedTimestamp>) -> String { #[sqlfunc( sqlname = "timestamp_to_date", preserves_uniqueness = false, - inverse = to_unary!(super::CastDateToTimestamp(None)), + inverse = super::CastDateToTimestamp(None), is_monotone = true )] fn cast_timestamp_to_date(a: CheckedTimestamp) -> Result { @@ -64,7 +64,7 @@ fn cast_timestamp_to_date(a: CheckedTimestamp) -> Result>) -> Result { diff --git a/src/expr/src/scalar/func/impls/uint16.rs b/src/expr/src/scalar/func/impls/uint16.rs index c386835620ac0..3dfebd9a791d2 100644 --- a/src/expr/src/scalar/func/impls/uint16.rs +++ b/src/expr/src/scalar/func/impls/uint16.rs @@ -20,7 +20,7 @@ use crate::scalar::func::EagerUnaryFunc; #[sqlfunc( sqlname = "~", preserves_uniqueness = true, - inverse = to_unary!(super::BitNotUint16) + inverse = super::BitNotUint16 )] fn bit_not_uint16(a: u16) -> u16 { !a @@ -29,7 +29,7 @@ fn bit_not_uint16(a: u16) -> u16 { #[sqlfunc( sqlname = "uint2_to_real", preserves_uniqueness = true, - inverse = to_unary!(super::CastFloat32ToUint16), + inverse = super::CastFloat32ToUint16, is_monotone = true )] fn cast_uint16_to_float32(a: u16) -> f32 { @@ -39,7 +39,7 @@ fn cast_uint16_to_float32(a: u16) -> f32 { #[sqlfunc( sqlname = "uint2_to_double", preserves_uniqueness = true, - inverse = to_unary!(super::CastFloat64ToUint16), + inverse = super::CastFloat64ToUint16, is_monotone = true )] fn cast_uint16_to_float64(a: u16) -> f64 { @@ -49,7 +49,7 @@ fn cast_uint16_to_float64(a: u16) -> f64 { #[sqlfunc( sqlname = "uint2_to_uint4", preserves_uniqueness = true, - inverse = to_unary!(super::CastUint32ToUint16), + inverse = super::CastUint32ToUint16, is_monotone = true )] fn cast_uint16_to_uint32(a: u16) -> u32 { @@ -59,7 +59,7 @@ fn cast_uint16_to_uint32(a: u16) -> u32 { #[sqlfunc( sqlname = "uint2_to_uint8", preserves_uniqueness = true, - inverse = to_unary!(super::CastUint64ToUint16), + inverse = super::CastUint64ToUint16, is_monotone = true )] fn cast_uint16_to_uint64(a: u16) -> u64 { @@ -69,7 +69,7 @@ fn cast_uint16_to_uint64(a: u16) -> u64 { #[sqlfunc( sqlname = "uint2_to_smallint", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt16ToUint16), + inverse = super::CastInt16ToUint16, is_monotone = true )] fn cast_uint16_to_int16(a: u16) -> Result { @@ -79,7 +79,7 @@ fn cast_uint16_to_int16(a: u16) -> Result { #[sqlfunc( sqlname = "uint2_to_integer", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt32ToUint16), + inverse = super::CastInt32ToUint16, is_monotone = true )] fn cast_uint16_to_int32(a: u16) -> i32 { @@ -88,7 +88,7 @@ fn cast_uint16_to_int32(a: u16) -> i32 { #[sqlfunc( sqlname = "uint2_to_bigint", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt64ToUint16), + inverse = super::CastInt64ToUint16, is_monotone = true )] fn cast_uint16_to_int64(a: u16) -> i64 { @@ -98,7 +98,7 @@ fn cast_uint16_to_int64(a: u16) -> i64 { #[sqlfunc( sqlname = "uint2_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToUint16) + inverse = super::CastStringToUint16 )] fn cast_uint16_to_string(a: u16) -> String { let mut buf = String::new(); diff --git a/src/expr/src/scalar/func/impls/uint32.rs b/src/expr/src/scalar/func/impls/uint32.rs index 0c42de90c4ddf..fa6c43f1375a5 100644 --- a/src/expr/src/scalar/func/impls/uint32.rs +++ b/src/expr/src/scalar/func/impls/uint32.rs @@ -20,7 +20,7 @@ use crate::scalar::func::EagerUnaryFunc; #[sqlfunc( sqlname = "~", preserves_uniqueness = true, - inverse = to_unary!(super::BitNotUint32) + inverse = super::BitNotUint32 )] fn bit_not_uint32(a: u32) -> u32 { !a @@ -29,7 +29,7 @@ fn bit_not_uint32(a: u32) -> u32 { #[sqlfunc( sqlname = "uint4_to_real", preserves_uniqueness = false, - inverse = to_unary!(super::CastFloat32ToUint32), + inverse = super::CastFloat32ToUint32, is_monotone = true )] fn cast_uint32_to_float32(a: u32) -> f32 { @@ -43,7 +43,7 @@ fn cast_uint32_to_float32(a: u32) -> f32 { #[sqlfunc( sqlname = "uint4_to_double", preserves_uniqueness = true, - inverse = to_unary!(super::CastFloat64ToUint32), + inverse = super::CastFloat64ToUint32, is_monotone = true )] fn cast_uint32_to_float64(a: u32) -> f64 { @@ -57,7 +57,7 @@ fn cast_uint32_to_float64(a: u32) -> f64 { // into an index lookup on `col`, skipping the cast and dropping its // out-of-range error. preserves_uniqueness = false, - inverse = to_unary!(super::CastUint16ToUint32), + inverse = super::CastUint16ToUint32, is_monotone = true )] fn cast_uint32_to_uint16(a: u32) -> Result { @@ -67,7 +67,7 @@ fn cast_uint32_to_uint16(a: u32) -> Result { #[sqlfunc( sqlname = "uint4_to_uint8", preserves_uniqueness = true, - inverse = to_unary!(super::CastUint64ToUint32), + inverse = super::CastUint64ToUint32, is_monotone = true )] fn cast_uint32_to_uint64(a: u32) -> u64 { @@ -78,7 +78,7 @@ fn cast_uint32_to_uint64(a: u32) -> u64 { sqlname = "uint4_to_smallint", // Partial: errors for `u32` values above `i16::MAX`. See `uint4_to_uint2`. preserves_uniqueness = false, - inverse = to_unary!(super::CastInt16ToUint32), + inverse = super::CastInt16ToUint32, is_monotone = true )] fn cast_uint32_to_int16(a: u32) -> Result { @@ -89,7 +89,7 @@ fn cast_uint32_to_int16(a: u32) -> Result { sqlname = "uint4_to_integer", // Partial: errors for `u32` values above `i32::MAX`. See `uint4_to_uint2`. preserves_uniqueness = false, - inverse = to_unary!(super::CastInt32ToUint32), + inverse = super::CastInt32ToUint32, is_monotone = true )] fn cast_uint32_to_int32(a: u32) -> Result { @@ -99,7 +99,7 @@ fn cast_uint32_to_int32(a: u32) -> Result { #[sqlfunc( sqlname = "uint4_to_bigint", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt64ToUint32), + inverse = super::CastInt64ToUint32, is_monotone = true )] fn cast_uint32_to_int64(a: u32) -> i64 { @@ -109,7 +109,7 @@ fn cast_uint32_to_int64(a: u32) -> i64 { #[sqlfunc( sqlname = "uint4_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToUint32) + inverse = super::CastStringToUint32 )] fn cast_uint32_to_string(a: u32) -> String { let mut buf = String::new(); diff --git a/src/expr/src/scalar/func/impls/uint64.rs b/src/expr/src/scalar/func/impls/uint64.rs index 966f9dca9fcd8..9e42d38d91bf4 100644 --- a/src/expr/src/scalar/func/impls/uint64.rs +++ b/src/expr/src/scalar/func/impls/uint64.rs @@ -20,7 +20,7 @@ use crate::scalar::func::EagerUnaryFunc; #[sqlfunc( sqlname = "~", preserves_uniqueness = true, - inverse = to_unary!(super::BitNotUint64) + inverse = super::BitNotUint64 )] fn bit_not_uint64(a: u64) -> u64 { !a @@ -29,7 +29,7 @@ fn bit_not_uint64(a: u64) -> u64 { #[sqlfunc( sqlname = "uint8_to_real", preserves_uniqueness = false, - inverse = to_unary!(super::CastFloat32ToUint64), + inverse = super::CastFloat32ToUint64, is_monotone = true )] fn cast_uint64_to_float32(a: u64) -> f32 { @@ -43,7 +43,7 @@ fn cast_uint64_to_float32(a: u64) -> f32 { #[sqlfunc( sqlname = "uint8_to_double", preserves_uniqueness = false, - inverse = to_unary!(super::CastFloat64ToUint64), + inverse = super::CastFloat64ToUint64, is_monotone = true )] fn cast_uint64_to_float64(a: u64) -> f64 { @@ -57,7 +57,7 @@ fn cast_uint64_to_float64(a: u64) -> f64 { #[sqlfunc( sqlname = "uint8_to_uint2", preserves_uniqueness = true, - inverse = to_unary!(super::CastUint16ToUint64), + inverse = super::CastUint16ToUint64, is_monotone = true )] fn cast_uint64_to_uint16(a: u64) -> Result { @@ -67,7 +67,7 @@ fn cast_uint64_to_uint16(a: u64) -> Result { #[sqlfunc( sqlname = "uint8_to_uint4", preserves_uniqueness = true, - inverse = to_unary!(super::CastUint32ToUint64), + inverse = super::CastUint32ToUint64, is_monotone = true )] fn cast_uint64_to_uint32(a: u64) -> Result { @@ -77,7 +77,7 @@ fn cast_uint64_to_uint32(a: u64) -> Result { #[sqlfunc( sqlname = "uint8_to_smallint", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt16ToUint64), + inverse = super::CastInt16ToUint64, is_monotone = true )] fn cast_uint64_to_int16(a: u64) -> Result { @@ -87,7 +87,7 @@ fn cast_uint64_to_int16(a: u64) -> Result { #[sqlfunc( sqlname = "uint8_to_integer", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt32ToUint64), + inverse = super::CastInt32ToUint64, is_monotone = true )] fn cast_uint64_to_int32(a: u64) -> Result { @@ -97,7 +97,7 @@ fn cast_uint64_to_int32(a: u64) -> Result { #[sqlfunc( sqlname = "uint8_to_bigint", preserves_uniqueness = true, - inverse = to_unary!(super::CastInt64ToUint64), + inverse = super::CastInt64ToUint64, is_monotone = true )] fn cast_uint64_to_int64(a: u64) -> Result { @@ -107,7 +107,7 @@ fn cast_uint64_to_int64(a: u64) -> Result { #[sqlfunc( sqlname = "uint8_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToUint64) + inverse = super::CastStringToUint64 )] fn cast_uint64_to_string(a: u64) -> String { let mut buf = String::new(); diff --git a/src/expr/src/scalar/func/impls/uuid.rs b/src/expr/src/scalar/func/impls/uuid.rs index 95b6064544f47..cc02680a3642e 100644 --- a/src/expr/src/scalar/func/impls/uuid.rs +++ b/src/expr/src/scalar/func/impls/uuid.rs @@ -14,7 +14,7 @@ use uuid::Uuid; #[sqlfunc( sqlname = "uuid_to_text", preserves_uniqueness = true, - inverse = to_unary!(super::CastStringToUuid) + inverse = super::CastStringToUuid )] fn cast_uuid_to_string(u: Uuid) -> String { let mut buf = String::with_capacity(36); diff --git a/src/expr/src/scalar/func/impls/varchar.rs b/src/expr/src/scalar/func/impls/varchar.rs index 76aeec77477eb..ffd270357a525 100644 --- a/src/expr/src/scalar/func/impls/varchar.rs +++ b/src/expr/src/scalar/func/impls/varchar.rs @@ -15,10 +15,10 @@ use mz_repr::adt::varchar::VarChar; sqlname = "varchar_to_text", preserves_uniqueness = true, is_eliminable_cast = true, - inverse = to_unary!(super::CastStringToVarChar { + inverse = super::CastStringToVarChar { length: None, fail_on_len: false, - }), + }, )] fn cast_var_char_to_string<'a>(a: VarChar<&'a str>) -> &'a str { a.0 diff --git a/src/expr/src/scalar/func/variadic.rs b/src/expr/src/scalar/func/variadic.rs index 8a4089ac0ab88..c284acbf9d387 100644 --- a/src/expr/src/scalar/func/variadic.rs +++ b/src/expr/src/scalar/func/variadic.rs @@ -373,7 +373,7 @@ fn array_fill<'a>( pub struct ArrayIndex { pub offset: i64, } -#[sqlfunc(ArrayIndex, sqlname = "array_index", introduces_nulls = true)] +#[sqlfunc(ArrayIndex, sqlname = "array_index")] fn array_index<'a, T: FromDatum<'a>>( &self, array: Array<'a, T>, @@ -601,16 +601,14 @@ impl fmt::Display for RangeCreate { #[sqlfunc( RangeCreate, skip_display = true, - output_type_expr = "SqlScalarType::Range { element_type: Box::new(self.elem_type.clone()) }.nullable(false)", - introduces_nulls = false + output_type_expr = "SqlScalarType::Range { element_type: Box::new(self.elem_type.clone()) }.nullable(false)" )] fn range_create<'a>( &self, lower: Datum<'a>, upper: Datum<'a>, flags_datum: Datum<'a>, - temp_storage: &'a RowArena, -) -> Result, EvalError> { +) -> Result>, EvalError> { let flags = match flags_datum { Datum::Null => { return Err(EvalError::InvalidRange( @@ -629,9 +627,7 @@ fn range_create<'a>( range.canonicalize()?; - Ok(temp_storage.make_datum(|row| { - row.push_range(range).expect("errors already handled"); - })) + Ok(range) } #[sqlfunc(sqlname = "datediff")] @@ -1005,8 +1001,7 @@ fn record_create<'a>( } #[sqlfunc( - output_type_expr = "input_types[0].scalar_type.unwrap_list_nth_layer_type(input_types.len() - 1).clone().nullable(true)", - introduces_nulls = true + output_type_expr = "input_types[0].scalar_type.unwrap_list_nth_layer_type(input_types.len() - 1).clone().nullable(true)" )] // TODO(benesch): remove potentially dangerous usage of `as`. #[allow(clippy::as_conversions)] @@ -1296,8 +1291,7 @@ fn pad_leading( } #[sqlfunc( - output_type_expr = "SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true)", - introduces_nulls = true + output_type_expr = "SqlScalarType::Array(Box::new(SqlScalarType::String)).nullable(true)" )] fn regexp_match<'a>( haystack: &'a str, From 2aeabaa165983b440f2857d5b15c870c003231f5 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Thu, 24 Sep 2026 16:23:23 +0200 Subject: [PATCH 10/19] expr-derive-impl: Report conflicts with Error::custom, and reject skip_display on a unit struct The modifier conflict checks built their errors with `unknown_field`, which renders as "Unknown field: " for fields the macro knows. They now use `Error::custom`, like every other legality error in the crate. `skip_display` exists so that a struct whose name depends on its state can keep a hand-written `Display`. Without a `&self` receiver the macro defines a unit struct, which has no such state, so the combination is now an error. The unary and binary `&self` paths were covered only by insta snapshots, which check the generated tokens but not that they compile. A test in `mz-expr` defines a stateful unary and a stateful binary function and evaluates both through their eager traits. The reference drops the per-row arity notes in favour of one sentence, states that the struct name defaults to the camel-cased function name with or without `&self`, and drops the counts and the chronology from the section on shapes the macro does not cover. Co-Authored-By: Claude Opus 5.5 (1M context) --- doc/developer/sqlfunc.md | 36 ++++++++++++++-------------- src/expr-derive-impl/src/generate.rs | 14 +++++++---- src/expr-derive-impl/src/lib.rs | 15 ++++++++++++ src/expr/src/scalar/func/macros.rs | 36 ++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 23 deletions(-) diff --git a/doc/developer/sqlfunc.md b/doc/developer/sqlfunc.md index 8fa18ea1240f8..3fc6e79d175db 100644 --- a/doc/developer/sqlfunc.md +++ b/doc/developer/sqlfunc.md @@ -29,12 +29,14 @@ This generates: The macro determines function arity from parameter count and types, after excluding `&self` receivers and trailing `&RowArena` parameters: -| Effective params | Dispatches to | Notes | -|---|---|---| -| 0 | Error | Nullary functions are not supported. | -| 1 | `EagerUnaryFunc` | Supports `&RowArena` and `&self`. | -| 2 | `EagerBinaryFunc` | Supports `&RowArena` and `&self`. | -| 3+ | `EagerVariadicFunc` | Supports `&RowArena` and `&self`. | +| Effective params | Dispatches to | +|---|---| +| 0 | Error: nullary functions are not supported. | +| 1 | `EagerUnaryFunc` | +| 2 | `EagerBinaryFunc` | +| 3+ | `EagerVariadicFunc` | + +Every arity accepts a `&self` receiver and a trailing `&RowArena`. **Exception:** If any parameter uses `Variadic` or `OptionalArg`, the function is always treated as variadic, regardless of parameter count. @@ -194,7 +196,8 @@ struct's own state rather than being a fixed string, so the call site keeps a hand-written `Display` impl. `RangeCreate` is an example: it picks between `int4range`, `int8range`, `daterange`, `numrange`, `tsrange`, and `tstzrange` based on its `elem_type` field. Setting it to `true` rejects `sqlname`, whose only reader is the -suppressed impl. +suppressed impl, and requires a `&self` receiver, since a unit struct has no state to +display. * **Type:** `bool` * **Default:** `false` @@ -208,8 +211,8 @@ arity's table is rejected with an error naming both the modifier and the arity. ### Struct name -The struct name can be specified as the first positional argument, at any arity. -This is required when a `&self` receiver is present (the struct is defined externally): +The struct name defaults to the camel-cased function name, and the first positional argument to the `sqlfunc` macro overrides it. +With a `&self` receiver the macro does not define the struct, so the name must match the one defined externally: ```rust #[sqlfunc( @@ -228,8 +231,7 @@ fn array_fill<'a>( } ``` -Without a `&self` receiver, the struct name defaults to the camel-cased function name. -It can still be overridden with the first positional argument: +Without `&self`, the macro defines the struct under that name: ```rust #[sqlfunc(Replace, sqlname = "replace")] @@ -252,7 +254,6 @@ Without `&self`, the macro generates the struct itself (with standard derives) i A trailing `&RowArena` parameter gives the function access to temporary storage for allocating return values that borrow from the arena. It is excluded from arity detection and from the generated `Input` type. -The arena is always passed to unary, binary, and variadic `call` implementations (the trait requires it); for functions that don't use it, the parameter is simply unused. ## Variadic functions @@ -384,14 +385,13 @@ Two shapes stay hand-written by design. * Functions generic over an `Eval` implementor, which hold sub-expressions in a `Box` or a `Box<[E]>` and evaluate them per element. The macro would need to emit an implementation generic over a struct type parameter with a trait bound, which is a - different mechanism from the type-parameter erasure it applies today. The nine + different mechanism from the type-parameter erasure it applies. The `impl LazyUnaryFunc` blocks under `src/expr/src/scalar/func/impls/` are these. * Functions that do not evaluate every operand. The macro emits `Eager*` implementations, which evaluate all arguments before dispatch. `And`, `Or`, `Coalesce`, `Greatest`, `Least`, `ErrorIfNull`, and `CaseLiteral` are these. -The ten remaining hand-written `LazyUnaryFunc` implementations under -`src/expr/src/scalar/func/impls/` are in neither category, and all ten are convertible. -Nine allocate their output into a `RowArena`, which `EagerUnaryFunc::call` now supplies. -`RecordGet` returns a field borrowed from its input, so it needs no arena, only an -`output_type_expr` reading `input_type`. +Hand-written `LazyUnaryFunc` implementations under `src/expr/src/scalar/func/impls/` +outside both categories are convertible. One that allocates its output gets a +`RowArena` from `EagerUnaryFunc::call`, and `RecordGet`, which returns a field borrowed +from its input, needs only an `output_type_expr` reading `input_type`. diff --git a/src/expr-derive-impl/src/generate.rs b/src/expr-derive-impl/src/generate.rs index fee056c08d7c4..a13292912834c 100644 --- a/src/expr-derive-impl/src/generate.rs +++ b/src/expr-derive-impl/src/generate.rs @@ -234,19 +234,23 @@ pub(crate) fn generate( } } - // TODO: the conflict checks below construct their error with `unknown_field`, - // which renders as "Unknown field: ", while every other modifier - // legality error in the crate uses `Error::custom`. if output_type.is_some() && output_type_expr.is_some() { - return Err(darling::Error::unknown_field( + return Err(darling::Error::custom( "output_type and output_type_expr cannot be used together", )); } if mods.skip_display() && mods.sqlname().is_some() { - return Err(darling::Error::unknown_field( + return Err(darling::Error::custom( "sqlname has no effect with skip_display, which suppresses the only impl that reads it", )); } + // A hand-written `Display` is only needed when the name depends on struct state, + // and without `&self` the macro defines a unit struct that has none. + if mods.skip_display() && !has_self { + return Err(darling::Error::custom( + "skip_display requires a &self receiver, since a unit struct has no state for a hand-written Display to read", + )); + } // Replace generic type params with `Datum<'a>` for use in the trait impl's associated // types, where the function's parameters are not in scope. diff --git a/src/expr-derive-impl/src/lib.rs b/src/expr-derive-impl/src/lib.rs index 60ed3843c1c4f..1607673c03a5d 100644 --- a/src/expr-derive-impl/src/lib.rs +++ b/src/expr-derive-impl/src/lib.rs @@ -468,4 +468,19 @@ mod test { "expected the conflict error, got:\n{output}" ); } + + #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri + #[mz_ore::test] + fn skip_display_rejects_a_unit_struct() { + let (output, _input) = super::test_sqlfunc( + quote! { skip_display = true }, + quote! { + fn extract_thing(a: i64) -> i64 { a } + }, + ); + assert!( + output.contains("skip_display requires a &self receiver"), + "expected the receiver error, got:\n{output}" + ); + } } diff --git a/src/expr/src/scalar/func/macros.rs b/src/expr/src/scalar/func/macros.rs index a6522d54a549b..10f90e439003a 100644 --- a/src/expr/src/scalar/func/macros.rs +++ b/src/expr/src/scalar/func/macros.rs @@ -136,6 +136,42 @@ mod test { SqlScalarType::Float32.nullable(true) ); } + + // Separate from the tests above, which call the lazy traits' methods and would + // find them ambiguous with the eager traits in scope. + mod stateful { + use mz_expr_derive::sqlfunc; + use mz_repr::RowArena; + + use crate::EvalError; + use crate::scalar::func::EagerUnaryFunc; + use crate::scalar::func::binary::EagerBinaryFunc; + + pub struct AddOffset(i64); + + #[sqlfunc(sqlname = "add_offset")] + fn add_offset(&self, a: i64) -> i64 { + a + self.0 + } + + pub struct ScaleSum(i64); + + #[sqlfunc(sqlname = "scale_sum")] + fn scale_sum(&self, a: i64, b: i64) -> Result { + (a + b) + .checked_mul(self.0) + .ok_or(EvalError::NumericFieldOverflow) + } + + #[mz_ore::test] + fn stateful_unary_and_binary_read_their_state() { + let arena = RowArena::new(); + assert_eq!(EagerUnaryFunc::call(&AddOffset(3), 4, &arena), 7); + assert_eq!(EagerBinaryFunc::call(&ScaleSum(2), (3, 4), &arena), Ok(14)); + assert_eq!(format!("{}", AddOffset(3)), "add_offset"); + assert_eq!(format!("{}", ScaleSum(2)), "scale_sum"); + } + } } /// Temporary macro that generates the equivalent of what enum_dispatch will do in the future. We From 5c72eae2a4a492f2a782a7b69f977531ec03cab4 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Wed, 14 Oct 2026 16:32:34 +0200 Subject: [PATCH 11/19] expr-derive-impl: Classify arity by the SQL arguments alone With every arity accepting a receiver, `Arity` carried the same `has_self` in each variant and `sqlfunc` repeated the `generate` call per arity. `determine_shape` returns the `Shape` directly, and `signature::sql_args` owns the rule that a receiver and a trailing `&RowArena` are not SQL arguments, which `determine_arity` and `variadic_params` each spelled out with their own index arithmetic. The three diagnostics that started with a capital letter are lowercase, like the rest of the crate's. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/expr-derive-impl/src/generate.rs | 21 ++----- src/expr-derive-impl/src/signature.rs | 21 ++++++- src/expr-derive-impl/src/sqlfunc.rs | 87 +++++++-------------------- 3 files changed, 45 insertions(+), 84 deletions(-) diff --git a/src/expr-derive-impl/src/generate.rs b/src/expr-derive-impl/src/generate.rs index a13292912834c..f4778d271e83e 100644 --- a/src/expr-derive-impl/src/generate.rs +++ b/src/expr-derive-impl/src/generate.rs @@ -127,24 +127,11 @@ fn expand(e: &Codegen, func: &syn::ItemFn, trait_impl: TokenStream) -> TokenStre } } -/// A variadic function's parameter names and their pre-erasure types, skipping the -/// receiver and the trailing `&RowArena`. -fn variadic_params( - func: &syn::ItemFn, - arena: bool, - has_self: bool, -) -> darling::Result<(Vec, Vec)> { - let start = usize::from(has_self); - let end = func.sig.inputs.len() - usize::from(arena); - if end == start { - return Err(darling::Error::custom( - "variadic function must have at least one input parameter", - )); - } - +/// A variadic function's parameter names and their pre-erasure types. +fn variadic_params(func: &syn::ItemFn) -> darling::Result<(Vec, Vec)> { let mut types = Vec::new(); let mut names = Vec::new(); - for param in func.sig.inputs.iter().skip(start).take(end - start) { + for param in signature::sql_args(func) { match param { syn::FnArg::Typed(pat) => { let syn::Pat::Ident(ident) = &*pat.pat else { @@ -210,7 +197,7 @@ pub(crate) fn generate( Ident::new("b", Span::call_site()), ], ), - Shape::Variadic => variadic_params(func, arena, has_self)?, + Shape::Variadic => variadic_params(func)?, }; let output_type = mods.output_type().cloned(); diff --git a/src/expr-derive-impl/src/signature.rs b/src/expr-derive-impl/src/signature.rs index 649daf95c5fcb..79c0890d294de 100644 --- a/src/expr-derive-impl/src/signature.rs +++ b/src/expr-derive-impl/src/signature.rs @@ -33,6 +33,23 @@ pub(crate) fn last_is_arena(func: &syn::ItemFn) -> bool { }) } +/// The parameters that carry SQL arguments, which excludes a `&self` receiver and a +/// trailing `&RowArena`. +pub(crate) fn sql_args(func: &syn::ItemFn) -> Vec<&syn::FnArg> { + let end = func.sig.inputs.len() - usize::from(last_is_arena(func)); + func.sig + .inputs + .iter() + .take(end) + .skip(usize::from(has_receiver(func))) + .collect() +} + +/// Checks if the function takes a `self` receiver. +pub(crate) fn has_receiver(func: &syn::ItemFn) -> bool { + matches!(func.sig.inputs.first(), Some(syn::FnArg::Receiver(_))) +} + /// Checks if a parameter type accepts NULL. /// /// `Option` always accepts NULL. `OptionalArg` delegates to `T`. @@ -593,7 +610,7 @@ pub(crate) fn arg_type(arg: &syn::ItemFn, nth: usize) -> Result Err(syn::Error::new( arg.sig.inputs[nth].span(), - "Unsupported argument type", + "unsupported argument type", )), } } @@ -642,7 +659,7 @@ pub(crate) fn output_type(arg: &syn::ItemFn) -> Result<&syn::Type, syn::Error> { syn::ReturnType::Type(_, ty) => Ok(&*ty), syn::ReturnType::Default => Err(syn::Error::new( arg.sig.output.span(), - "Function needs to return a value", + "function needs to return a value", )), } } diff --git a/src/expr-derive-impl/src/sqlfunc.rs b/src/expr-derive-impl/src/sqlfunc.rs index 52f05099b7124..0cdd82029cdec 100644 --- a/src/expr-derive-impl/src/sqlfunc.rs +++ b/src/expr-derive-impl/src/sqlfunc.rs @@ -18,7 +18,7 @@ use quote::quote; use crate::generate::generate; use crate::modifiers::Modifiers; use crate::shape::Shape; -use crate::signature::last_is_arena; +use crate::signature; /// Implementation for the `#[sqlfunc]` macro. The first parameter is the attribute /// arguments, the second is the function body. The third parameter indicates @@ -47,23 +47,13 @@ pub fn sqlfunc( let generate_tests = modifiers.generates_test(); let func = syn::parse2::(item.clone())?; - let tokens = match determine_arity(&func) { - Arity::Nullary => Err(darling::Error::custom("Nullary functions not supported")), - Arity::Unary { has_self } => { - generate(Shape::Unary, &func, modifiers, struct_ty, has_self, &attr) - } - Arity::Binary { has_self } => { - generate(Shape::Binary, &func, modifiers, struct_ty, has_self, &attr) - } - Arity::Variadic { has_self } => generate( - Shape::Variadic, - &func, - modifiers, - struct_ty, - has_self, - &attr, - ), - }?; + let has_self = signature::has_receiver(&func); + let Some(shape) = determine_shape(&func) else { + return Err(darling::Error::custom( + "nullary functions are not supported", + )); + }; + let tokens = generate(shape, &func, modifiers, struct_ty, has_self, &attr)?; let test = (generate_tests && include_test).then(|| generate_test(attr, item, &func.sig.ident)); @@ -96,14 +86,6 @@ fn generate_test(_attr: TokenStream, _item: TokenStream, _name: &Ident) -> Token quote! {} } -/// Arity classification for a function annotated with `#[sqlfunc]`. -enum Arity { - Nullary, - Unary { has_self: bool }, - Binary { has_self: bool }, - Variadic { has_self: bool }, -} - /// Checks whether a parameter's type is `Variadic<...>` or `OptionalArg<...>`, /// which indicates the function should be treated as variadic regardless of /// parameter count. @@ -119,46 +101,21 @@ fn is_variadic_arg(arg: &syn::FnArg) -> bool { false } -/// Determines the arity of a function annotated with `#[sqlfunc]`. +/// The shape of a function annotated with `#[sqlfunc]`, or `None` for a function +/// without SQL arguments. /// -/// Accounts for `&self` receivers, trailing `&RowArena` parameters, and -/// parameter types like `Variadic` or `OptionalArg` that indicate -/// variadic dispatch. -fn determine_arity(func: &syn::ItemFn) -> Arity { - let arena = last_is_arena(func); - let has_self = matches!(func.sig.inputs.first(), Some(syn::FnArg::Receiver(_))); - - let mut effective_count = func.sig.inputs.len(); - if arena { - effective_count -= 1; +/// Only the SQL arguments count, so a `&self` receiver and a trailing `&RowArena` are +/// skipped. A `Variadic` or `OptionalArg` argument makes the function variadic +/// regardless of how many arguments it has. +fn determine_shape(func: &syn::ItemFn) -> Option { + let args = signature::sql_args(func); + if args.iter().any(|arg| is_variadic_arg(arg)) { + return Some(Shape::Variadic); } - if has_self { - effective_count -= 1; - } - - // Check if any effective parameter uses a variadic-typed wrapper. - let start = if has_self { 1 } else { 0 }; - let end = if arena { - func.sig.inputs.len() - 1 - } else { - func.sig.inputs.len() - }; - let has_variadic_param = func - .sig - .inputs - .iter() - .skip(start) - .take(end - start) - .any(is_variadic_arg); - - if has_variadic_param || effective_count >= 3 { - Arity::Variadic { has_self } - } else { - match effective_count { - 0 => Arity::Nullary, - 1 => Arity::Unary { has_self }, - 2 => Arity::Binary { has_self }, - _ => unreachable!(), - } + match args.len() { + 0 => None, + 1 => Some(Shape::Unary), + 2 => Some(Shape::Binary), + _ => Some(Shape::Variadic), } } From dc6036e57d883ba4598b263c815167d00e505d41 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Wed, 14 Oct 2026 16:34:29 +0200 Subject: [PATCH 12/19] expr-derive-impl: Resolve the output type in one function `generate` built the output column type and the `introduces_nulls` method in two passes, first from `output_type` and then overriding both from `output_type_expr` and `introduces_nulls`. `output_type_and_introduces_nulls` decides both in one place from the three modifiers and the generic-signature derivation. The generated code is unchanged, as the snapshots show. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/expr-derive-impl/src/generate.rs | 121 +++++++++++++++------------ 1 file changed, 69 insertions(+), 52 deletions(-) diff --git a/src/expr-derive-impl/src/generate.rs b/src/expr-derive-impl/src/generate.rs index f4778d271e83e..2a4e2e11c0bbf 100644 --- a/src/expr-derive-impl/src/generate.rs +++ b/src/expr-derive-impl/src/generate.rs @@ -150,6 +150,68 @@ fn variadic_params(func: &syn::ItemFn) -> darling::Result<(Vec, Vec darling::Result<(TokenStream, Option)> { + let output_type = mods.output_type(); + let mut output_type_expr = mods.output_type_expr().map(|expr| quote! { #expr }); + let mut introduces_nulls = mods.introduces_nulls().map(|expr| quote! { #expr }); + + if output_type.is_some() && output_type_expr.is_some() { + return Err(darling::Error::custom( + "output_type and output_type_expr cannot be used together", + )); + } + + // Derive the output type from where the generic parameters land. This reads the + // pre-erasure types, because erasure is what removes those parameters. + if !generic_params.is_empty() && output_type.is_none() && output_type_expr.is_none() { + if let Some(derived) = signature::derive_output_type_for_generics( + param_types_raw, + output_ty_raw, + generic_params, + shape == Shape::Unary, + )? { + output_type_expr = Some(derived); + if introduces_nulls.is_none() { + let nullable = signature::is_option_wrapped(output_ty_raw); + introduces_nulls = Some(quote! { #nullable }); + } + } + } + + let column_type = match (output_type, output_type_expr) { + (Some(output_type), _) => quote! { <#output_type>::as_column_type() }, + (None, Some(output_type_expr)) => output_type_expr, + (None, None) => quote! { Self::Output::as_column_type() }, + }; + // Without an `introduces_nulls` method the trait default reads `Self::Output`, + // which an explicit `output_type` replaces. + let introduces_nulls = introduces_nulls.or_else(|| { + output_type.map(|output_type| { + quote! { <#output_type as ::mz_repr::OutputDatumType<'_, ()>>::nullable() } + }) + }); + let introduces_nulls_fn = introduces_nulls.map(|introduces_nulls| { + quote! { + fn introduces_nulls(&self) -> bool { + #introduces_nulls + } + } + }); + Ok((column_type, introduces_nulls_fn)) +} + /// Generates the trait impl for one `#[sqlfunc]`-annotated function, together with the /// items [`expand`] wraps around it. /// @@ -200,32 +262,6 @@ pub(crate) fn generate( Shape::Variadic => variadic_params(func)?, }; - let output_type = mods.output_type().cloned(); - let mut output_type_expr = mods.output_type_expr().cloned(); - let mut introduces_nulls = mods.introduces_nulls().cloned(); - - // Derive the output type from where the generic parameters land. This reads the - // pre-erasure types, because erasure is what removes those parameters. - if !generic_params.is_empty() && output_type.is_none() && output_type_expr.is_none() { - if let Some(derived) = signature::derive_output_type_for_generics( - ¶m_types_raw, - output_ty_raw, - &generic_params, - shape == Shape::Unary, - )? { - output_type_expr = Some(syn::parse2(derived)?); - if introduces_nulls.is_none() { - let nullable = signature::is_option_wrapped(output_ty_raw); - introduces_nulls = Some(syn::parse_quote!(#nullable)); - } - } - } - - if output_type.is_some() && output_type_expr.is_some() { - return Err(darling::Error::custom( - "output_type and output_type_expr cannot be used together", - )); - } if mods.skip_display() && mods.sqlname().is_some() { return Err(darling::Error::custom( "sqlname has no effect with skip_display, which suppresses the only impl that reads it", @@ -259,32 +295,13 @@ pub(crate) fn generate( quote! { (#(#param_names),*) } }; - let (mut output_type_code, mut introduces_nulls_fn) = if let Some(output_type) = output_type { - let introduces_nulls_fn = quote! { - fn introduces_nulls(&self) -> bool { - <#output_type as ::mz_repr::OutputDatumType<'_, ()>>::nullable() - } - }; - ( - quote! { <#output_type>::as_column_type() }, - Some(introduces_nulls_fn), - ) - } else { - (quote! { Self::Output::as_column_type() }, None) - }; - - if let Some(output_type_expr) = output_type_expr { - output_type_code = quote! { #output_type_expr }; - } - - if let Some(introduces_nulls) = introduces_nulls { - introduces_nulls_fn = Some(quote! { - fn introduces_nulls(&self) -> bool { - #introduces_nulls - } - }); - } - + let (output_type_code, introduces_nulls_fn) = output_type_and_introduces_nulls( + shape, + &mods, + ¶m_types_raw, + output_ty_raw, + &generic_params, + )?; let methods = override_methods(shape, &mods, introduces_nulls_fn); // Every arity's `call` receives the arena; only `arena` (whether the annotated From 7ec8ed2ef9bf85dc98347731814de90e7104489d Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Wed, 14 Oct 2026 16:34:51 +0200 Subject: [PATCH 13/19] expr-derive-impl: Move the expansion tests into their own file The test module had grown to most of `lib.rs`. It moves to `test.rs` under the same module name, so the insta snapshot names stay the same. The three tests without the Miri reason now carry it like the rest, and the two public test helpers get doc comments. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/expr-derive-impl/src/lib.rs | 432 +----------------------------- src/expr-derive-impl/src/test.rs | 437 +++++++++++++++++++++++++++++++ 2 files changed, 441 insertions(+), 428 deletions(-) create mode 100644 src/expr-derive-impl/src/test.rs diff --git a/src/expr-derive-impl/src/lib.rs b/src/expr-derive-impl/src/lib.rs index 1607673c03a5d..736c7d5fb00b7 100644 --- a/src/expr-derive-impl/src/lib.rs +++ b/src/expr-derive-impl/src/lib.rs @@ -31,6 +31,8 @@ fn sqlfunc_for_test( sqlfunc(attr, item, false) } +/// Expands `#[sqlfunc(attr)]` on `item` and returns the formatted expansion, or the +/// formatted errors, along with the formatted input. #[cfg(any(feature = "test", test))] pub fn test_sqlfunc( attr: proc_macro2::TokenStream, @@ -50,437 +52,11 @@ pub fn test_sqlfunc( (output, input) } +/// [`test_sqlfunc`] over source text, as the generated snapshot tests pass it. #[cfg(any(feature = "test", test))] pub fn test_sqlfunc_str(attr: &str, item: &str) -> (String, String) { test_sqlfunc(attr.parse().unwrap(), item.parse().unwrap()) } #[cfg(test)] -mod test { - use quote::quote; - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_add_int16() { - let attr = quote! { - is_monotone = (true, true), - output_type = i16, - is_infix_op = true, - sqlname = "+", - propagates_nulls = true, - test = true, - }; - let item = quote! { - fn add_int16<'a>(a: Datum<'a>, b: Datum<'a>) -> Result, EvalError> { - a.unwrap_int16() - .checked_add(b.unwrap_int16()) - .ok_or(EvalError::NumericFieldOverflow) - .map(Datum::from) - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("add_int16", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_unary() { - let attr = quote! {test = true}; - let item = quote! { - fn unary_fn<'a>(a: Datum<'a>) -> bool { - unimplemented!() - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("unary_fn", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_unary_arena() { - let attr = quote! {test = true}; - let item = quote! { - fn unary_fn<'a>(a: Datum<'a>, temp_storage: &RowArena) -> bool { - unimplemented!() - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("unary_arena_fn", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_unary_arena_used() { - let attr = quote! { sqlname = "to_arena_string", test = true }; - let item = quote! { - fn to_arena_string<'a>(a: i32, temp_storage: &'a RowArena) -> &'a str { - temp_storage.push_string(a.to_string()) - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("unary_arena_used", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_unary_ref() { - let attr = quote! {test = true}; - let item = quote! { - fn unary_fn<'a>(a: &i16) -> bool { - unimplemented!() - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("unary_ref", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_complex_output_type() { - let attr = quote! { - is_monotone = (true, true), - output_type = "Option", - is_infix_op = true, - sqlname = "test", - propagates_nulls = true, - test = true, - }; - let item = quote! { - fn complex_output_type_fn<'a>( - a: Datum<'a>, - b: Datum<'a>, - ) -> Result, EvalError> { - unimplemented!() - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("complex_type", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_binary_arena() { - let attr = quote! {test = true}; - let item = quote! { - fn unary_fn<'a>(a: Datum<'a>, b: u16, temp_storage: &RowArena) -> bool { - unimplemented!() - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("binary_arena_fn", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_variadic_tuple() { - let attr = quote! { - Replace, - sqlname = "replace", - }; - let item = quote! { - fn replace(text: &str, from: &str, to: &str) -> Result { - Ok(text.replace(from, to)) - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("variadic_tuple", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_variadic_variadic_type() { - let attr = quote! { - Concat, - sqlname = "concat", - is_associative = true, - }; - let item = quote! { - fn concat(strs: Variadic>) -> Result { - Ok(strs.into_iter().flatten().collect()) - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("variadic_variadic_type", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_variadic_arena() { - let attr = quote! { - ArrayFill, - sqlname = "array_fill", - introduces_nulls = false, - }; - let item = quote! { - fn array_fill<'a>( - &self, - fill: Datum<'a>, - dims: Datum<'a>, - lb: OptionalArg>, - temp_storage: &RowArena, - ) -> Result, EvalError> { - unimplemented!() - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("variadic_arena", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_variadic_modifiers() { - let attr = quote! { - Greatest, - sqlname = "greatest", - could_error = false, - is_monotone = true, - }; - let item = quote! { - fn greatest<'a>(datums: Variadic>) -> Datum<'a> { - datums.into_iter().max().unwrap_or(Datum::Null) - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("variadic_modifiers", output, &input); - } - - #[cfg_attr(miri, ignore)] - #[mz_ore::test] - fn insta_test_unary_generic_range() { - let attr = quote! { - sqlname = "rangelower", - is_monotone = true, - }; - let item = quote! { - fn range_lower<'a, T>(a: Range) -> Option { - a.inner.map(|inner| inner.lower.bound).flatten() - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("unary_generic_range", output, &input); - } - - #[cfg_attr(miri, ignore)] - #[mz_ore::test] - fn insta_test_binary_generic_list() { - let attr = quote! { - is_infix_op = true, - sqlname = "||", - propagates_nulls = false, - introduces_nulls = false, - }; - let item = quote! { - fn list_list_concat<'a, T>( - a: Option>, - b: Option>, - temp_storage: &'a RowArena, - ) -> Option> { - unimplemented!() - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("binary_generic_list", output, &input); - } - - #[cfg_attr(miri, ignore)] - #[mz_ore::test] - fn insta_test_binary_multi_generic() { - let attr = quote! { - sqlname = "multi_generic", - }; - let item = quote! { - fn multi_generic<'a, A, B>( - a: DatumList<'a, A>, - b: DatumList<'a, B>, - ) -> Option { - unimplemented!() - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("binary_multi_generic", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_unary_all_modifiers() { - let attr = quote! { - could_error = true, - output_type = i16, - inverse = UnaryAllModifiers, - is_monotone = true, - preserves_uniqueness = true, - is_eliminable_cast = false, - sqlname = "unary_all_modifiers", - }; - let item = quote! { - fn unary_all_modifiers<'a>(a: Datum<'a>) -> Datum<'a> { - a - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("unary_all_modifiers", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_binary_all_modifiers() { - let attr = quote! { - could_error = true, - output_type = i16, - is_infix_op = true, - is_monotone = (true, true), - is_infinity_monotone = false, - negate = BinaryAllModifiers, - propagates_nulls = true, - sqlname = "binary_all_modifiers", - }; - let item = quote! { - fn binary_all_modifiers<'a>(a: Datum<'a>, b: Datum<'a>) -> Datum<'a> { - a - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("binary_all_modifiers", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_variadic_all_modifiers() { - let attr = quote! { - could_error = true, - output_type = i16, - is_infix_op = true, - is_monotone = true, - is_associative = true, - propagates_nulls = true, - sqlname = "variadic_all_modifiers", - }; - let item = quote! { - fn variadic_all_modifiers<'a>(datums: Variadic>) -> Datum<'a> { - datums.into_iter().next().unwrap_or(Datum::Null) - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("variadic_all_modifiers", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn unknown_modifier_is_an_error() { - let (output, _input) = super::test_sqlfunc( - quote! { not_a_modifier = true }, - quote! { - fn some_unary<'a>(a: i32) -> i32 { a } - }, - ); - assert!( - output.contains("not_a_modifier"), - "expected an error naming the unknown modifier, got:\n{output}" - ); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn unary_rejects_negate_by_name() { - let (output, _input) = super::test_sqlfunc( - quote! { negate = super::Foo }, - quote! { - fn some_unary<'a>(a: i32) -> i32 { a } - }, - ); - assert!( - output.contains("`negate` is not supported for unary functions"), - "expected an error naming the modifier and the arity, got:\n{output}" - ); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn unary_rejects_is_infinity_monotone() { - let (output, _input) = super::test_sqlfunc( - quote! { is_infinity_monotone = false }, - quote! { - fn some_unary<'a>(a: i32) -> i32 { a } - }, - ); - assert!( - output.contains("`is_infinity_monotone` is not supported for unary functions"), - "is_infinity_monotone must be rejected on unary rather than ignored, got:\n{output}" - ); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_unary_self() { - let attr = quote! { PadTo, sqlname = "pad_to", test = true }; - let item = quote! { - fn pad_to<'a>(&self, a: &'a str, temp_storage: &'a RowArena) -> &'a str { - temp_storage.push_string(format!("{a:width$}", width = self.width)) - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("unary_self", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_binary_self() { - let attr = quote! { ClampAt, sqlname = "clamp_at", is_infix_op = false, test = true }; - let item = quote! { - fn clamp_at<'a>(&self, a: i64, b: i64) -> i64 { - a.clamp(self.lower, b) - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - insta::assert_snapshot!("binary_self", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn insta_test_skip_display() { - let attr = quote! { ExtractThing, skip_display = true, test = true }; - let item = quote! { - fn extract_thing<'a>(&self, a: i64) -> i64 { - a + self.offset - } - }; - let (output, input) = super::test_sqlfunc(attr, item); - assert!( - !output.contains("impl std::fmt::Display"), - "skip_display must suppress the Display impl, got:\n{output}" - ); - insta::assert_snapshot!("skip_display", output, &input); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn skip_display_rejects_sqlname() { - let (output, _input) = super::test_sqlfunc( - quote! { ExtractThing, skip_display = true, sqlname = "extract_thing" }, - quote! { - fn extract_thing<'a>(&self, a: i64) -> i64 { a + self.offset } - }, - ); - // A successful expansion also contains both modifier names, because the - // registry's `decl` field echoes the attribute text, so match the error itself. - assert!( - output.contains("sqlname has no effect with skip_display"), - "expected the conflict error, got:\n{output}" - ); - } - - #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri - #[mz_ore::test] - fn skip_display_rejects_a_unit_struct() { - let (output, _input) = super::test_sqlfunc( - quote! { skip_display = true }, - quote! { - fn extract_thing(a: i64) -> i64 { a } - }, - ); - assert!( - output.contains("skip_display requires a &self receiver"), - "expected the receiver error, got:\n{output}" - ); - } -} +mod test; diff --git a/src/expr-derive-impl/src/test.rs b/src/expr-derive-impl/src/test.rs new file mode 100644 index 0000000000000..19f83d23fd635 --- /dev/null +++ b/src/expr-derive-impl/src/test.rs @@ -0,0 +1,437 @@ +// Copyright Materialize, Inc. and contributors. All rights reserved. +// +// Use of this software is governed by the Business Source License +// included in the LICENSE file. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0. + +//! Snapshot and diagnostic tests for the `#[sqlfunc]` expansion. + +use quote::quote; + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_add_int16() { + let attr = quote! { + is_monotone = (true, true), + output_type = i16, + is_infix_op = true, + sqlname = "+", + propagates_nulls = true, + test = true, + }; + let item = quote! { + fn add_int16<'a>(a: Datum<'a>, b: Datum<'a>) -> Result, EvalError> { + a.unwrap_int16() + .checked_add(b.unwrap_int16()) + .ok_or(EvalError::NumericFieldOverflow) + .map(Datum::from) + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("add_int16", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_unary() { + let attr = quote! {test = true}; + let item = quote! { + fn unary_fn<'a>(a: Datum<'a>) -> bool { + unimplemented!() + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("unary_fn", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_unary_arena() { + let attr = quote! {test = true}; + let item = quote! { + fn unary_fn<'a>(a: Datum<'a>, temp_storage: &RowArena) -> bool { + unimplemented!() + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("unary_arena_fn", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_unary_arena_used() { + let attr = quote! { sqlname = "to_arena_string", test = true }; + let item = quote! { + fn to_arena_string<'a>(a: i32, temp_storage: &'a RowArena) -> &'a str { + temp_storage.push_string(a.to_string()) + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("unary_arena_used", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_unary_ref() { + let attr = quote! {test = true}; + let item = quote! { + fn unary_fn<'a>(a: &i16) -> bool { + unimplemented!() + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("unary_ref", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_complex_output_type() { + let attr = quote! { + is_monotone = (true, true), + output_type = "Option", + is_infix_op = true, + sqlname = "test", + propagates_nulls = true, + test = true, + }; + let item = quote! { + fn complex_output_type_fn<'a>( + a: Datum<'a>, + b: Datum<'a>, + ) -> Result, EvalError> { + unimplemented!() + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("complex_type", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_binary_arena() { + let attr = quote! {test = true}; + let item = quote! { + fn unary_fn<'a>(a: Datum<'a>, b: u16, temp_storage: &RowArena) -> bool { + unimplemented!() + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("binary_arena_fn", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_variadic_tuple() { + let attr = quote! { + Replace, + sqlname = "replace", + }; + let item = quote! { + fn replace(text: &str, from: &str, to: &str) -> Result { + Ok(text.replace(from, to)) + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("variadic_tuple", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_variadic_variadic_type() { + let attr = quote! { + Concat, + sqlname = "concat", + is_associative = true, + }; + let item = quote! { + fn concat(strs: Variadic>) -> Result { + Ok(strs.into_iter().flatten().collect()) + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("variadic_variadic_type", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_variadic_arena() { + let attr = quote! { + ArrayFill, + sqlname = "array_fill", + introduces_nulls = false, + }; + let item = quote! { + fn array_fill<'a>( + &self, + fill: Datum<'a>, + dims: Datum<'a>, + lb: OptionalArg>, + temp_storage: &RowArena, + ) -> Result, EvalError> { + unimplemented!() + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("variadic_arena", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_variadic_modifiers() { + let attr = quote! { + Greatest, + sqlname = "greatest", + could_error = false, + is_monotone = true, + }; + let item = quote! { + fn greatest<'a>(datums: Variadic>) -> Datum<'a> { + datums.into_iter().max().unwrap_or(Datum::Null) + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("variadic_modifiers", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_unary_generic_range() { + let attr = quote! { + sqlname = "rangelower", + is_monotone = true, + }; + let item = quote! { + fn range_lower<'a, T>(a: Range) -> Option { + a.inner.map(|inner| inner.lower.bound).flatten() + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("unary_generic_range", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_binary_generic_list() { + let attr = quote! { + is_infix_op = true, + sqlname = "||", + propagates_nulls = false, + introduces_nulls = false, + }; + let item = quote! { + fn list_list_concat<'a, T>( + a: Option>, + b: Option>, + temp_storage: &'a RowArena, + ) -> Option> { + unimplemented!() + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("binary_generic_list", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_binary_multi_generic() { + let attr = quote! { + sqlname = "multi_generic", + }; + let item = quote! { + fn multi_generic<'a, A, B>( + a: DatumList<'a, A>, + b: DatumList<'a, B>, + ) -> Option { + unimplemented!() + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("binary_multi_generic", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_unary_all_modifiers() { + let attr = quote! { + could_error = true, + output_type = i16, + inverse = UnaryAllModifiers, + is_monotone = true, + preserves_uniqueness = true, + is_eliminable_cast = false, + sqlname = "unary_all_modifiers", + }; + let item = quote! { + fn unary_all_modifiers<'a>(a: Datum<'a>) -> Datum<'a> { + a + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("unary_all_modifiers", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_binary_all_modifiers() { + let attr = quote! { + could_error = true, + output_type = i16, + is_infix_op = true, + is_monotone = (true, true), + is_infinity_monotone = false, + negate = BinaryAllModifiers, + propagates_nulls = true, + sqlname = "binary_all_modifiers", + }; + let item = quote! { + fn binary_all_modifiers<'a>(a: Datum<'a>, b: Datum<'a>) -> Datum<'a> { + a + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("binary_all_modifiers", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_variadic_all_modifiers() { + let attr = quote! { + could_error = true, + output_type = i16, + is_infix_op = true, + is_monotone = true, + is_associative = true, + propagates_nulls = true, + sqlname = "variadic_all_modifiers", + }; + let item = quote! { + fn variadic_all_modifiers<'a>(datums: Variadic>) -> Datum<'a> { + datums.into_iter().next().unwrap_or(Datum::Null) + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("variadic_all_modifiers", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn unknown_modifier_is_an_error() { + let (output, _input) = super::test_sqlfunc( + quote! { not_a_modifier = true }, + quote! { + fn some_unary<'a>(a: i32) -> i32 { a } + }, + ); + assert!( + output.contains("not_a_modifier"), + "expected an error naming the unknown modifier, got:\n{output}" + ); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn unary_rejects_negate_by_name() { + let (output, _input) = super::test_sqlfunc( + quote! { negate = super::Foo }, + quote! { + fn some_unary<'a>(a: i32) -> i32 { a } + }, + ); + assert!( + output.contains("`negate` is not supported for unary functions"), + "expected an error naming the modifier and the arity, got:\n{output}" + ); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn unary_rejects_is_infinity_monotone() { + let (output, _input) = super::test_sqlfunc( + quote! { is_infinity_monotone = false }, + quote! { + fn some_unary<'a>(a: i32) -> i32 { a } + }, + ); + assert!( + output.contains("`is_infinity_monotone` is not supported for unary functions"), + "is_infinity_monotone must be rejected on unary rather than ignored, got:\n{output}" + ); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_unary_self() { + let attr = quote! { PadTo, sqlname = "pad_to", test = true }; + let item = quote! { + fn pad_to<'a>(&self, a: &'a str, temp_storage: &'a RowArena) -> &'a str { + temp_storage.push_string(format!("{a:width$}", width = self.width)) + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("unary_self", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_binary_self() { + let attr = quote! { ClampAt, sqlname = "clamp_at", is_infix_op = false, test = true }; + let item = quote! { + fn clamp_at<'a>(&self, a: i64, b: i64) -> i64 { + a.clamp(self.lower, b) + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + insta::assert_snapshot!("binary_self", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_skip_display() { + let attr = quote! { ExtractThing, skip_display = true, test = true }; + let item = quote! { + fn extract_thing<'a>(&self, a: i64) -> i64 { + a + self.offset + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + assert!( + !output.contains("impl std::fmt::Display"), + "skip_display must suppress the Display impl, got:\n{output}" + ); + insta::assert_snapshot!("skip_display", output, &input); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn skip_display_rejects_sqlname() { + let (output, _input) = super::test_sqlfunc( + quote! { ExtractThing, skip_display = true, sqlname = "extract_thing" }, + quote! { + fn extract_thing<'a>(&self, a: i64) -> i64 { a + self.offset } + }, + ); + // A successful expansion also contains both modifier names, because the + // registry's `decl` field echoes the attribute text, so match the error itself. + assert!( + output.contains("sqlname has no effect with skip_display"), + "expected the conflict error, got:\n{output}" + ); +} + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn skip_display_rejects_a_unit_struct() { + let (output, _input) = super::test_sqlfunc( + quote! { skip_display = true }, + quote! { + fn extract_thing(a: i64) -> i64 { a } + }, + ); + assert!( + output.contains("skip_display requires a &self receiver"), + "expected the receiver error, got:\n{output}" + ); +} From a11bbd6a67bbca331abccc8fa9e0657063b72e1a Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Wed, 14 Oct 2026 16:35:47 +0200 Subject: [PATCH 14/19] doc: Correct the sqlfunc reference against the macro The reference said the generated trait implementations live in the trait modules, that `output.nullable` comes from `introduces_nulls`, and that `output_type` generates an `output_type` method. It had no entry for `is_eliminable_cast`, two examples repeated the function name as `sqlname`, and several lines used em-dashes and arrows as sentence structure. The `output_type_expr` entry also names the bindings the expression can read. Co-Authored-By: Claude Opus 5.5 (1M context) --- doc/developer/sqlfunc.md | 57 ++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/doc/developer/sqlfunc.md b/doc/developer/sqlfunc.md index 3fc6e79d175db..b4df2558e6c2a 100644 --- a/doc/developer/sqlfunc.md +++ b/doc/developer/sqlfunc.md @@ -4,8 +4,8 @@ The `#[sqlfunc]` attribute macro generates boilerplate for SQL scalar functions. From a plain Rust function it creates a trait implementation (`EagerUnaryFunc`, `EagerBinaryFunc`, or `EagerVariadicFunc`), a `FuncName` implementation, and, unless the function takes a `&self` receiver, the struct itself. It also creates a `Display` implementation unless `skip_display` suppresses it. -The macro lives in `src/expr-derive-impl/src/sqlfunc.rs`. -The generated trait implementations live in `src/expr/src/scalar/func/{unary,binary,variadic}.rs`. +The macro lives in `src/expr-derive-impl/`, with its entry point in `sqlfunc.rs`. +The traits it implements are defined in `src/expr/src/scalar/func/{unary,binary,variadic}.rs`. ## Quick start @@ -59,17 +59,16 @@ The SQL-visible name of the function. * **Default:** the Rust function name (via `stringify!`) * **Applies to:** all arities -The generated `Display` impl is the only reader, so `skip_display = true` rejects -`sqlname`. +The generated `Display` impl is the only reader, so `skip_display = true` rejects `sqlname`. ### `propagates_nulls` Whether a NULL input produces a NULL output, skipping the function body entirely. When true, the evaluation layer short-circuits on NULL inputs and returns NULL without calling the function. -This also affects `output_type`: if `propagates_nulls` is true and any input column is nullable, the output column is marked nullable. +This also affects the output column type: if `propagates_nulls` is true and any input column is nullable, the output column is marked nullable. * **Type:** boolean expression -* **Default:** inferred from input type — `!Input::nullable()`. +* **Default:** `!Input::nullable()`, inferred from the input type. If the input type accepts `Option` or `Datum` (which can represent NULL), it is nullable, so `propagates_nulls` defaults to `false`. If the input type is a non-nullable type like `&str` or `i32`, nulls are propagated (the function never sees them). * **Applies to:** binary, variadic @@ -81,7 +80,7 @@ Whether the function can produce NULL from non-NULL inputs. The optimizer uses this to reason about column nullability. * **Type:** boolean expression -* **Default:** inferred from output type — `Output::nullable()`. +* **Default:** `Output::nullable()`, inferred from the output type. A function returning `Option` or `Datum` introduces nulls. A function returning `String` or `i32` does not. * **Applies to:** all arities @@ -93,7 +92,7 @@ Whether the function can produce an error at runtime. The optimizer uses this to avoid certain rewrites that might change error behavior. * **Type:** boolean expression -* **Default:** inferred from output type — `Output::fallible()`. +* **Default:** `Output::fallible()`, inferred from the output type. A function returning `Result` is fallible. A function returning `T` directly is not. * **Applies to:** all arities @@ -132,6 +131,15 @@ The inverse function, if it exists. * **Default:** `None` * **Applies to:** unary only +### `is_eliminable_cast` + +Whether the function is a cast that does no work and only changes the tracked type. +Lowering drops such casts when `enable_cast_elimination` is on. + +* **Type:** boolean expression +* **Default:** `false` +* **Applies to:** unary only + ### `negate` The logical negation of a comparison function. @@ -161,7 +169,7 @@ Whether the function is associative: `f(a, f(b, c)) = f(f(a, b), c)`. ### `output_type` An explicit type path for computing the output column type. -When set, the macro generates `output_type` and `introduces_nulls` based on this type instead of inferring from the return type. +When set, the output column type and the `introduces_nulls` default come from this type instead of the return type. * **Type:** type path (e.g., `i16`, `String`) * **Default:** inferred from return type via `Self::Output::as_column_type()` @@ -170,8 +178,10 @@ When set, the macro generates `output_type` and `introduces_nulls` based on this ### `output_type_expr` -An expression that computes the output column type at runtime. +An expression that computes the output column type when the expression is typed. +It can read `self` and the input column types, bound as `input_type` for unary functions and as `input_types` for binary and variadic ones. Use this for functions whose output type depends on input types or struct fields (e.g., `ArrayCreate` where the element type is stored on the struct). +The macro applies the nullability rules in [Null handling](#null-handling) on top of the expression's result. * **Type:** expression evaluating to `SqlColumnType` * **Default:** none @@ -191,21 +201,17 @@ Update them with `cargo insta accept` after running `cargo test -p mz-expr-deriv ### `skip_display` -Suppresses the generated `Display` impl. Use this when the displayed name depends on the -struct's own state rather than being a fixed string, so the call site keeps a -hand-written `Display` impl. `RangeCreate` is an example: it picks between -`int4range`, `int8range`, `daterange`, `numrange`, `tsrange`, and `tstzrange` based on -its `elem_type` field. Setting it to `true` rejects `sqlname`, whose only reader is the -suppressed impl, and requires a `&self` receiver, since a unit struct has no state to -display. +Suppresses the generated `Display` impl. +Use this when the displayed name depends on the struct's own state, so the call site keeps a hand-written `Display` impl. +`RangeCreate` is an example: it picks between `int4range`, `int8range`, `daterange`, `numrange`, `tsrange`, and `tstzrange` based on its `elem_type` field. +Setting it to `true` rejects `sqlname`, whose only reader is the suppressed impl, and requires a `&self` receiver, since a unit struct has no state to display. * **Type:** `bool` * **Default:** `false` * **Applies to:** all arities -Which modifiers apply to which arity is declared in -`src/expr-derive-impl/src/shape.rs`, one table per arity. A modifier absent from an -arity's table is rejected with an error naming both the modifier and the arity. +Which modifiers apply to which arity is declared in `src/expr-derive-impl/src/shape.rs`, one table per arity. +A modifier absent from an arity's table is rejected with an error naming both the modifier and the arity. ## Generated items @@ -234,7 +240,7 @@ fn array_fill<'a>( Without `&self`, the macro defines the struct under that name: ```rust -#[sqlfunc(Replace, sqlname = "replace")] +#[sqlfunc(Replace)] fn replace(text: &str, from: &str, to: &str) -> Result { // ... } @@ -295,7 +301,7 @@ For variadic functions, the generated `Input` type depends on parameter count: The interplay between `propagates_nulls`, `introduces_nulls`, and input/output types determines the nullability of the output column. -The `output_type` method on each generated struct computes the output `SqlColumnType` as: +The generated output-type method (`output_sql_type` for unary and binary functions, `output_type` for variadic ones) computes the output `SqlColumnType` as: ``` output.nullable = output.nullable @@ -304,7 +310,7 @@ output.nullable = output.nullable ``` Where: -* `output.nullable` comes from `introduces_nulls` (or is inferred from the output type). +* `output.nullable` is the nullability of the column type that `output_type`, `output_type_expr`, or the return type gives. * `non_nullable_input_is_nullable` is true if a nullable input column lands in a parameter position whose Rust type cannot represent NULL. Such a position makes the evaluation layer short-circuit to NULL, so the output is nullable no matter what `propagates_nulls` says. Unary functions have no such term, because a unary function's single position is the one `propagates_nulls` already describes. * `propagates_nulls` indicates whether NULL passes through. * `any_input_nullable` is true if any input column is nullable. @@ -351,7 +357,7 @@ fn add_int16<'a>(a: Datum<'a>, b: Datum<'a>) -> Result, EvalError> { ```rust #[sqlfunc(sqlname = "datediff")] fn date_diff_date(unit_str: &str, a: Date, b: Date) -> Result { - // Three fixed parameters → variadic with tuple input (&str, Date, Date) + // Three fixed parameters make it variadic, with tuple input (&str, Date, Date). } ``` @@ -360,7 +366,7 @@ fn date_diff_date(unit_str: &str, a: Date, b: Date) -> Result { ```rust #[sqlfunc(is_associative = true)] fn concat(strs: Variadic>) -> Result { - // Variadic detected → variadic dispatch regardless of parameter count + // A Variadic parameter makes it variadic regardless of parameter count. } ``` @@ -369,7 +375,6 @@ fn concat(strs: Variadic>) -> Result { ```rust #[sqlfunc( ArrayCreate, - sqlname = "array_create", output_type_expr = "SqlScalarType::Array(Box::new(self.elem_type.clone())).nullable(false)" )] fn array_create<'a>(&self, datums: Variadic>, temp_storage: &'a RowArena) -> Array<'a> { From 6154ee737055ee4695f4c498b920c791b011a745 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Wed, 14 Oct 2026 16:41:25 +0200 Subject: [PATCH 15/19] expr-derive-impl: Take every arity's parameters from sql_args Unary and binary read their parameter types with `arg_type`, which indexed past the receiver by hand and added `'a` only to a top-level reference, so a unary `Option<&str>` parameter produced an `Input` type without a lifetime. Variadic already used `sql_args` with `patch_lifetimes`. `sql_params` now does that for every arity and keeps the positional `a` and `b` names for unary and binary. `generate` derives the receiver itself instead of taking it as a flag, `is_variadic_arg` moves next to the other type checks in `signature.rs`, and the arena parameter of `call` is written inline, since every arity has it. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/expr-derive-impl/src/generate.rs | 72 ++++++++----------- src/expr-derive-impl/src/signature.rs | 43 ++++------- ...r_derive_impl__test__unary_nested_ref.snap | 67 +++++++++++++++++ src/expr-derive-impl/src/sqlfunc.rs | 20 +----- src/expr-derive-impl/src/test.rs | 17 +++++ 5 files changed, 129 insertions(+), 90 deletions(-) create mode 100644 src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_nested_ref.snap diff --git a/src/expr-derive-impl/src/generate.rs b/src/expr-derive-impl/src/generate.rs index 2a4e2e11c0bbf..88cb448694919 100644 --- a/src/expr-derive-impl/src/generate.rs +++ b/src/expr-derive-impl/src/generate.rs @@ -40,7 +40,7 @@ fn override_methods( .iter() .find(|(candidate, _)| candidate == modifier) .map(|(_, expr)| *expr)?; - let name = Ident::new(modifier.name(), proc_macro2::Span::call_site()); + let name = Ident::new(modifier.name(), Span::call_site()); let body = ret.body(expr); let ret = ret.to_tokens(); Some(quote! { @@ -127,25 +127,32 @@ fn expand(e: &Codegen, func: &syn::ItemFn, trait_impl: TokenStream) -> TokenStre } } -/// A variadic function's parameter names and their pre-erasure types. -fn variadic_params(func: &syn::ItemFn) -> darling::Result<(Vec, Vec)> { +/// The SQL arguments' pre-erasure types, and the names `call` binds them to. +/// +/// Unary and binary bind their inputs positionally as `a` and `b`, ignoring how the +/// function spells its parameters, while variadic forwards the real names. +fn sql_params(shape: Shape, func: &syn::ItemFn) -> darling::Result<(Vec, Vec)> { + // `crate::sqlfunc::determine_shape` gives unary one SQL argument and binary two. + const POSITIONAL: [&str; 2] = ["a", "b"]; let mut types = Vec::new(); let mut names = Vec::new(); - for param in signature::sql_args(func) { - match param { - syn::FnArg::Typed(pat) => { + for (position, param) in signature::sql_args(func).into_iter().enumerate() { + let syn::FnArg::Typed(pat) = param else { + return Err(darling::Error::custom("unexpected self parameter").with_span(param)); + }; + let name = match shape { + Shape::Unary | Shape::Binary => Ident::new(POSITIONAL[position], Span::call_site()), + Shape::Variadic => { let syn::Pat::Ident(ident) = &*pat.pat else { return Err( darling::Error::custom("unsupported parameter pattern").with_span(&pat.pat) ); }; - names.push(ident.ident.clone()); - types.push(signature::patch_lifetimes(&pat.ty)); - } - syn::FnArg::Receiver(_) => { - return Err(darling::Error::custom("unexpected self parameter")); + ident.ident.clone() } - } + }; + types.push(signature::patch_lifetimes(&pat.ty)); + names.push(name); } Ok((types, names)) } @@ -215,15 +222,12 @@ fn output_type_and_introduces_nulls( /// Generates the trait impl for one `#[sqlfunc]`-annotated function, together with the /// items [`expand`] wraps around it. /// -/// `struct_ty` names the generated struct when the call site spells one. `has_self` -/// says the annotated function takes a receiver, so the trait's `call` dispatches -/// through it. +/// `struct_ty` names the generated struct when the call site spells one. pub(crate) fn generate( shape: Shape, func: &syn::ItemFn, mods: Modifiers, struct_ty: Option, - has_self: bool, attr: &TokenStream, ) -> darling::Result { reject_inapplicable(shape, &mods)?; @@ -234,33 +238,16 @@ pub(crate) fn generate( .and_then(|ty| ty.segments.last()) .map_or_else(|| signature::camel_case(fn_name), |seg| seg.ident.clone()); - // Whether the annotated function itself wants the arena, which is independent of - // whether the trait's `call` receives one. + // A receiver makes `call` dispatch through `self` to a method on a struct defined + // at the call site. + let has_self = signature::has_receiver(func); + // Whether the annotated function takes the arena. The trait's `call` always receives + // one. let arena = signature::last_is_arena(func); let output_ty_raw = signature::output_type(func)?; let generic_params = signature::find_generic_type_params(func); - // Unary and binary bind their inputs positionally, ignoring how the function - // spells its parameters, while variadic forwards the real names. A `&self` - // receiver occupies position 0, so it offsets where the bound arguments start. - let self_offset = usize::from(has_self); - let (param_types_raw, param_names) = match shape { - Shape::Unary => ( - vec![signature::arg_type(func, self_offset)?], - vec![Ident::new("a", Span::call_site())], - ), - Shape::Binary => ( - vec![ - signature::arg_type(func, self_offset)?, - signature::arg_type(func, self_offset + 1)?, - ], - vec![ - Ident::new("a", Span::call_site()), - Ident::new("b", Span::call_site()), - ], - ), - Shape::Variadic => variadic_params(func)?, - }; + let (param_types_raw, param_names) = sql_params(shape, func)?; if mods.skip_display() && mods.sqlname().is_some() { return Err(darling::Error::custom( @@ -304,9 +291,6 @@ pub(crate) fn generate( )?; let methods = override_methods(shape, &mods, introduces_nulls_fn); - // Every arity's `call` receives the arena; only `arena` (whether the annotated - // function itself wants it) varies. - let arena_param = quote! { , temp_storage: &'a mz_repr::RowArena }; let arena_arg = if arena { quote! { , temp_storage } } else { @@ -329,8 +313,8 @@ pub(crate) fn generate( fn call<'a>( &self, - #destructure: Self::Input<'a> - #arena_param + #destructure: Self::Input<'a>, + temp_storage: &'a mz_repr::RowArena, ) -> Self::Output<'a> { #receiver #fn_name(#(#param_names),* #arena_arg) } diff --git a/src/expr-derive-impl/src/signature.rs b/src/expr-derive-impl/src/signature.rs index 79c0890d294de..aa6357afd76e6 100644 --- a/src/expr-derive-impl/src/signature.rs +++ b/src/expr-derive-impl/src/signature.rs @@ -76,6 +76,21 @@ fn is_nullable_type(ty: &syn::Type) -> bool { false } +/// Checks whether a parameter's type is `Variadic<...>` or `OptionalArg<...>`, which +/// makes the function variadic regardless of parameter count. +pub(crate) fn is_variadic_arg(arg: &syn::FnArg) -> bool { + let syn::FnArg::Typed(pat) = arg else { + return false; + }; + let syn::Type::Path(path) = &*pat.ty else { + return false; + }; + path.path + .segments + .last() + .is_some_and(|segment| segment.ident == "Variadic" || segment.ident == "OptionalArg") +} + /// Checks if a type is `Variadic`. fn is_variadic_type(ty: &syn::Type) -> bool { if let syn::Type::Path(type_path) = ty { @@ -587,34 +602,6 @@ pub(crate) fn erase_all_generic_params(ty: &syn::Type, generic_names: &[Ident]) ty } -/// Determines the argument type of the nth argument of the function. -/// -/// Adds a lifetime `'a` to the argument type if it is a reference type. -/// -/// Panics if the function has fewer than `nth` arguments. Returns an error if -/// the parameter is a `self` receiver. -pub(crate) fn arg_type(arg: &syn::ItemFn, nth: usize) -> Result { - match &arg.sig.inputs[nth] { - syn::FnArg::Typed(pat) => { - // Patch lifetimes to be 'a if reference - if let syn::Type::Reference(r) = &*pat.ty { - if r.lifetime.is_none() { - let ty = syn::Type::Reference(syn::TypeReference { - lifetime: Some(Lifetime::new("'a", r.span())), - ..r.clone() - }); - return Ok(ty); - } - } - Ok((*pat.ty).clone()) - } - syn::FnArg::Receiver(_) => Err(syn::Error::new( - arg.sig.inputs[nth].span(), - "unsupported argument type", - )), - } -} - /// Recursively patches lifetimes in a type, adding `'a` to references without a lifetime /// and recursing into generic arguments and tuples. pub(crate) fn patch_lifetimes(ty: &syn::Type) -> syn::Type { diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_nested_ref.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_nested_ref.snap new file mode 100644 index 0000000000000..f322accd0d34a --- /dev/null +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_nested_ref.snap @@ -0,0 +1,67 @@ +--- +source: src/expr-derive-impl/src/test.rs +expression: "#[sqlfunc(test = true)]\nfn unary_fn<'a>(a: Option<&str>) -> bool {\n unimplemented!()\n}\n" +--- +#[derive( + Ord, + PartialOrd, + Clone, + Debug, + Eq, + PartialEq, + serde::Serialize, + serde::Deserialize, + Hash, +)] +#[cfg_attr(any(test, feature = "proptest"), derive(proptest_derive::Arbitrary))] +pub struct UnaryFn; +impl crate::func::EagerUnaryFunc for UnaryFn { + type Input<'a> = Option<&'a str>; + type Output<'a> = bool; + fn call<'a>( + &self, + a: Self::Input<'a>, + temp_storage: &'a mz_repr::RowArena, + ) -> Self::Output<'a> { + unary_fn(a) + } + fn output_sql_type( + &self, + input_type: mz_repr::SqlColumnType, + ) -> mz_repr::SqlColumnType { + use mz_repr::AsColumnType; + let output = Self::Output::as_column_type(); + let propagates_nulls = crate::func::EagerUnaryFunc::propagates_nulls(self); + let nullable = output.nullable; + output.nullable(nullable || (propagates_nulls && input_type.nullable)) + } +} +impl std::fmt::Display for UnaryFn { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str(stringify!(unary_fn)) + } +} +impl crate::func::FuncName for UnaryFn { + const NAME: &'static str = stringify!(unary_fn); + #[cfg(feature = "func-registry")] + const SQLFUNC: Option = Some(crate::func::SqlFuncSource { + decl: "#[sqlfunc(test = true)] fn unary_fn<'a>(a: Option<&str>) -> bool", + signature: "fn(Option<&'a str>) -> bool", + body_fingerprint: 4726381389472009124u64, + }); + #[cfg(feature = "func-registry")] + fn sqlfunc_input_types() -> Option> { + use crate::func::registry::{ProbeColumnType as _, ProbeColumnTypeFallback as _}; + [ + (&crate::func::registry::ColumnTypeProbe::< + Option<&'static str>, + >(::std::marker::PhantomData)) + .column_type(), + ] + .into_iter() + .collect() + } +} +fn unary_fn<'a>(a: Option<&str>) -> bool { + unimplemented!() +} diff --git a/src/expr-derive-impl/src/sqlfunc.rs b/src/expr-derive-impl/src/sqlfunc.rs index 0cdd82029cdec..2ff7c56c14b57 100644 --- a/src/expr-derive-impl/src/sqlfunc.rs +++ b/src/expr-derive-impl/src/sqlfunc.rs @@ -47,13 +47,12 @@ pub fn sqlfunc( let generate_tests = modifiers.generates_test(); let func = syn::parse2::(item.clone())?; - let has_self = signature::has_receiver(&func); let Some(shape) = determine_shape(&func) else { return Err(darling::Error::custom( "nullary functions are not supported", )); }; - let tokens = generate(shape, &func, modifiers, struct_ty, has_self, &attr)?; + let tokens = generate(shape, &func, modifiers, struct_ty, &attr)?; let test = (generate_tests && include_test).then(|| generate_test(attr, item, &func.sig.ident)); @@ -86,21 +85,6 @@ fn generate_test(_attr: TokenStream, _item: TokenStream, _name: &Ident) -> Token quote! {} } -/// Checks whether a parameter's type is `Variadic<...>` or `OptionalArg<...>`, -/// which indicates the function should be treated as variadic regardless of -/// parameter count. -fn is_variadic_arg(arg: &syn::FnArg) -> bool { - if let syn::FnArg::Typed(pat) = arg { - if let syn::Type::Path(path) = &*pat.ty { - if let Some(segment) = path.path.segments.last() { - let ident = segment.ident.to_string(); - return ident == "Variadic" || ident == "OptionalArg"; - } - } - } - false -} - /// The shape of a function annotated with `#[sqlfunc]`, or `None` for a function /// without SQL arguments. /// @@ -109,7 +93,7 @@ fn is_variadic_arg(arg: &syn::FnArg) -> bool { /// regardless of how many arguments it has. fn determine_shape(func: &syn::ItemFn) -> Option { let args = signature::sql_args(func); - if args.iter().any(|arg| is_variadic_arg(arg)) { + if args.iter().any(|arg| signature::is_variadic_arg(arg)) { return Some(Shape::Variadic); } match args.len() { diff --git a/src/expr-derive-impl/src/test.rs b/src/expr-derive-impl/src/test.rs index 19f83d23fd635..c44e7358c219c 100644 --- a/src/expr-derive-impl/src/test.rs +++ b/src/expr-derive-impl/src/test.rs @@ -86,6 +86,23 @@ fn insta_test_unary_ref() { insta::assert_snapshot!("unary_ref", output, &input); } +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn insta_test_unary_nested_ref() { + let attr = quote! {test = true}; + let item = quote! { + fn unary_fn<'a>(a: Option<&str>) -> bool { + unimplemented!() + } + }; + let (output, input) = super::test_sqlfunc(attr, item); + assert!( + output.contains("type Input<'a> = Option<&'a str>;"), + "a reference nested in a unary parameter must get the trait's lifetime, got:\n{output}" + ); + insta::assert_snapshot!("unary_nested_ref", output, &input); +} + #[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri #[mz_ore::test] fn insta_test_complex_output_type() { From bc12401fb4e29265ccf579f737773503b92302d4 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Wed, 14 Oct 2026 16:42:51 +0200 Subject: [PATCH 16/19] expr-derive-impl: Validate modifiers in one place, at their spans Modifier checks were split between `reject_inapplicable`, two `skip_display` checks in `generate`, and the `output_type` conflict in the output-type resolution. Each stopped at the first problem and reported it at the whole attribute. `Modifiers::validate` runs them all before generation, reports every error, and attaches each to the modifier it concerns. `Modifiers::get` returns one method-producing modifier, so `override_methods` walks the arity table directly instead of searching a list in another order, and `Modifiers::iter` goes away. `ReturnTy::to_tokens` is renamed to `tokens`, since it does not implement `quote::ToTokens`. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/expr-derive-impl/src/generate.rs | 36 ++------ src/expr-derive-impl/src/modifiers.rs | 121 +++++++++++++------------- src/expr-derive-impl/src/shape.rs | 51 +++++++---- src/expr-derive-impl/src/test.rs | 16 ++++ 4 files changed, 117 insertions(+), 107 deletions(-) diff --git a/src/expr-derive-impl/src/generate.rs b/src/expr-derive-impl/src/generate.rs index 88cb448694919..cd51c76d523f5 100644 --- a/src/expr-derive-impl/src/generate.rs +++ b/src/expr-derive-impl/src/generate.rs @@ -11,7 +11,7 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::quote; -use crate::modifiers::{Modifiers, reject_inapplicable}; +use crate::modifiers::Modifiers; use crate::shape::{Modifier, Shape}; use crate::signature; use crate::source::sqlfunc_source; @@ -28,7 +28,6 @@ fn override_methods( mods: &Modifiers, mut introduces_nulls_fn: Option, ) -> Vec { - let present: Vec<_> = mods.iter().collect(); shape .modifiers() .iter() @@ -36,13 +35,10 @@ fn override_methods( if *modifier == Modifier::IntroducesNulls { return introduces_nulls_fn.take(); } - let expr = present - .iter() - .find(|(candidate, _)| candidate == modifier) - .map(|(_, expr)| *expr)?; + let expr = mods.get(*modifier)?; let name = Ident::new(modifier.name(), Span::call_site()); let body = ret.body(expr); - let ret = ret.to_tokens(); + let ret = ret.tokens(); Some(quote! { fn #name(&self) -> #ret { #body @@ -172,13 +168,9 @@ fn output_type_and_introduces_nulls( ) -> darling::Result<(TokenStream, Option)> { let output_type = mods.output_type(); let mut output_type_expr = mods.output_type_expr().map(|expr| quote! { #expr }); - let mut introduces_nulls = mods.introduces_nulls().map(|expr| quote! { #expr }); - - if output_type.is_some() && output_type_expr.is_some() { - return Err(darling::Error::custom( - "output_type and output_type_expr cannot be used together", - )); - } + let mut introduces_nulls = mods + .get(Modifier::IntroducesNulls) + .map(|expr| quote! { #expr }); // Derive the output type from where the generic parameters land. This reads the // pre-erasure types, because erasure is what removes those parameters. @@ -230,8 +222,6 @@ pub(crate) fn generate( struct_ty: Option, attr: &TokenStream, ) -> darling::Result { - reject_inapplicable(shape, &mods)?; - let fn_name = &func.sig.ident; let struct_name = struct_ty .as_ref() @@ -241,6 +231,7 @@ pub(crate) fn generate( // A receiver makes `call` dispatch through `self` to a method on a struct defined // at the call site. let has_self = signature::has_receiver(func); + mods.validate(shape, has_self)?; // Whether the annotated function takes the arena. The trait's `call` always receives // one. let arena = signature::last_is_arena(func); @@ -249,19 +240,6 @@ pub(crate) fn generate( let (param_types_raw, param_names) = sql_params(shape, func)?; - if mods.skip_display() && mods.sqlname().is_some() { - return Err(darling::Error::custom( - "sqlname has no effect with skip_display, which suppresses the only impl that reads it", - )); - } - // A hand-written `Display` is only needed when the name depends on struct state, - // and without `&self` the macro defines a unit struct that has none. - if mods.skip_display() && !has_self { - return Err(darling::Error::custom( - "skip_display requires a &self receiver, since a unit struct has no state for a hand-written Display to read", - )); - } - // Replace generic type params with `Datum<'a>` for use in the trait impl's associated // types, where the function's parameters are not in scope. let param_types: Vec = param_types_raw diff --git a/src/expr-derive-impl/src/modifiers.rs b/src/expr-derive-impl/src/modifiers.rs index 4a1f8907a8414..7c99d7df6c23b 100644 --- a/src/expr-derive-impl/src/modifiers.rs +++ b/src/expr-derive-impl/src/modifiers.rs @@ -8,10 +8,10 @@ // by the Apache License, Version 2.0. //! The `#[sqlfunc]` attribute's key-value modifiers. //! -//! [`Modifiers`] is the parsed attribute and [`reject_inapplicable`] gates it against -//! the per-arity tables in [`crate::shape`]. Modifiers that feed `Display` or the -//! output-type body have their own accessors, and the ones that map onto a trait method -//! are reached through [`Modifiers::iter`]. +//! [`Modifiers`] is the parsed attribute. [`Modifiers::validate`] checks it before +//! anything is generated. Modifiers that feed `Display` or the output-type body have +//! their own accessors, and the ones that map onto a trait method are reached through +//! [`Modifiers::get`]. use proc_macro2::TokenStream; use quote::quote; @@ -20,9 +20,6 @@ use syn::{Expr, Lit}; use crate::shape::{Modifier, Shape}; /// Modifiers passed as key-value pairs to the `#[sqlfunc]` macro. -/// -/// Which arities accept which modifier is declared by the tables in [`crate::shape`], -/// not here. [`reject_inapplicable`] enforces it. #[derive(Debug, Default, darling::FromMeta)] pub(crate) struct Modifiers { /// Whether the function is monotone with respect to its arguments. Binary functions @@ -86,67 +83,73 @@ impl Modifiers { self.output_type_expr.as_ref() } - /// The call site's `introduces_nulls`, which `crate::generate::generate` combines - /// with the output type instead of emitting it on its own. - pub(crate) fn introduces_nulls(&self) -> Option<&Expr> { - self.introduces_nulls.as_ref() - } - /// Whether the call site asked to suppress the generated `Display` impl. pub(crate) fn skip_display(&self) -> bool { self.skip_display.unwrap_or(false) } - /// The method-producing modifiers that are present, in this method's own fixed - /// order, which does not match any of the three per-arity tables in - /// `crate::shape`. A caller that needs table order joins against - /// `Shape::modifiers()` by `Modifier` instead. - /// - /// Modifiers that do not produce a trait method are excluded, because - /// `crate::generate::generate` reads those through their accessors. - /// `introduces_nulls` is included so [`reject_inapplicable`] checks it, although - /// `crate::generate::generate` builds its method from the output type. - pub(crate) fn iter(&self) -> impl Iterator + '_ { - [ - (Modifier::CouldError, self.could_error.as_ref()), - (Modifier::IntroducesNulls, self.introduces_nulls.as_ref()), - (Modifier::IsMonotone, self.is_monotone.as_ref()), - (Modifier::IsInfixOp, self.is_infix_op.as_ref()), - (Modifier::PropagatesNulls, self.propagates_nulls.as_ref()), - (Modifier::Inverse, self.inverse.as_ref()), - ( - Modifier::PreservesUniqueness, - self.preserves_uniqueness.as_ref(), - ), - (Modifier::IsEliminableCast, self.is_eliminable_cast.as_ref()), - (Modifier::Negate, self.negate.as_ref()), - ( - Modifier::IsInfinityMonotone, - self.is_infinity_monotone.as_ref(), - ), - (Modifier::IsAssociative, self.is_associative.as_ref()), - ] - .into_iter() - .filter_map(|(modifier, expr)| expr.map(|expr| (modifier, expr))) + /// The expression the call site gave a method-producing modifier, if any. + pub(crate) fn get(&self, modifier: Modifier) -> Option<&Expr> { + match modifier { + Modifier::CouldError => self.could_error.as_ref(), + Modifier::IntroducesNulls => self.introduces_nulls.as_ref(), + Modifier::IsMonotone => self.is_monotone.as_ref(), + Modifier::IsInfixOp => self.is_infix_op.as_ref(), + Modifier::PropagatesNulls => self.propagates_nulls.as_ref(), + Modifier::Inverse => self.inverse.as_ref(), + Modifier::PreservesUniqueness => self.preserves_uniqueness.as_ref(), + Modifier::IsEliminableCast => self.is_eliminable_cast.as_ref(), + Modifier::Negate => self.negate.as_ref(), + Modifier::IsInfinityMonotone => self.is_infinity_monotone.as_ref(), + Modifier::IsAssociative => self.is_associative.as_ref(), + } } -} -/// Errors if `mods` carries a method-producing modifier `shape` does not accept. -pub(crate) fn reject_inapplicable(shape: Shape, mods: &Modifiers) -> darling::Result<()> { - for (modifier, _) in mods.iter() { - let accepted = shape - .modifiers() - .iter() - .any(|(candidate, _)| *candidate == modifier); - if !accepted { - return Err(darling::Error::custom(format!( - "`{}` is not supported for {} functions", - modifier.name(), - shape.label(), - ))); + /// Errors if a modifier does not apply to `shape`, or conflicts with another + /// modifier or with the function's receiver. Reports every problem, each at the + /// modifier it concerns. + pub(crate) fn validate(&self, shape: Shape, has_self: bool) -> darling::Result<()> { + let mut errors = darling::Error::accumulator(); + for modifier in Modifier::ALL { + let Some(expr) = self.get(modifier) else { + continue; + }; + if !shape.accepts(modifier) { + errors.push( + darling::Error::custom(format!( + "`{}` is not supported for {} functions", + modifier.name(), + shape.label(), + )) + .with_span(expr), + ); + } + } + if let (Some(_), Some(expr)) = (&self.output_type, &self.output_type_expr) { + errors.push( + darling::Error::custom("output_type and output_type_expr cannot be used together") + .with_span(expr), + ); + } + if self.skip_display() { + if let Some(sqlname) = &self.sqlname { + errors.push( + darling::Error::custom( + "sqlname has no effect with skip_display, which suppresses the only impl that reads it", + ) + .with_span(sqlname), + ); + } + // A hand-written `Display` is only needed when the name depends on struct + // state, and without `&self` the macro defines a unit struct that has none. + if !has_self { + errors.push(darling::Error::custom( + "skip_display requires a &self receiver, since a unit struct has no state for a hand-written Display to read", + )); + } } + errors.finish() } - Ok(()) } /// A name for the SQL function. It can be either a literal or a macro, thus we diff --git a/src/expr-derive-impl/src/shape.rs b/src/expr-derive-impl/src/shape.rs index 19f93c24267e7..c0452c8f1220b 100644 --- a/src/expr-derive-impl/src/shape.rs +++ b/src/expr-derive-impl/src/shape.rs @@ -45,6 +45,21 @@ pub(crate) enum Modifier { } impl Modifier { + /// Every method-producing modifier. + pub(crate) const ALL: [Modifier; 11] = [ + Modifier::CouldError, + Modifier::IntroducesNulls, + Modifier::IsMonotone, + Modifier::IsInfixOp, + Modifier::PropagatesNulls, + Modifier::Inverse, + Modifier::PreservesUniqueness, + Modifier::IsEliminableCast, + Modifier::Negate, + Modifier::IsInfinityMonotone, + Modifier::IsAssociative, + ]; + /// The attribute key, which is also the generated method name. pub(crate) fn name(&self) -> &'static str { match self { @@ -73,7 +88,7 @@ pub(crate) enum ReturnTy { } impl ReturnTy { - pub(crate) fn to_tokens(&self) -> TokenStream { + pub(crate) fn tokens(&self) -> TokenStream { match self { ReturnTy::Bool => quote! { bool }, ReturnTy::BoolPair => quote! { (bool, bool) }, @@ -93,10 +108,8 @@ impl ReturnTy { } } -// `crate::generate::override_methods` walks these tables in order to generate each -// arity's override methods. The order here therefore determines the method order -// in the generated code, which the insta snapshots under `sqlfunc.rs` pin exactly, -// so it is load-bearing, not cosmetic. +// `crate::generate::override_methods` walks these tables in order, so they also fix +// the method order in the generated code, which the snapshots in `crate::test` pin. const UNARY_MODIFIERS: &[(Modifier, ReturnTy)] = &[ (Modifier::CouldError, ReturnTy::Bool), (Modifier::IntroducesNulls, ReturnTy::Bool), @@ -136,6 +149,13 @@ impl Shape { } } + /// Whether this arity's table lists `modifier`. + pub(crate) fn accepts(&self, modifier: Modifier) -> bool { + self.modifiers() + .iter() + .any(|(candidate, _)| *candidate == modifier) + } + pub(crate) fn label(&self) -> &'static str { match self { Shape::Unary => "unary", @@ -228,25 +248,18 @@ impl Shape { mod tests { use super::{Modifier, Shape}; - fn has(shape: Shape, m: Modifier) -> bool { - shape - .modifiers() - .iter() - .any(|(candidate, _)| *candidate == m) - } - #[mz_ore::test] fn unary_accepts_inverse_and_rejects_negate() { - assert!(has(Shape::Unary, Modifier::Inverse)); - assert!(has(Shape::Unary, Modifier::PreservesUniqueness)); - assert!(has(Shape::Unary, Modifier::IsEliminableCast)); - assert!(!has(Shape::Unary, Modifier::Negate)); - assert!(!has(Shape::Unary, Modifier::IsInfixOp)); + assert!(Shape::Unary.accepts(Modifier::Inverse)); + assert!(Shape::Unary.accepts(Modifier::PreservesUniqueness)); + assert!(Shape::Unary.accepts(Modifier::IsEliminableCast)); + assert!(!Shape::Unary.accepts(Modifier::Negate)); + assert!(!Shape::Unary.accepts(Modifier::IsInfixOp)); assert!( - !has(Shape::Unary, Modifier::PropagatesNulls), + !Shape::Unary.accepts(Modifier::PropagatesNulls), "Shape::nullability omits unary's non-nullable-position term on the strength of \ this absence" ); - assert!(!has(Shape::Unary, Modifier::IsAssociative)); + assert!(!Shape::Unary.accepts(Modifier::IsAssociative)); } } diff --git a/src/expr-derive-impl/src/test.rs b/src/expr-derive-impl/src/test.rs index c44e7358c219c..ecdb91b449c83 100644 --- a/src/expr-derive-impl/src/test.rs +++ b/src/expr-derive-impl/src/test.rs @@ -452,3 +452,19 @@ fn skip_display_rejects_a_unit_struct() { "expected the receiver error, got:\n{output}" ); } + +#[cfg_attr(miri, ignore)] // unsupported operation: extern static `pidfd_spawnp` is not supported by Miri +#[mz_ore::test] +fn validation_reports_every_error() { + let (output, _input) = super::test_sqlfunc( + quote! { negate = super::Foo, skip_display = true }, + quote! { + fn some_unary<'a>(a: i32) -> i32 { a } + }, + ); + assert!( + output.contains("`negate` is not supported for unary functions") + && output.contains("skip_display requires a &self receiver"), + "expected both errors, got:\n{output}" + ); +} From 6d92c1af9da233a6e6dd7ed94c863bac0774020c Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Wed, 14 Oct 2026 16:43:11 +0200 Subject: [PATCH 17/19] expr-derive-impl: Share the binary and variadic nullability tail The two arms of `Shape::nullability` computed the same expression and differed only in whether they bound it to `is_null` first. They are one arm now, and the variadic snapshots pick up the binding. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/expr-derive-impl/src/shape.rs | 11 +---------- ...xpr_derive_impl__test__variadic_all_modifiers.snap | 10 ++++------ .../mz_expr_derive_impl__test__variadic_arena.snap | 10 ++++------ ...mz_expr_derive_impl__test__variadic_modifiers.snap | 10 ++++------ .../mz_expr_derive_impl__test__variadic_tuple.snap | 10 ++++------ ...xpr_derive_impl__test__variadic_variadic_type.snap | 10 ++++------ 6 files changed, 21 insertions(+), 40 deletions(-) diff --git a/src/expr-derive-impl/src/shape.rs b/src/expr-derive-impl/src/shape.rs index c0452c8f1220b..c05f4a5a08183 100644 --- a/src/expr-derive-impl/src/shape.rs +++ b/src/expr-derive-impl/src/shape.rs @@ -223,7 +223,7 @@ impl Shape { Shape::Unary => quote! { output.nullable(nullable || (propagates_nulls && input_type.nullable)) }, - Shape::Binary => quote! { + Shape::Binary | Shape::Variadic => quote! { let non_nullable_input_is_nullable = false #(#checks)*; let inputs_nullable = input_types.iter().any(|it| it.nullable); let is_null = nullable @@ -231,15 +231,6 @@ impl Shape { || (propagates_nulls && inputs_nullable); output.nullable(is_null) }, - Shape::Variadic => quote! { - let non_nullable_input_is_nullable = false #(#checks)*; - let inputs_nullable = input_types.iter().any(|it| it.nullable); - output.nullable( - nullable - || non_nullable_input_is_nullable - || (propagates_nulls && inputs_nullable) - ) - }, } } } diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_all_modifiers.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_all_modifiers.snap index 484e94cef9e6e..a3c2dc25aa0a5 100644 --- a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_all_modifiers.snap +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_all_modifiers.snap @@ -1,5 +1,5 @@ --- -source: src/expr-derive-impl/src/lib.rs +source: src/expr-derive-impl/src/test.rs expression: "#[sqlfunc(\n could_error = true,\n output_type = i16,\n is_infix_op = true,\n is_monotone = true,\n is_associative = true,\n propagates_nulls = true,\n sqlname = \"variadic_all_modifiers\",\n)]\nfn variadic_all_modifiers<'a>(datums: Variadic>) -> Datum<'a> {\n datums.into_iter().next().unwrap_or(Datum::Null)\n}\n" --- #[derive( @@ -37,11 +37,9 @@ impl crate::func::variadic::EagerVariadicFunc for VariadicAllModifiers { let nullable = output.nullable; let non_nullable_input_is_nullable = false; let inputs_nullable = input_types.iter().any(|it| it.nullable); - output - .nullable( - nullable || non_nullable_input_is_nullable - || (propagates_nulls && inputs_nullable), - ) + let is_null = nullable || non_nullable_input_is_nullable + || (propagates_nulls && inputs_nullable); + output.nullable(is_null) } fn could_error(&self) -> bool { true diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_arena.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_arena.snap index b9d67aad14f47..24e1f01d95e2a 100644 --- a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_arena.snap +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_arena.snap @@ -1,5 +1,5 @@ --- -source: src/expr-derive-impl/src/lib.rs +source: src/expr-derive-impl/src/test.rs expression: "#[sqlfunc(ArrayFill, sqlname = \"array_fill\", introduces_nulls = false)]\nfn array_fill<'a>(\n &self,\n fill: Datum<'a>,\n dims: Datum<'a>,\n lb: OptionalArg>,\n temp_storage: &RowArena,\n) -> Result, EvalError> {\n unimplemented!()\n}\n" --- impl ArrayFill { @@ -35,11 +35,9 @@ impl crate::func::variadic::EagerVariadicFunc for ArrayFill { let nullable = output.nullable; let non_nullable_input_is_nullable = false; let inputs_nullable = input_types.iter().any(|it| it.nullable); - output - .nullable( - nullable || non_nullable_input_is_nullable - || (propagates_nulls && inputs_nullable), - ) + let is_null = nullable || non_nullable_input_is_nullable + || (propagates_nulls && inputs_nullable); + output.nullable(is_null) } fn introduces_nulls(&self) -> bool { false diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_modifiers.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_modifiers.snap index 2a3edfaa9c858..fc87dec2bb647 100644 --- a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_modifiers.snap +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_modifiers.snap @@ -1,5 +1,5 @@ --- -source: src/expr-derive-impl/src/lib.rs +source: src/expr-derive-impl/src/test.rs expression: "#[sqlfunc(Greatest, sqlname = \"greatest\", could_error = false, is_monotone = true)]\nfn greatest<'a>(datums: Variadic>) -> Datum<'a> {\n datums.into_iter().max().unwrap_or(Datum::Null)\n}\n" --- #[derive( @@ -37,11 +37,9 @@ impl crate::func::variadic::EagerVariadicFunc for Greatest { let nullable = output.nullable; let non_nullable_input_is_nullable = false; let inputs_nullable = input_types.iter().any(|it| it.nullable); - output - .nullable( - nullable || non_nullable_input_is_nullable - || (propagates_nulls && inputs_nullable), - ) + let is_null = nullable || non_nullable_input_is_nullable + || (propagates_nulls && inputs_nullable); + output.nullable(is_null) } fn could_error(&self) -> bool { false diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_tuple.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_tuple.snap index 5c30e5f17ad3c..26d12b0a5b8af 100644 --- a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_tuple.snap +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_tuple.snap @@ -1,5 +1,5 @@ --- -source: src/expr-derive-impl/src/lib.rs +source: src/expr-derive-impl/src/test.rs expression: "#[sqlfunc(Replace, sqlname = \"replace\")]\nfn replace(text: &str, from: &str, to: &str) -> Result {\n Ok(text.replace(from, to))\n}\n" --- #[derive( @@ -40,11 +40,9 @@ impl crate::func::variadic::EagerVariadicFunc for Replace { || input_types.get(1usize).map_or(false, |t| t.nullable) || input_types.get(2usize).map_or(false, |t| t.nullable); let inputs_nullable = input_types.iter().any(|it| it.nullable); - output - .nullable( - nullable || non_nullable_input_is_nullable - || (propagates_nulls && inputs_nullable), - ) + let is_null = nullable || non_nullable_input_is_nullable + || (propagates_nulls && inputs_nullable); + output.nullable(is_null) } } impl std::fmt::Display for Replace { diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_variadic_type.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_variadic_type.snap index 634e29df91e15..3868209fe582b 100644 --- a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_variadic_type.snap +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_variadic_type.snap @@ -1,5 +1,5 @@ --- -source: src/expr-derive-impl/src/lib.rs +source: src/expr-derive-impl/src/test.rs expression: "#[sqlfunc(Concat, sqlname = \"concat\", is_associative = true)]\nfn concat(strs: Variadic>) -> Result {\n Ok(strs.into_iter().flatten().collect())\n}\n" --- #[derive( @@ -37,11 +37,9 @@ impl crate::func::variadic::EagerVariadicFunc for Concat { let nullable = output.nullable; let non_nullable_input_is_nullable = false; let inputs_nullable = input_types.iter().any(|it| it.nullable); - output - .nullable( - nullable || non_nullable_input_is_nullable - || (propagates_nulls && inputs_nullable), - ) + let is_null = nullable || non_nullable_input_is_nullable + || (propagates_nulls && inputs_nullable); + output.nullable(is_null) } fn is_associative(&self) -> bool { true From 13f3e9571d1379b8a09e666a784e6f4defdb1a53 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Wed, 14 Oct 2026 16:43:33 +0200 Subject: [PATCH 18/19] expr-derive-impl: Classify a generic's output usage once `derive_output_type_for_generics` classified each generic against the output type to pick one, and `derive_output_type_for_generic` classified the chosen one again, with an `Absent` check its only caller made unreachable. The usage is now passed along. The error for a generic missing from the inputs names the generic instead of always saying `T`. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/expr-derive-impl/src/signature.rs | 33 ++++++++++++--------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/expr-derive-impl/src/signature.rs b/src/expr-derive-impl/src/signature.rs index aa6357afd76e6..12a4a0599139f 100644 --- a/src/expr-derive-impl/src/signature.rs +++ b/src/expr-derive-impl/src/signature.rs @@ -338,17 +338,18 @@ pub(crate) fn derive_output_type_for_generics( is_unary: bool, ) -> darling::Result> { // Find the first generic param that appears in the output. - let generic_name = match generic_names - .iter() - .find(|gn| classify_generic_usage(output_ty, gn) != GenericUsage::Absent) - { - Some(gn) => gn, - None => return Ok(None), + let Some((generic_name, output_usage)) = generic_names.iter().find_map(|gn| { + let usage = classify_generic_usage(output_ty, gn); + (usage != GenericUsage::Absent).then_some((gn, usage)) + }) else { + return Ok(None); }; - derive_output_type_for_generic(input_types, output_ty, generic_name, is_unary) + derive_output_type_for_generic(input_types, output_ty, generic_name, output_usage, is_unary) + .map(Some) } -/// Derives an `output_type_expr` for a single generic parameter. +/// Derives an `output_type_expr` for a single generic parameter, which appears in the +/// output type as `output_usage`. /// /// Uses `SqlContainerType` trait calls instead of matching on specific container /// type names. The generated code calls `::unwrap_element_type()` @@ -357,13 +358,9 @@ fn derive_output_type_for_generic( input_types: &[syn::Type], output_ty: &syn::Type, generic_name: &Ident, + output_usage: GenericUsage, is_unary: bool, -) -> darling::Result> { - let output_usage = classify_generic_usage(output_ty, generic_name); - if output_usage == GenericUsage::Absent { - return Ok(None); - } - +) -> darling::Result { let nullable = is_option_wrapped(output_ty); // Find the first input parameter that has T in a container. @@ -387,9 +384,9 @@ fn derive_output_type_for_generic( } let (pos, source_usage) = container_input.ok_or_else(|| { - darling::Error::custom( - "generic parameter T appears in the output type but not in any input type", - ) + darling::Error::custom(format!( + "generic parameter {generic_name} appears in the output type but not in any input type", + )) })?; // Generate the base expression to access the input type. @@ -486,7 +483,7 @@ fn derive_output_type_for_generic( } }; - Ok(Some(expr)) + Ok(expr) } /// Generates a token stream that extracts the T-level SQL type from an input From 97a1efee7106c9ce168447a481bfd93682c3a2e3 Mon Sep 17 00:00:00 2001 From: Moritz Hoffmann Date: Wed, 14 Oct 2026 16:44:22 +0200 Subject: [PATCH 19/19] expr-derive-impl: Fix the remaining stale docs and test fixtures * The `sqlfunc` rustdoc in `mz-expr-derive` listed every modifier and had drifted: `negate` still took `Some(BinaryFunc::..)` and `output_type_expr` still required `introduces_nulls`. It points at `doc/developer/sqlfunc.md`, which owns that reference. * `sqlfunc.md` put the `test` modifier's snapshots in the macro crate, although they land beside the calling file. It gave a different reason than `Shape::nullability` for the missing unary position term, and it did not describe the output-type derivation for generic signatures. * The `sqlfunc_source` doc called the lifetime-patched parameter types "as written". * `sqlfunc_for_test` was a one-line wrapper with one caller. The binary arena fixture was named `unary_fn`, and the variadic modifier fixture borrowed `Greatest`, which the macro does not cover. Co-Authored-By: Claude Opus 5.5 (1M context) --- doc/developer/sqlfunc.md | 21 +++++++-- src/expr-derive-impl/src/lib.rs | 11 +---- ...pr_derive_impl__test__binary_arena_fn.snap | 22 +++++----- ...derive_impl__test__variadic_modifiers.snap | 20 ++++----- src/expr-derive-impl/src/source.rs | 4 +- src/expr-derive-impl/src/test.rs | 7 ++- src/expr-derive/src/lib.rs | 43 +------------------ 7 files changed, 47 insertions(+), 81 deletions(-) diff --git a/doc/developer/sqlfunc.md b/doc/developer/sqlfunc.md index b4df2558e6c2a..7ebce1785573b 100644 --- a/doc/developer/sqlfunc.md +++ b/doc/developer/sqlfunc.md @@ -196,8 +196,9 @@ Generate a snapshot test for the macro expansion. * **Default:** `false` * **Applies to:** all arities -Snapshot files are stored in `src/expr-derive-impl/src/snapshots/`. -Update them with `cargo insta accept` after running `cargo test -p mz-expr-derive-impl`. +The test is emitted next to the function, so its snapshot lands in the `snapshots/` directory beside the calling source file, for example `src/expr/src/scalar/func/snapshots/`. +Update it with `cargo insta accept` after running `cargo test -p mz-expr`. +The macro's own expansion tests live in `src/expr-derive-impl/src/test.rs`, with snapshots in `src/expr-derive-impl/src/snapshots/`. ### `skip_display` @@ -213,6 +214,20 @@ Setting it to `true` rejects `sqlname`, whose only reader is the suppressed impl Which modifiers apply to which arity is declared in `src/expr-derive-impl/src/shape.rs`, one table per arity. A modifier absent from an arity's table is rejected with an error naming both the modifier and the arity. +## Generic type parameters + +A function can be generic over the element type of a container, for example `fn range_lower<'a, T>(a: Range) -> Option`. +The generated trait impl replaces each type parameter with `Datum<'a>`, because the parameters are not in scope there. +Without `output_type` or `output_type_expr`, the macro derives the output column type from where the first type parameter of the output appears among the inputs, through `mz_repr::SqlContainerType`: + +* Output `T` from an input container: the container's element type. +* Output `T` from an input `T`: that input's type. +* Output container from an input container: the input's element type, wrapped in the output container. + +Any other combination needs an explicit `output_type_expr`. +The derived type is nullable when the return type is an `Option`, which also sets the `introduces_nulls` default. +For binary and variadic functions, the derived expression soft-asserts that every input carrying the parameter agrees on its SQL type. + ## Generated items ### Struct name @@ -311,7 +326,7 @@ output.nullable = output.nullable Where: * `output.nullable` is the nullability of the column type that `output_type`, `output_type_expr`, or the return type gives. -* `non_nullable_input_is_nullable` is true if a nullable input column lands in a parameter position whose Rust type cannot represent NULL. Such a position makes the evaluation layer short-circuit to NULL, so the output is nullable no matter what `propagates_nulls` says. Unary functions have no such term, because a unary function's single position is the one `propagates_nulls` already describes. +* `non_nullable_input_is_nullable` is true if a nullable input column lands in a parameter position whose Rust type cannot represent NULL. Such a position makes the evaluation layer short-circuit to NULL, so the output is nullable no matter what `propagates_nulls` says. Unary functions have no such term. Their `propagates_nulls` cannot be overridden, so it is always `!Input::nullable()`, which already covers a non-nullable input. The NOTE on `Shape::nullability` in `src/expr-derive-impl/src/shape.rs` owns this argument. * `propagates_nulls` indicates whether NULL passes through. * `any_input_nullable` is true if any input column is nullable. diff --git a/src/expr-derive-impl/src/lib.rs b/src/expr-derive-impl/src/lib.rs index 736c7d5fb00b7..dc2f4018f061f 100644 --- a/src/expr-derive-impl/src/lib.rs +++ b/src/expr-derive-impl/src/lib.rs @@ -22,15 +22,6 @@ mod sqlfunc; pub use sqlfunc::sqlfunc; -/// Non-exported version of `sqlfunc` for testing purposes, accepting proc_macro2 token streams. -#[cfg(any(feature = "test", test))] -fn sqlfunc_for_test( - attr: proc_macro2::TokenStream, - item: proc_macro2::TokenStream, -) -> darling::Result { - sqlfunc(attr, item, false) -} - /// Expands `#[sqlfunc(attr)]` on `item` and returns the formatted expansion, or the /// formatted errors, along with the formatted input. #[cfg(any(feature = "test", test))] @@ -45,7 +36,7 @@ pub fn test_sqlfunc( let input = rust_fmt(&format!("#[sqlfunc({attr})]\n{item}")); let output = rust_fmt( - &sqlfunc_for_test(attr, item) + &sqlfunc(attr, item, false) .unwrap_or_else(|err| err.write_errors()) .to_string(), ); diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__binary_arena_fn.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__binary_arena_fn.snap index 254844ae635cd..8aa5093ebd96d 100644 --- a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__binary_arena_fn.snap +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__binary_arena_fn.snap @@ -1,6 +1,6 @@ --- -source: src/expr-derive-impl/src/lib.rs -expression: "#[sqlfunc(test = true)]\nfn unary_fn<'a>(a: Datum<'a>, b: u16, temp_storage: &RowArena) -> bool {\n unimplemented!()\n}\n" +source: src/expr-derive-impl/src/test.rs +expression: "#[sqlfunc(test = true)]\nfn binary_fn<'a>(a: Datum<'a>, b: u16, temp_storage: &RowArena) -> bool {\n unimplemented!()\n}\n" --- #[derive( Ord, @@ -14,8 +14,8 @@ expression: "#[sqlfunc(test = true)]\nfn unary_fn<'a>(a: Datum<'a>, b: u16, temp Hash, )] #[cfg_attr(any(test, feature = "proptest"), derive(proptest_derive::Arbitrary))] -pub struct UnaryFn; -impl crate::func::binary::EagerBinaryFunc for UnaryFn { +pub struct BinaryFn; +impl crate::func::binary::EagerBinaryFunc for BinaryFn { type Input<'a> = (Datum<'a>, u16); type Output<'a> = bool; fn call<'a>( @@ -23,7 +23,7 @@ impl crate::func::binary::EagerBinaryFunc for UnaryFn { (a, b): Self::Input<'a>, temp_storage: &'a mz_repr::RowArena, ) -> Self::Output<'a> { - unary_fn(a, b, temp_storage) + binary_fn(a, b, temp_storage) } fn output_sql_type( &self, @@ -43,16 +43,16 @@ impl crate::func::binary::EagerBinaryFunc for UnaryFn { output.nullable(is_null) } } -impl std::fmt::Display for UnaryFn { +impl std::fmt::Display for BinaryFn { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.write_str(stringify!(unary_fn)) + f.write_str(stringify!(binary_fn)) } } -impl crate::func::FuncName for UnaryFn { - const NAME: &'static str = stringify!(unary_fn); +impl crate::func::FuncName for BinaryFn { + const NAME: &'static str = stringify!(binary_fn); #[cfg(feature = "func-registry")] const SQLFUNC: Option = Some(crate::func::SqlFuncSource { - decl: "#[sqlfunc(test = true)] fn unary_fn<'a>(a: Datum<'a>, b: u16, temp_storage: &RowArena) -> bool", + decl: "#[sqlfunc(test = true)] fn binary_fn<'a>(a: Datum<'a>, b: u16, temp_storage: &RowArena) -> bool", signature: "fn(Datum<'a>, u16) -> bool", body_fingerprint: 4726381389472009124u64, }); @@ -71,6 +71,6 @@ impl crate::func::FuncName for UnaryFn { .collect() } } -fn unary_fn<'a>(a: Datum<'a>, b: u16, temp_storage: &RowArena) -> bool { +fn binary_fn<'a>(a: Datum<'a>, b: u16, temp_storage: &RowArena) -> bool { unimplemented!() } diff --git a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_modifiers.snap b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_modifiers.snap index fc87dec2bb647..2ff40cc04d0c0 100644 --- a/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_modifiers.snap +++ b/src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__variadic_modifiers.snap @@ -1,6 +1,6 @@ --- source: src/expr-derive-impl/src/test.rs -expression: "#[sqlfunc(Greatest, sqlname = \"greatest\", could_error = false, is_monotone = true)]\nfn greatest<'a>(datums: Variadic>) -> Datum<'a> {\n datums.into_iter().max().unwrap_or(Datum::Null)\n}\n" +expression: "#[sqlfunc(MaxDatum, could_error = false, is_monotone = true)]\nfn max_datum<'a>(datums: Variadic>) -> Datum<'a> {\n datums.into_iter().max().unwrap_or(Datum::Null)\n}\n" --- #[derive( Ord, @@ -14,8 +14,8 @@ expression: "#[sqlfunc(Greatest, sqlname = \"greatest\", could_error = false, is Hash, )] #[cfg_attr(any(test, feature = "proptest"), derive(proptest_derive::Arbitrary))] -pub struct Greatest; -impl crate::func::variadic::EagerVariadicFunc for Greatest { +pub struct MaxDatum; +impl crate::func::variadic::EagerVariadicFunc for MaxDatum { type Input<'a> = Variadic>; type Output<'a> = Datum<'a>; fn call<'a>( @@ -23,7 +23,7 @@ impl crate::func::variadic::EagerVariadicFunc for Greatest { datums: Self::Input<'a>, temp_storage: &'a mz_repr::RowArena, ) -> Self::Output<'a> { - greatest(datums) + max_datum(datums) } fn output_type( &self, @@ -48,16 +48,16 @@ impl crate::func::variadic::EagerVariadicFunc for Greatest { true } } -impl std::fmt::Display for Greatest { +impl std::fmt::Display for MaxDatum { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - f.write_str("greatest") + f.write_str(stringify!(max_datum)) } } -impl crate::func::FuncName for Greatest { - const NAME: &'static str = stringify!(greatest); +impl crate::func::FuncName for MaxDatum { + const NAME: &'static str = stringify!(max_datum); #[cfg(feature = "func-registry")] const SQLFUNC: Option = Some(crate::func::SqlFuncSource { - decl: "#[sqlfunc(Greatest, sqlname = \"greatest\", could_error = false, is_monotone = true,)] fn greatest<'a>(datums: Variadic>) -> Datum<'a>", + decl: "#[sqlfunc(MaxDatum, could_error = false, is_monotone = true,)] fn max_datum<'a>(datums: Variadic>) -> Datum<'a>", signature: "fn(Variadic>) -> Datum<'a>", body_fingerprint: 12432331865766881162u64, }); @@ -78,6 +78,6 @@ impl crate::func::FuncName for Greatest { .collect() } } -fn greatest<'a>(datums: Variadic>) -> Datum<'a> { +fn max_datum<'a>(datums: Variadic>) -> Datum<'a> { datums.into_iter().max().unwrap_or(Datum::Null) } diff --git a/src/expr-derive-impl/src/source.rs b/src/expr-derive-impl/src/source.rs index 2fc811982830d..e7c4701230179 100644 --- a/src/expr-derive-impl/src/source.rs +++ b/src/expr-derive-impl/src/source.rs @@ -114,8 +114,8 @@ fn fnv1a64(bytes: &[u8]) -> u64 { /// implement. /// /// Text comes from the token trees via [`render_tokens`], so formatting and -/// comments do not affect it. `input_tys_raw` are the parameter types as -/// written, for the signature. `input_tys` are the erased types the trait +/// comments do not affect it. `input_tys_raw` are the parameter types +/// before generic erasure, with `'a` added to elided references, for the signature. `input_tys` are the erased types the trait /// impl uses, for the probes. pub(crate) fn sqlfunc_source( attr: &TokenStream, diff --git a/src/expr-derive-impl/src/test.rs b/src/expr-derive-impl/src/test.rs index ecdb91b449c83..a20d0b4dc3b5b 100644 --- a/src/expr-derive-impl/src/test.rs +++ b/src/expr-derive-impl/src/test.rs @@ -131,7 +131,7 @@ fn insta_test_complex_output_type() { fn insta_test_binary_arena() { let attr = quote! {test = true}; let item = quote! { - fn unary_fn<'a>(a: Datum<'a>, b: u16, temp_storage: &RowArena) -> bool { + fn binary_fn<'a>(a: Datum<'a>, b: u16, temp_storage: &RowArena) -> bool { unimplemented!() } }; @@ -199,13 +199,12 @@ fn insta_test_variadic_arena() { #[mz_ore::test] fn insta_test_variadic_modifiers() { let attr = quote! { - Greatest, - sqlname = "greatest", + MaxDatum, could_error = false, is_monotone = true, }; let item = quote! { - fn greatest<'a>(datums: Variadic>) -> Datum<'a> { + fn max_datum<'a>(datums: Variadic>) -> Datum<'a> { datums.into_iter().max().unwrap_or(Datum::Null) } }; diff --git a/src/expr-derive/src/lib.rs b/src/expr-derive/src/lib.rs index ab490c4934c91..5f7fd5565d83a 100644 --- a/src/expr-derive/src/lib.rs +++ b/src/expr-derive/src/lib.rs @@ -15,47 +15,8 @@ /// and variadic functions. Depending on the kind of function, it will implement the /// appropriate traits: `EagerUnaryFunc`, `EagerBinaryFunc`, or `EagerVariadicFunc`. /// -/// The macro takes the following arguments: -/// * `is_monotone`: An expression indicating whether the function is monotone. For unary and -/// variadic functions, it should be an expression that evaluates to a boolean. For binary -/// functions, it should be a tuple of two expressions, each evaluating to a boolean. See -/// `LazyBinaryFunc` for details. -/// * `is_infinity_monotone`: A boolean indicating whether `is_monotone`'s endpoint-sampling -/// guarantee still holds when an operand may be infinite. Applies to binary functions only. -/// * `sqlname`: The SQL name of the function. -/// * `preserves_uniqueness`: A boolean indicating whether the function preserves uniqueness. -/// Unary functions only. -/// * `inverse`: A function that is the inverse of the function. Applies to unary functions only. -/// * `negate`: A function that negates the function. Applies to binary functions only. For example, -/// the `!=` operator is the negation of the `=` operator, and we'd mark the `Eq` function with -/// `negate = Some(BinaryFunc::NotEq)`. -/// * `is_infix_op`: A boolean indicating whether the function is an infix operator. Applies to -/// binary and variadic functions. -/// * `is_associative`: A boolean indicating whether the function is associative. Applies to -/// variadic functions only. -/// * `is_eliminable_cast`: A boolean indicating whether the function is a cast that the -/// optimizer may remove. Applies to unary functions only. -/// * `output_type`: The output type of the function. -/// * `output_type_expr`: An expression that evaluates to the output type. Applies to unary, -/// binary, and variadic functions. For unary functions, the expression has access to -/// `input_type: SqlColumnType`; for binary and variadic functions, -/// `input_types: &[SqlColumnType]`. Should evaluate to a `SqlColumnType` value. This -/// expression determines the *base* output type; the macro separately computes -/// nullability by combining `introduces_nulls`, `propagates_nulls`, and input nullability. -/// Requires `introduces_nulls`, and conflicts with `output_type`. -/// * `could_error`: A boolean indicating whether the function could error. -/// * `propagates_nulls`: A boolean indicating whether the function propagates nulls. Applies to -/// binary and variadic functions. If not specified, use the default implementation from the -/// trait. The default is to return true if all input types are non-nullable. -/// * `introduces_nulls`: A boolean indicating whether the function introduces nulls. Applies to -/// all functions. If not specified, use the default implementation from the trait. -/// The default is to return the `nullable` property of the output type. -/// * `test`: A boolean indicating whether to generate a snapshot test for the function. -/// Defaults to `false`. -/// * `skip_display`: A boolean suppressing the generated `fmt::Display` impl. Set this -/// when the struct's name depends on its state, so the call site keeps a hand-written -/// `Display` impl instead. Setting it to `true` rejects `sqlname`, whose only reader -/// is the suppressed impl. +/// `doc/developer/sqlfunc.md` documents the modifiers, their defaults, and which arities +/// accept them. /// /// # Limitations /// * The input and output types can contain lifetime parameters, as long as they are `'a`.