common_macro/lib.rs
1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15mod admin_fn;
16mod aggr_func;
17mod print_caller;
18mod range_fn;
19mod row;
20mod stack_trace_debug;
21mod utils;
22
23use aggr_func::{impl_aggr_func_type_store, impl_as_aggr_func_creator};
24use print_caller::process_print_caller;
25use proc_macro::TokenStream;
26use quote::quote;
27use range_fn::process_range_fn;
28use syn::{Data, DeriveInput, Fields, parse_macro_input};
29
30use crate::admin_fn::process_admin_fn;
31use crate::row::into_row::derive_into_row_impl;
32use crate::row::schema::derive_schema_impl;
33use crate::row::to_row::derive_to_row_impl;
34
35/// Make struct implemented trait [AggrFuncTypeStore], which is necessary when writing UDAF.
36/// This derive macro is expect to be used along with attribute macro [macro@as_aggr_func_creator].
37#[proc_macro_derive(AggrFuncTypeStore)]
38pub fn aggr_func_type_store_derive(input: TokenStream) -> TokenStream {
39 let ast = parse_macro_input!(input as DeriveInput);
40 impl_aggr_func_type_store(&ast)
41}
42
43/// A struct can be used as a creator for aggregate function if it has been annotated with this
44/// attribute first.
45///
46/// This attribute add a necessary field which is intended to store the input
47/// data's types to the struct.
48/// This attribute is expected to be used along with derive macro [AggrFuncTypeStore].
49#[proc_macro_attribute]
50pub fn as_aggr_func_creator(args: TokenStream, input: TokenStream) -> TokenStream {
51 impl_as_aggr_func_creator(args, input)
52}
53
54/// Attribute macro to convert an arithimetic function to a range function. The annotated function
55/// should accept servaral arrays as input and return a single value as output.
56///
57/// This procedure macro can works on any number of input parameters. Return type can be either
58/// primitive type or wrapped in `Option`.
59///
60/// # Example
61/// Take `count_over_time()` in PromQL as an example:
62/// ```rust, ignore
63/// /// The count of all values in the specified interval.
64/// #[range_fn(
65/// name = "CountOverTime",
66/// ret = "Float64Array",
67/// display_name = "prom_count_over_time"
68/// )]
69/// pub fn count_over_time(_: &TimestampMillisecondArray, values: &Float64Array) -> f64 {
70/// values.len() as f64
71/// }
72/// ```
73///
74/// # Arguments
75/// - `name`: The name of the generated `ScalarUDF` struct.
76/// - `ret`: The return type of the generated UDF function.
77/// - `display_name`: The display name of the generated UDF function.
78#[proc_macro_attribute]
79pub fn range_fn(args: TokenStream, input: TokenStream) -> TokenStream {
80 process_range_fn(args, input)
81}
82
83/// Attribute macro to convert a normal function to SQL administration function. The annotated function
84/// should accept:
85/// - `&ProcedureServiceHandlerRef` or `&TableMutationHandlerRef` or `FlowServiceHandlerRef` as the first argument,
86/// - `&QueryContextRef` as the second argument, and
87/// - `&[ValueRef<'_>]` as the third argument which is SQL function input values in each row.
88///
89/// Return type must be `common_query::error::Result<Value>`.
90///
91/// # Example see `common/function/src/system/procedure_state.rs`.
92///
93/// # Arguments
94/// - `name`: The name of the generated `Function` implementation.
95/// - `ret`: The return type of the generated SQL function, it will be transformed into `ConcreteDataType::{ret}_datatype()` result.
96/// - `display_name`: The display name of the generated SQL function.
97/// - `sig_fn`: the function to returns `Signature` of generated `Function`.
98/// - `user_path`: Optional path to the trait and context (e.g., `crate`);
99/// defaults to `crate` if not provided.
100#[proc_macro_attribute]
101pub fn admin_fn(args: TokenStream, input: TokenStream) -> TokenStream {
102 process_admin_fn(args, input)
103}
104
105/// Attribute macro to print the caller to the annotated function.
106/// The caller is printed as its filename and the call site line number.
107///
108/// This macro works like this: inject the tracking codes as the first statement to the annotated
109/// function body. The tracking codes use [backtrace-rs](https://crates.io/crates/backtrace) to get
110/// the callers. So you must dependent on the `backtrace-rs` crate.
111///
112/// # Arguments
113/// - `depth`: The max depth of call stack to print. Optional, defaults to 1.
114///
115/// # Example
116/// ```rust, ignore
117///
118/// #[print_caller(depth = 3)]
119/// fn foo() {}
120/// ```
121#[proc_macro_attribute]
122pub fn print_caller(args: TokenStream, input: TokenStream) -> TokenStream {
123 process_print_caller(args, input)
124}
125
126/// Attribute macro to derive [std::fmt::Debug] for the annotated `Error` type.
127///
128/// The generated `Debug` implementation will print the error in a stack trace style. E.g.:
129/// ```plaintext
130/// 0: Foo error, at src/common/catalog/src/error.rs:80:10
131/// 1: Bar error, at src/common/function/src/error.rs:90:10
132/// 2: Root cause, invalid table name, at src/common/catalog/src/error.rs:100:10
133/// ```
134///
135/// Notes on using this macro:
136/// - `#[snafu(display)]` must present on each enum variants,
137/// and should not include `location` and `source`.
138/// - Only our internal error can be named `source`.
139/// All external error should be `error` with an `#[snafu(source)]` annotation.
140/// - `common_error` crate must be accessible.
141#[proc_macro_attribute]
142pub fn stack_trace_debug(args: TokenStream, input: TokenStream) -> TokenStream {
143 stack_trace_debug::stack_trace_style_impl(args.into(), input.into()).into()
144}
145
146/// Generates implementation for `From<&TableMeta> for TableMetaBuilder`
147#[proc_macro_derive(ToMetaBuilder)]
148pub fn derive_meta_builder(input: TokenStream) -> TokenStream {
149 let input = parse_macro_input!(input as DeriveInput);
150
151 let Data::Struct(data_struct) = input.data else {
152 panic!("ToMetaBuilder can only be derived for structs");
153 };
154
155 let Fields::Named(fields) = data_struct.fields else {
156 panic!("ToMetaBuilder can only be derived for structs with named fields");
157 };
158
159 // Check that this is being applied to TableMeta struct
160 if input.ident != "TableMeta" {
161 panic!("ToMetaBuilder can only be derived for TableMeta struct");
162 }
163
164 let field_init = fields.named.iter().map(|field| {
165 let field_name = field.ident.as_ref().unwrap();
166 quote! {
167 #field_name: Default::default(),
168 }
169 });
170
171 let field_assignments = fields.named.iter().map(|field| {
172 let field_name = field.ident.as_ref().unwrap();
173 quote! {
174 builder.#field_name(meta.#field_name.clone());
175 }
176 });
177
178 let generated = quote! {
179 impl From<&TableMeta> for TableMetaBuilder {
180 fn from(meta: &TableMeta) -> Self {
181 let mut builder = Self {
182 #(#field_init)*
183 };
184
185 #(#field_assignments)*
186 builder
187 }
188 }
189 };
190
191 generated.into()
192}
193
194/// Derive macro to convert a struct to a row.
195///
196/// # Example
197/// ```rust, ignore
198/// use api::v1::Row;
199/// use api::v1::value::ValueData;
200/// use api::v1::Value;
201///
202/// #[derive(ToRow)]
203/// struct ToRowTest {
204/// my_value: i32,
205/// #[col(name = "string_value", datatype = "string", semantic = "tag")]
206/// my_string: String,
207/// my_bool: bool,
208/// my_float: f32,
209/// #[col(
210/// name = "timestamp_value",
211/// semantic = "Timestamp",
212/// datatype = "TimestampMillisecond"
213/// )]
214/// my_timestamp: i64,
215/// #[col(skip)]
216/// my_skip: i32,
217/// }
218///
219/// let row = ToRowTest {
220/// my_value: 1,
221/// my_string: "test".to_string(),
222/// my_bool: true,
223/// my_float: 1.0,
224/// my_timestamp: 1718563200000,
225/// my_skip: 1,
226/// }.to_row();
227/// ```
228#[proc_macro_derive(ToRow, attributes(col))]
229pub fn derive_to_row(input: TokenStream) -> TokenStream {
230 let input = parse_macro_input!(input as DeriveInput);
231 let output = derive_to_row_impl(input);
232 output.unwrap_or_else(|e| e.to_compile_error()).into()
233}
234
235/// Derive macro to convert a struct to a row with move semantics.
236///
237/// # Example
238/// ```rust, ignore
239/// use api::v1::Row;
240/// use api::v1::value::ValueData;
241/// use api::v1::Value;
242///
243/// #[derive(IntoRow)]
244/// struct IntoRowTest {
245/// my_value: i32,
246/// #[col(name = "string_value", datatype = "string", semantic = "tag")]
247/// my_string: String,
248/// my_bool: bool,
249/// my_float: f32,
250/// #[col(
251/// name = "timestamp_value",
252/// semantic = "Timestamp",
253/// datatype = "TimestampMillisecond"
254/// )]
255/// my_timestamp: i64,
256/// #[col(skip)]
257/// my_skip: i32,
258/// }
259///
260/// let row = IntoRowTest {
261/// my_value: 1,
262/// my_string: "test".to_string(),
263/// my_bool: true,
264/// my_float: 1.0,
265/// my_timestamp: 1718563200000,
266/// my_skip: 1,
267/// }.into_row();
268/// ```
269#[proc_macro_derive(IntoRow, attributes(col))]
270pub fn derive_into_row(input: TokenStream) -> TokenStream {
271 let input = parse_macro_input!(input as DeriveInput);
272 let output = derive_into_row_impl(input);
273 output.unwrap_or_else(|e| e.to_compile_error()).into()
274}
275
276/// Derive macro to convert a struct to a schema.
277///
278/// # Example
279/// ```rust, ignore
280/// use api::v1::ColumnSchema;
281///
282/// #[derive(Schema)]
283/// struct SchemaTest {
284/// my_value: i32,
285/// #[col(name = "string_value", datatype = "string", semantic = "tag")]
286/// my_string: String,
287/// my_bool: bool,
288/// my_float: f32,
289/// #[col(
290/// name = "timestamp_value",
291/// semantic = "Timestamp",
292/// datatype = "TimestampMillisecond"
293/// )]
294/// my_timestamp: i64,
295/// #[col(skip)]
296/// my_skip: i32,
297/// }
298///
299/// let schema = SchemaTest::schema();
300/// ```
301#[proc_macro_derive(Schema, attributes(col))]
302pub fn derive_schema(input: TokenStream) -> TokenStream {
303 let input = parse_macro_input!(input as DeriveInput);
304 let output = derive_schema_impl(input);
305 output.unwrap_or_else(|e| e.to_compile_error()).into()
306}