caco3_web/
macros.rs

1/// Measure given expression usage time.
2///
3/// If expression is early return, Time will not be measured.
4/// It occur when we use `?` in expression.
5/// Some kind of that expression can be measured by moving `?` out of expression.
6///
7/// For example
8/// ```ignore
9/// // This is async method that return Result
10/// async fn get() -> Result<(), ()> { ... }
11///
12/// async fn main() -> Result<(), ()> {
13///     const TAG: &'static str = "Get something";
14///     // don't do this, if error occur, measurement log is not emit.
15///     measure_time!(TAG, get().await?);
16///     // change to this instead.
17///     measure_time!(TAG, get().await)?;
18/// }
19/// ```
20///
21/// NOTE:
22/// * Use `;` as a unit separator cause rustfmt at call site not working properly.
23/// * `$tag` can be anything that implement `std::fmt::Display`.
24#[macro_export]
25macro_rules! measure_time {
26    // Custom unit implementation
27    (@unit [$unit:literal, $as_unit:ident]; $tag:expr, $expr:expr) => {
28        {
29            let start = ::std::time::Instant::now();
30            let value = $expr;
31            $crate::re::tracing::debug!(
32                ::core::concat!("{} in {} ", $unit),
33                $tag,
34                start.elapsed().$as_unit(),
35            );
36            value
37        }
38    };
39    // Auto unit implementation
40    (@auto $tag:expr, $expr:expr) => {
41        {
42            let start = ::std::time::Instant::now();
43            let value = $expr;
44            $crate::re::tracing::debug!(
45                "{} in {}",
46                $tag,
47                $crate::_macro_support::AutoUnitDuration::from(start),
48            );
49            value
50        }
51    };
52    // We usually use this variant
53    ($tag:expr, $expr:expr) => { $crate::measure_time!(@auto $tag, $expr) };
54    // Use following variants when custom unit is desire
55    (MILLI, $tag:expr, $expr:expr) => { $crate::measure_time!(@unit ["ms", as_millis]; $tag, $expr) };
56    (MICRO, $tag:expr, $expr:expr) => { $crate::measure_time!(@unit ["µs", as_micros]; $tag, $expr) };
57    (NANO,  $tag:expr, $expr:expr) => { $crate::measure_time!(@unit ["ns", as_nanos];  $tag, $expr) };
58    (SEC,   $tag:expr, $expr:expr) => { $crate::measure_time!(@unit ["s",  as_secs];   $tag, $expr) };
59}
60
61/// Generate database access layer method on given struct.
62///
63/// This helper macro avoid boilerplate when implement database access layer.
64/// For complex sql operation, one should implement it manually.
65///
66/// ```ignore
67/// // Example usage
68/// #[derive(sqlx::FromRow)]
69/// struct Account {
70///     id: i64,
71///     name: String,
72///     surname: String,
73///     active: bool,
74/// }
75///
76/// // Prepared statement arguments
77/// struct FindAccount {
78///     id: i64,
79///     active: bool,
80/// }
81///
82/// impl FindAccount {
83///     const FETCH_SQL: &'static str = "select * from accounts where id = ?, and active = ?";
84///
85///    // Use case 1, Get one record from query result.
86///    // -- source --
87///    postgres_query! {
88///        fetch_one(FindAccount::FETCH_SQL) -> Account,
89///        pub async fn get {
90///            id,
91///        }
92///    }
93///    // -- expanded --
94///    pub async fn get<'c, E>(&self, executor: E) -> sqlx::Result<Account>
95///    where
96///        E: sqlx::Executor<'c, Database = sqlx::Postgres>,
97///    {
98///        sqlx::query_as(FindAccount::FETCH_SQL)
99///            .bind(&self.id)
100///            .fetch_one(executor)
101///            .await
102///    }
103///
104///
105///     // Use case 2, Find one record from query result.
106///     // -- source --
107///     postgres_query! {
108///         fetch_optional(FindAccount::FETCH_SQL) -> Account,
109///         pub async fn find {
110///             id,
111///             active,
112///         }
113///     }
114///     // -- expanded --
115///     pub async fn find<'c, E>(
116///         &self,
117///         executor: E,
118///     ) -> sqlx::Result<Option<Account>>
119///     where
120///         E: sqlx::Executor<'c, Database = sqlx::Postgres>,
121///     {
122///         sqlx::query_as(FindAccount::FETCH_SQL)
123///             .bind(&self.id)
124///             .bind(&self.active)
125///             .fetch_optional(executor)
126///             .await
127///     }
128///
129///
130///     // Use case 3, Get all records from query result.
131///     // -- source --
132///     postgres_query! {
133///         fetch_all(FindAccount::FETCH_SQL) -> Account,
134///         pub async fn list {
135///             id,
136///             active,
137///         }
138///     }
139///     // -- expanded --
140///     pub async fn list<'c, E>(
141///         &self,
142///         executor: E,
143///     ) -> sqlx::Result<Vec<Account>>
144///     where
145///         E: sqlx::Executor<'c, Database = sqlx::Postgres>,
146///     {
147///         sqlx::query_as(FindAccount::FETCH_SQL)
148///             .bind(&self.id)
149///             .bind(&self.active)
150///             .fetch_all(executor)
151///             .await
152///     }
153/// }
154/// ```
155#[macro_export]
156macro_rules! postgres_query {
157    // Hide distracting implementation details from the generated rustdoc.
158    ($($body:tt)+) => {
159        $crate::postgres_query_internal! {$($body)+}
160    };
161}
162
163#[doc(hidden)]
164#[macro_export]
165macro_rules! postgres_query_internal {
166    // internal rules
167    (
168        @query_impl
169        ($query_fn:ident, $execute_fn:ident -> $from_row:ty),
170        $sql:expr,
171        $(#[$fn_meta:meta])*
172        $fn_vis:vis async fn $fn_name:ident ($($field:tt),* $(,)?)
173    ) => {
174        $(#[$fn_meta])*
175        $fn_vis async fn $fn_name<'c, E>(&self, executor: E) -> ::sqlx::Result<$from_row>
176        where
177            E: ::sqlx::Executor<'c, Database = ::sqlx::Postgres>,
178        {
179            use ::std::sync::OnceLock;
180            use $crate::sql::SqlTrimBoxed;
181
182            // we choose this name to avoid shadowing outer SQL (if exist)
183            static __BOXED_QUERY__: OnceLock<Box<str>> = OnceLock::new();
184
185            ::sqlx::$query_fn(
186                    &**__BOXED_QUERY__.get_or_init(|| {
187                        $sql.sql_trim_boxed()
188                    })
189                )
190                $(.bind(&self.$field))*
191                .$execute_fn(executor)
192                .await
193        }
194    };
195    // support named struct
196    (
197        @query
198        ($query_fn:ident, $execute_fn:ident -> $from_row:ty),
199        $sql:expr,
200        $(#[$fn_meta:meta])*
201        $fn_vis:vis async fn $fn_name:ident {$($field:ident),* $(,)?}
202    ) => {
203        $crate::postgres_query_internal! {
204            @query_impl
205            ($query_fn, $execute_fn -> $from_row),
206            $sql,
207            $(#[$fn_meta])*
208            $fn_vis async fn $fn_name ($($field),*)
209        }
210    };
211    // support tuple struct
212    (
213        @query
214        ($query_fn:ident, $execute_fn:ident -> $from_row:ty),
215        $sql:expr,
216        $(#[$fn_meta:meta])*
217        $fn_vis:vis async fn $fn_name:ident ($($field:tt),* $(,)?)
218    ) => {
219        $crate::postgres_query_internal! {
220            @query_impl
221            ($query_fn, $execute_fn -> $from_row),
222            $sql,
223            $(#[$fn_meta])*
224            $fn_vis async fn $fn_name ($($field),*)
225        }
226    };
227    // get one row
228    (
229        fetch_one($sql:expr) -> $from_row:ty,
230        $($fn_spec:tt)*
231    ) => {
232        $crate::postgres_query_internal! {
233            @query
234            (query_as, fetch_one -> $from_row),
235            $sql,
236            $($fn_spec)*
237        }
238    };
239    // get one row with single column
240    (
241        fetch_one_scalar($sql:expr) -> $from_row:ty,
242        $($fn_spec:tt)*
243    ) => {
244        $crate::postgres_query_internal! {
245            @query
246            (query_scalar, fetch_one -> $from_row),
247            $sql,
248            $($fn_spec)*
249        }
250    };
251    // find one row
252    (
253        fetch_optional($sql:expr) -> $from_row:ty,
254        $($fn_spec:tt)*
255    ) => {
256        $crate::postgres_query_internal! {
257            @query
258            (query_as, fetch_optional -> ::std::option::Option<$from_row>),
259            $sql,
260            $($fn_spec)*
261        }
262    };
263    // find one row with single column
264    (
265        fetch_optional_scalar($sql:expr) -> $from_row:ty,
266        $($fn_spec:tt)*
267    ) => {
268        $crate::postgres_query_internal! {
269            @query
270            (query_scalar, fetch_optional -> ::std::option::Option<$from_row>),
271            $sql,
272            $($fn_spec)*
273        }
274    };
275    // fetch all
276    (
277        fetch_all($sql:expr) -> $from_row:ty,
278        $($fn_spec:tt)*
279    ) => {
280        $crate::postgres_query_internal! {
281            @query
282            (query_as, fetch_all -> ::std::vec::Vec<$from_row>),
283            $sql,
284            $($fn_spec)*
285        }
286    };
287    // execute
288    (
289        execute($sql:expr),
290        $($fn_spec:tt)*
291    ) => {
292        $crate::postgres_query_internal! {
293            @query
294            (query, execute -> ::sqlx::postgres::PgQueryResult),
295            $sql,
296            $($fn_spec)*
297        }
298    };
299}
300
301#[macro_export]
302macro_rules! sqlite_query {
303    // Hide distracting implementation details from the generated rustdoc.
304    ($($body:tt)+) => {
305        $crate::sqlite_query_internal! {$($body)+}
306    };
307}
308
309#[doc(hidden)]
310#[macro_export]
311macro_rules! sqlite_query_internal {
312    // internal rules
313    (
314        @query_impl
315        ($query_fn:ident, $execute_fn:ident -> $entity:ty),
316        $sql:expr,
317        $(#[$fn_meta:meta])*
318        $fn_vis:vis async fn $fn_name:ident ($($field:tt),* $(,)?)
319    ) => {
320        $(#[$fn_meta])*
321        $fn_vis async fn $fn_name<'c, E>(&self, executor: E) -> ::sqlx::Result<$entity>
322        where
323            E: ::sqlx::Executor<'c, Database = ::sqlx::Sqlite>,
324        {
325            use ::std::sync::OnceLock;
326            use $crate::sql::SqlTrimBoxed;
327
328            // we choose this name to avoid shadowing outer SQL (if exist)
329            static __BOXED_QUERY__: OnceLock<Box<str>> = OnceLock::new();
330            ::sqlx::$query_fn(&**__BOXED_QUERY__.get_or_init(|| {
331                        $sql.sql_trim_boxed()
332                    })
333                )
334                $(.bind(&self.$field))*
335                .$execute_fn(executor)
336                .await
337        }
338    };
339    // support named struct
340    (
341        @query
342        ($query_fn:ident, $execute_fn:ident -> $entity:ty),
343        $sql:expr,
344        $(#[$fn_meta:meta])*
345        $fn_vis:vis async fn $fn_name:ident {$($field:ident),* $(,)?}
346    ) => {
347        $crate::sqlite_query_internal! {
348            @query_impl
349            ($query_fn, $execute_fn -> $entity),
350            $sql,
351            $(#[$fn_meta])*
352            $fn_vis async fn $fn_name ($($field),*)
353        }
354    };
355    // support tuple struct
356    (
357        @query
358        ($query_fn:ident, $execute_fn:ident -> $entity:ty),
359        $sql:expr,
360        $(#[$fn_meta:meta])*
361        $fn_vis:vis async fn $fn_name:ident ($($field:tt),* $(,)?)
362    ) => {
363        $crate::sqlite_query_internal! {
364            @query_impl
365            ($query_fn, $execute_fn -> $entity),
366            $sql,
367            $(#[$fn_meta])*
368            $fn_vis async fn $fn_name ($($field),*)
369        }
370    };
371    // get one entity
372    (
373        get($sql:expr) -> $entity:ty,
374        $($fn_spec:tt)*
375    ) => {
376        $crate::sqlite_query_internal! {
377            @query
378            (query_as, fetch_one -> $entity),
379            $sql,
380            $($fn_spec)*
381        }
382    };
383    // get one entity (scalar)
384    (
385        get_scalar($sql:expr) -> $entity:ty,
386        $($fn_spec:tt)*
387    ) => {
388        $crate::sqlite_query_internal! {
389            @query
390            (query_scalar, fetch_one -> $entity),
391            $sql,
392            $($fn_spec)*
393        }
394    };
395    // find one entity
396    (
397        find($sql:expr) -> $entity:ty,
398        $($fn_spec:tt)*
399    ) => {
400        $crate::sqlite_query_internal! {
401            @query
402            (query_as, fetch_optional -> ::std::option::Option<$entity>),
403            $sql,
404            $($fn_spec)*
405        }
406    };
407    // find one entity (scalar)
408    (
409        find_scalar($sql:expr) -> $entity:ty,
410        $($fn_spec:tt)*
411    ) => {
412        $crate::sqlite_query_internal! {
413            @query
414            (query_scalar, fetch_optional -> ::std::option::Option<$entity>),
415            $sql,
416            $($fn_spec)*
417        }
418    };
419    // fetch all
420    (
421        list($sql:expr) -> $entity:ty,
422        $($fn_spec:tt)*
423    ) => {
424        $crate::sqlite_query_internal! {
425            @query
426            (query_as, fetch_all -> ::std::vec::Vec<$entity>),
427            $sql,
428            $($fn_spec)*
429        }
430    };
431    // execute
432    (
433        execute($sql:expr),
434        $($fn_spec:tt)*
435    ) => {
436        $crate::sqlite_query_internal! {
437            @query
438            (query, execute -> ::sqlx::sqlite::SqliteQueryResult),
439            $sql,
440            $($fn_spec)*
441        }
442    };
443}
444
445/// Generate `builder()` method which return builder with default values.
446#[macro_export]
447macro_rules! with_builder {
448    ($builder:ty => $ty:ty) => {
449        impl $ty {
450            pub fn builder() -> $builder {
451                <$builder as ::core::default::Default>::default()
452            }
453        }
454    };
455}
456
457/// Generating function used for reading jemalloc stats.
458///
459/// Unfortunately we couldn't re-export jemalloc struct so we hard coded its path here.
460#[macro_export]
461macro_rules! generate_read_jemalloc_raw_data {
462    ($vis:vis fn $name:ident) => {
463        $vis fn $name() -> ::core::option::Option<$crate::jemalloc::info::JemallocRawData> {
464            use ::std::prelude::*;
465            use ::std::sync::OnceLock;
466
467            use ::tikv_jemalloc_ctl::{
468                arenas, background_thread, background_thread_mib, epoch, epoch_mib, max_background_threads,
469                max_background_threads_mib, stats,
470            };
471
472            use $crate::jemalloc::info::{JemallocRawData, BackgroundThread};
473
474            struct Mib {
475                epoch: epoch_mib,
476                max_background_threads: max_background_threads_mib,
477                background_thread: background_thread_mib,
478                narenas: arenas::narenas_mib,
479                active: stats::active_mib,
480                allocated: stats::allocated_mib,
481                mapped: stats::mapped_mib,
482                metadata: stats::metadata_mib,
483                resident: stats::resident_mib,
484                retained: stats::retained_mib,
485            }
486
487            fn read_background_thread(mib: &Mib) -> Option<BackgroundThread> {
488                Some(BackgroundThread {
489                    max: mib.max_background_threads.read().ok()?,
490                    enabled: mib.background_thread.read().ok()?,
491                })
492            }
493
494            fn get_mib() -> Option<&'static Mib> {
495                static MIB: OnceLock<Option<Mib>> = OnceLock::new();
496                fn init() -> Option<Mib> {
497                    let val = Mib {
498                        epoch: epoch::mib().ok()?,
499                        max_background_threads: max_background_threads::mib().ok()?,
500                        background_thread: background_thread::mib().ok()?,
501                        narenas: arenas::narenas::mib().ok()?,
502                        active: stats::active::mib().ok()?,
503                        allocated: stats::allocated::mib().ok()?,
504                        mapped: stats::mapped::mib().ok()?,
505                        metadata: stats::metadata::mib().ok()?,
506                        resident: stats::resident::mib().ok()?,
507                        retained: stats::retained::mib().ok()?,
508                    };
509                    Some(val)
510                }
511                MIB.get_or_init(init).as_ref()
512            }
513
514            let mib = get_mib()?;
515            // Many statistics are cached and only updated
516            // when the epoch is advanced:
517            mib.epoch.advance().ok()?;
518
519            let value = JemallocRawData {
520                // config
521                background_thread: read_background_thread(&mib),
522                number_of_arenas: arenas::narenas::read().ok()?,
523                // stats
524                active_bytes: stats::active::read().ok()?,
525                allocated_bytes: stats::allocated::read().ok()?,
526                mapped_bytes: stats::mapped::read().ok()?,
527                metadata_bytes: stats::metadata::read().ok()?,
528                resident_bytes: stats::resident::read().ok()?,
529                retained_bytes: stats::retained::read().ok()?,
530            };
531            Some(value)
532        }
533    };
534}