udf/
lib.rs

1//! A wrapper crate to make writing SQL user-defined functions (UDFs) easy
2//!
3//! This crate provides bindings for easy creation of SQL user-defined functions
4//! in Rust. See [the
5//! readme](https://github.com/pluots/sql-udf/blob/main/README.md) for more
6//! background information on how UDFs work in general.
7//!
8//! # Usage
9//!
10//! Using this crate is fairly simple: create a struct that will be used to
11//! share data among UDF function calls (which can be zero-sized), then
12//! implement needed traits for it. [`BasicUdf`] provides function signatures
13//! for standard UDFs, and [`AggregateUdf`] provides signatures for aggregate
14//! (and window) UDFs. See the documentation there for a step-by-step guide.
15//!
16//! ```
17//! use udf::prelude::*;
18//!
19//! // Our struct that will produce a UDF of name `my_udf`
20//! // If there is no data to store between calls, it can be zero sized
21//! struct MyUdf;
22//!
23//! // Specifying a name is optional; `#[register]` uses a snake case version of
24//! // the struct name by default (`my_udf` in this case)
25//! #[register(name = "my_shiny_udf")]
26//! impl BasicUdf for MyUdf {
27//!     // Specify return type of this UDF to be a nullable integer
28//!     type Returns<'a> = Option<i64>;
29//!
30//!     // Perform initialization steps here
31//!     fn init(cfg: &UdfCfg<Init>, args: &ArgList<Init>) -> Result<Self, String> {
32//!         todo!();
33//!     }
34//!
35//!     // Create a result here
36//!     fn process<'a>(
37//!         &'a mut self,
38//!         cfg: &UdfCfg<Process>,
39//!         args: &ArgList<Process>,
40//!         error: Option<NonZeroU8>,
41//!     ) -> Result<Self::Returns<'a>, ProcessError> {
42//!         todo!();
43//!     }
44//! }
45//! ```
46//!
47//! # Building & Usage
48//!
49//! The above example will create three C-callable functions: `my_udf`,
50//! `my_udf_init`, and `my_udf_deinit`, which is what `MariaDB` and `MySql`
51//! expect for UDFs. To create a C dynamic library (as is required for usage),
52//! add the following to your `Cargo.toml`
53//!
54//! ```toml
55//! [lib]
56//! crate-type = ["cdylib"]
57//! ```
58//!
59//! The next time you run `cargo build --release`, in `target/release` there
60//! will be a shared library `.so` file. Copy this to your `plugin_dir` location
61//! (usually `/usr/lib/mysql/plugin/`), and load the function with the
62//! following:
63//!
64//! ```sql
65//! CREATE FUNCTION my_udf RETURNS integer SONAME 'libudf_test.so';
66//! ```
67//!
68//! Replace `my_udf` with the function name, `integer` with the return type, and
69//! `libudf_test.so` with the correct file name.
70//!
71//! More details on building are discussed in [the project
72//! readme](https://github.com/pluots/sql-udf/blob/main/README.md). See [the
73//! `MariaDB` documentation](https://mariadb.com/kb/en/create-function-udf/) for
74//! more detailed information on how to load the created libraries.
75//!
76//! # Crate Features
77//!
78//! This crate includes some optional features. They can be enabled in your
79//! `Cargo.toml`.
80//!
81//! - `mock`: enable this feature to add the [mock] module for easier unit
82//!   testing. _(note: this feature will become unneeded in the future and
83//!   `mock` will be available by default. It is currently feature-gated because
84//!   it is considered unstable.)_
85//! - `logging-debug`: enable this feature to turn on debug level logging for
86//!   this crate. This uses the `udf_log!` macro and includes information about
87//!   memory management and function calls. These will show up with your SQL
88//!   server logs, like:
89//!
90//!   ```text
91//!   2023-03-23 00:45:53+00:00 [Debug] UDF: ENTER init for 'udf_examples::lookup::Lookup6'
92//!   2023-03-23 00:45:53+00:00 [Debug] UDF: 0x7fdea4022220 24 bytes udf->server control transfer
93//!                                          (BufConverter<Lookup6, Option<String>>)
94//!   2023-03-23 00:45:53+00:00 [Debug] UDF: EXIT init for 'udf_examples::lookup::Lookup6'
95//!   2023-03-23 00:45:53+00:00 [Debug] UDF: ENTER process for 'udf_examples::lookup::Lookup6'
96//!   2023-03-23 00:45:53+00:00 [Debug] UDF: 0x7fdea4022220 24 bytes server->udf control transfer
97//!                                          (BufConverter<Lookup6, Option<String>>)
98//!   2023-03-23 00:45:53+00:00 [Debug] UDF: 0x7fdea4022220 24 bytes udf->server control transfer
99//!                                          (BufConverter<Lookup6, Option<String>>)
100//!   2023-03-23 00:45:53+00:00 [Debug] UDF: EXIT process for 'udf_examples::lookup::Lookup6'
101//!   2023-03-23 00:45:53+00:00 [Debug] UDF: ENTER deinit for 'udf_examples::lookup::Lookup6'
102//!   2023-03-23 00:45:53+00:00 [Debug] UDF: 0x7fdea4022220 24 bytes server->udf control transfer
103//!                                          (BufConverter<Lookup6, Option<String>>)
104//!   ```
105//!
106//!   This output can be helpful to understand the exact data flow between
107//!   the library and the server. They are enabled by default in the `udf-examples`
108//!   library.
109//!
110//! - `logging-debug-calls` full debugging printing of the structs passed
111//!   between this library and the SQL server. Implies `logging-debug`. This
112//!   output can be noisy, but can help to debug issues related to the lower
113//!   level interfaces (i.e. problems with this library or with the server
114//!   itself).
115//!
116//! # Version Note
117//!
118//! Because of reliance on a feature called GATs, this library requires Rust
119//! version >= 1.65. This only became stable on 2022-11-03; if you encounter
120//! issues compiling, be sure to update your toolchain.
121
122// Strict clippy
123#![warn(
124    clippy::pedantic,
125    // clippy::cargo,
126    clippy::nursery,
127    clippy::str_to_string,
128    clippy::exhaustive_enums,
129    clippy::pattern_type_mismatch
130)]
131// Pedantic config
132#![allow(
133    clippy::missing_const_for_fn,
134    // clippy::missing_panics_doc,
135    clippy::must_use_candidate,
136    clippy::cast_possible_truncation
137)]
138
139// We re-export this so we can use it in our macro, but don't need it
140// to show up in our docs
141#[doc(hidden)]
142pub extern crate chrono;
143
144#[doc(hidden)]
145pub extern crate udf_sys;
146
147extern crate udf_macros;
148
149pub use udf_macros::register;
150
151#[macro_use]
152mod macros;
153pub mod prelude;
154pub mod traits;
155pub mod types;
156
157// We hide this because it's really only used by our proc macros
158#[doc(hidden)]
159pub mod wrapper;
160
161#[doc(inline)]
162pub use traits::*;
163#[doc(inline)]
164pub use types::{MYSQL_ERRMSG_SIZE, *};
165
166pub mod mock;