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;