Skip to content

Commit f8f9b3a

Browse files
committed
Address comments
Signed-off-by: Moritz Hoffmann <mh@materialize.com>
1 parent 8c18315 commit f8f9b3a

21 files changed

+111
-67
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,10 @@ differential-dataflow = { opt-level = 3 }
273273
mz-compute = { opt-level = 3 }
274274
mz-transform = { opt-level = 3 }
275275

276+
# Recommended by insta
277+
insta = { opt-level = 3 }
278+
similar = { opt-level = 3 }
279+
276280
[profile.release]
277281
# Compile time seems similar to "lto = false", runtime ~10% faster
278282
lto = "thin"

WORKSPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,8 @@ crates_repository(
558558
"rustc-demangle": [crate.annotation(rustc_flags = ["-Copt-level=3"])],
559559
"timely": [crate.annotation(rustc_flags = ["-Copt-level=3"])],
560560
"differential-dataflow": [crate.annotation(rustc_flags = ["-Copt-level=3"])],
561+
"insta": [crate.annotation(rustc_flags = ["-Copt-level=3"])],
562+
"similar": [crate.annotation(rustc_flags = ["-Copt-level=3"])],
561563
},
562564
cargo_config = "//:.cargo/config.toml",
563565
cargo_lockfile = "//:Cargo.lock",

src/expr-derive-impl/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ insta = { version = "1.42", features = ["serde"] }
2323
mz-ore = { path = "../ore", features = ["test"] }
2424
prettyplease = "0.2"
2525

26+
[package.metadata.cargo-gazelle.lib]
27+
rustc_flags = ["-Copt-level=3"]
28+
2629
[package.metadata.cargo-udeps.ignore]
2730
normal = ["workspace-hack"]
2831

src/expr-derive-impl/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
// by the Apache License, Version 2.0.
99

1010
//! Implementations of proc macros to derive SQL function traits.
11+
//!
12+
//! This is separate from the actual proc macro to allow exporting the
13+
//! function defining the proc macro itself. Proc macro crates cannot
14+
//! export anything but proc macros.
1115
1216
mod sqlfunc;
1317

src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__add_int16.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl<'a> crate::func::binary::EagerBinaryFunc<'a> for AddInt16 {
4747
)
4848
}
4949
fn introduces_nulls(&self) -> bool {
50-
<i16 as mz_repr::DatumType<'_, ()>>::nullable()
50+
<i16 as ::mz_repr::DatumType<'_, ()>>::nullable()
5151
}
5252
fn is_infix_op(&self) -> bool {
5353
true

src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__complex_type.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl<'a> crate::func::binary::EagerBinaryFunc<'a> for ComplexOutputTypeFn {
4747
)
4848
}
4949
fn introduces_nulls(&self) -> bool {
50-
<Option<bool> as mz_repr::DatumType<'_, ()>>::nullable()
50+
<Option<bool> as ::mz_repr::DatumType<'_, ()>>::nullable()
5151
}
5252
fn is_infix_op(&self) -> bool {
5353
true

src/expr-derive-impl/src/snapshots/mz_expr_derive_impl__test__unary_arena_fn.snap

Lines changed: 2 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,6 @@
22
source: src/expr-derive-impl/src/lib.rs
33
expression: "#[sqlfunc()]\nfn unary_fn<'a>(a: Datum<'a>, temp_storage: &RowArena) -> bool {\n unimplemented!()\n}\n"
44
---
5-
#[derive(
6-
proptest_derive::Arbitrary,
7-
Ord,
8-
PartialOrd,
9-
Clone,
10-
Debug,
11-
Eq,
12-
PartialEq,
13-
serde::Serialize,
14-
serde::Deserialize,
15-
Hash,
16-
mz_lowertest::MzReflect
17-
)]
18-
pub struct UnaryFn;
19-
impl<'a> crate::func::EagerUnaryFunc<'a> for UnaryFn {
20-
type Input = Datum<'a>;
21-
type Output = bool;
22-
fn call(&self, a: Self::Input) -> Self::Output {
23-
unary_fn(a, temp_storage)
24-
}
25-
fn output_type(&self, input_type: mz_repr::ColumnType) -> mz_repr::ColumnType {
26-
use mz_repr::AsColumnType;
27-
let output = Self::Output::as_column_type();
28-
let propagates_nulls = crate::func::EagerUnaryFunc::propagates_nulls(self);
29-
let nullable = output.nullable;
30-
output.nullable(nullable || (propagates_nulls && input_type.nullable))
31-
}
32-
}
33-
impl std::fmt::Display for UnaryFn {
34-
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
35-
f.write_str(stringify!(unary_fn))
36-
}
37-
}
38-
fn unary_fn<'a>(a: Datum<'a>, temp_storage: &RowArena) -> bool {
39-
unimplemented!()
5+
::core::compile_error! {
6+
"Unary functions do not yet support RowArena."
407
}

src/expr-derive-impl/src/sqlfunc.rs

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use syn::{Expr, Lifetime};
1616
/// Modifiers passed as key-value pairs to the `#[sqlfunc]` macro.
1717
#[derive(Debug, Default, darling::FromMeta)]
1818
pub(crate) struct Modifiers {
19-
/// An optional expression that evaluates to a boolean indicating whether the function i
19+
/// An optional expression that evaluates to a boolean indicating whether the function is
2020
/// monotone with respect to its arguments. Defined for unary and binary functions.
2121
is_monotone: Option<Expr>,
2222
/// The SQL name for the function. Applies to all functions.
@@ -37,6 +37,8 @@ pub(crate) struct Modifiers {
3737
could_error: Option<Expr>,
3838
/// Whether the function propagates nulls. Applies to binary functions.
3939
propagates_nulls: Option<Expr>,
40+
/// Whether the function introduces nulls. Applies to all functions.
41+
introduces_nulls: Option<Expr>,
4042
}
4143

4244
/// Implementation for the `#[sqlfunc]` macro. The first parameter is the attribute
@@ -54,7 +56,10 @@ pub fn sqlfunc(
5456
let func = syn::parse2::<syn::ItemFn>(item.clone())?;
5557

5658
let tokens = match determine_parameters_arena(&func) {
57-
(1, arena) => unary_func(&func, modifiers, arena),
59+
(1, false) => unary_func(&func, modifiers),
60+
(1, true) => Err(darling::Error::custom(
61+
"Unary functions do not yet support RowArena.",
62+
)),
5863
(2, arena) => binary_func(&func, modifiers, arena),
5964
(other, _) => Err(darling::Error::custom(format!(
6065
"Unsupported function: {} parameters",
@@ -171,11 +176,7 @@ fn output_type(arg: &syn::ItemFn) -> Result<&syn::Type, syn::Error> {
171176
}
172177

173178
/// Produce a `EagerUnaryFunc` implementation.
174-
fn unary_func(
175-
func: &syn::ItemFn,
176-
modifiers: Modifiers,
177-
arena: bool,
178-
) -> darling::Result<TokenStream> {
179+
fn unary_func(func: &syn::ItemFn, modifiers: Modifiers) -> darling::Result<TokenStream> {
179180
let fn_name = &func.sig.ident;
180181
let struct_name = camel_case(&func.sig.ident);
181182
let input_ty = arg_type(func, 0)?;
@@ -190,16 +191,23 @@ fn unary_func(
190191
negate,
191192
could_error,
192193
propagates_nulls,
194+
introduces_nulls,
193195
} = modifiers;
194196

195197
if is_infix_op.is_some() {
196-
return Err(darling::Error::unknown_field("is_infix_op"));
198+
return Err(darling::Error::unknown_field(
199+
"is_infix_op not supported for unary functions",
200+
));
197201
}
198202
if negate.is_some() {
199-
return Err(darling::Error::unknown_field("negate"));
203+
return Err(darling::Error::unknown_field(
204+
"negate not supported for unary functions",
205+
));
200206
}
201207
if propagates_nulls.is_some() {
202-
return Err(darling::Error::unknown_field("propagates_nulls"));
208+
return Err(darling::Error::unknown_field(
209+
"propagates_nulls not supported for unary functions",
210+
));
203211
}
204212

205213
let preserves_uniqueness_fn = preserves_uniqueness.map(|preserves_uniqueness| {
@@ -236,10 +244,10 @@ fn unary_func(
236244
}
237245
};
238246

239-
let (output_type, introduces_nulls_fn) = if let Some(output_type) = output_type {
247+
let (output_type, mut introduces_nulls_fn) = if let Some(output_type) = output_type {
240248
let introduces_nulls_fn = quote! {
241249
fn introduces_nulls(&self) -> bool {
242-
<#output_type as mz_repr::DatumType<'_, ()>>::nullable()
250+
<#output_type as ::mz_repr::DatumType<'_, ()>>::nullable()
243251
}
244252
};
245253
let output_type = quote! { <#output_type> };
@@ -248,7 +256,13 @@ fn unary_func(
248256
(quote! { Self::Output }, None)
249257
};
250258

251-
let arena = arena.then(|| quote! { , temp_storage });
259+
if let Some(introduces_nulls) = introduces_nulls {
260+
introduces_nulls_fn = Some(quote! {
261+
fn introduces_nulls(&self) -> bool {
262+
#introduces_nulls
263+
}
264+
});
265+
}
252266

253267
let could_error_fn = could_error.map(|could_error| {
254268
quote! {
@@ -267,7 +281,7 @@ fn unary_func(
267281
type Output = #output_ty;
268282

269283
fn call(&self, a: Self::Input) -> Self::Output {
270-
#fn_name(a #arena)
284+
#fn_name(a)
271285
}
272286

273287
fn output_type(&self, input_type: mz_repr::ColumnType) -> mz_repr::ColumnType {
@@ -320,13 +334,18 @@ fn binary_func(
320334
negate,
321335
could_error,
322336
propagates_nulls,
337+
introduces_nulls,
323338
} = modifiers;
324339

325340
if preserves_uniqueness.is_some() {
326-
return Err(darling::Error::unknown_field("preserves_uniqueness"));
341+
return Err(darling::Error::unknown_field(
342+
"preserves_uniqueness not supported for binary functions",
343+
));
327344
}
328345
if inverse.is_some() {
329-
return Err(darling::Error::unknown_field("inverse"));
346+
return Err(darling::Error::unknown_field(
347+
"inverse not supported for binary functions",
348+
));
330349
}
331350

332351
let negate_fn = negate.map(|negate| {
@@ -355,10 +374,10 @@ fn binary_func(
355374
}
356375
};
357376

358-
let (output_type, introduces_nulls_fn) = if let Some(output_type) = output_type {
377+
let (output_type, mut introduces_nulls_fn) = if let Some(output_type) = output_type {
359378
let introduces_nulls_fn = quote! {
360379
fn introduces_nulls(&self) -> bool {
361-
<#output_type as mz_repr::DatumType<'_, ()>>::nullable()
380+
<#output_type as ::mz_repr::DatumType<'_, ()>>::nullable()
362381
}
363382
};
364383
let output_type = quote! { <#output_type> };
@@ -367,6 +386,14 @@ fn binary_func(
367386
(quote! { Self::Output }, None)
368387
};
369388

389+
if let Some(introduces_nulls) = introduces_nulls {
390+
introduces_nulls_fn = Some(quote! {
391+
fn introduces_nulls(&self) -> bool {
392+
#introduces_nulls
393+
}
394+
});
395+
}
396+
370397
let arena = if arena {
371398
quote! { , temp_storage }
372399
} else {

src/expr-derive/src/lib.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,43 @@
1010
//! Proc macros to derive SQL function traits.
1111
1212
/// Derive function traits for SQL functions.
13+
///
14+
/// The `sqlfunc` attribute macro is used to derive SQL function traits for unary and binary
15+
/// functions. Depending on the kind of function, it will implement the appropriate traits.
16+
/// For unary functions, it implements the `EagerUnaryFunc` trait, and for binary functions,
17+
/// it implements the `EagerBinaryFunc` trait.
18+
///
19+
/// The macro takes the following arguments:
20+
/// * `is_monotone`: An expression indicating whether the function is monotone. For unary functions,
21+
/// it should be an expression that evaluates to a boolean. For binary functions, it should be a
22+
/// tuple of two expressions, each evaluating to a boolean. See `LazyBinaryFunc` for details.
23+
/// * `sqlname`: The SQL name of the function.
24+
/// * `preserves_uniqueness`: A boolean indicating whether the function preserves uniqueness.
25+
/// Unary functions only.
26+
/// * `inverse`: A function that is the inverse of the function. Applies to unary functions only.
27+
/// * `negate`: A function that negates the function. Applies to binary functions only. For example,
28+
/// the `!=` operator is the negation of the `=` operator, and we'd mark the `Eq` function with
29+
/// `negate = Some(BinaryFunc::NotEq)`.
30+
/// * `is_infix_op`: A boolean indicating whether the function is an infix operator. Applies to
31+
/// binary functions only.
32+
/// * `output_type`: The output type of the function.
33+
/// * `could_error`: A boolean indicating whether the function could error.
34+
/// * `propagate_nulls`: A boolean indicating whether the function propagates nulls. Applies to
35+
/// binary functions only.
36+
///
37+
/// # Limitations
38+
/// * The input and output types can contain lifetime parameters, as long as they are `'a`.
39+
/// * Unary functions cannot yet receive a `&RowArena` as an argument.
40+
/// * The `output_type`
41+
///
42+
/// # Examples
43+
/// ```ignore
44+
/// use mz_expr_derive::sqlfunc;
45+
/// #[sqlfunc(sqlname = "!")]
46+
/// fn negate(a: bool) -> bool {
47+
/// !a
48+
/// }
49+
/// ```
1350
#[proc_macro_attribute]
1451
pub fn sqlfunc(
1552
attr: proc_macro::TokenStream,

src/expr/src/scalar/snapshots/mz_expr__scalar__func__add_date_interval.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl<'a> crate::func::binary::EagerBinaryFunc<'a> for AddDateInterval {
4747
)
4848
}
4949
fn introduces_nulls(&self) -> bool {
50-
<CheckedTimestamp<NaiveDateTime> as mz_repr::DatumType<'_, ()>>::nullable()
50+
<CheckedTimestamp<NaiveDateTime> as ::mz_repr::DatumType<'_, ()>>::nullable()
5151
}
5252
fn is_infix_op(&self) -> bool {
5353
true

0 commit comments

Comments
 (0)