#[proc]
Expand description
#[tarantool::proc]
is a macro attribute for creating stored procedure
functions.
#[tarantool::proc]
fn add(x: i32, y: i32) -> i32 {
x + y
}
Create a “C” stored procedure from Tarantool and call it with arguments wrapped within a Lua table:
box.schema.func.create("libname.add", { language = 'C' })
assert(box.func['libname.add']:call({ 1, 2 }) == 3)
Returning errors
Assuming the function’s return type is Result
<T, E>
(where E
implements
Display
), the return values read as follows:
Ok(v)
: the stored procedure will returnv
Err(e)
: the stored procedure will fail ande
will be set as the last Tarantool error (see alsoTarantoolError::last
)
use tarantool::{error::Error, index::IteratorType::Eq, space::Space};
#[tarantool::proc]
fn get_name(id: usize) -> Result<Option<String>, Error> {
Ok(
if let Some(space) = Space::find("users") {
if let Some(row) = space.select(Eq, &[id])?.next() {
row.get("name")
} else {
None
}
} else {
None
}
)
}
Returning custom types
The return type of the stored procedure must implement the Return
trait which is
implemented for most built-in types. To return an arbitrary type that
implements serde::Serialize
you can use the ReturnMsgpack
wrapper
type or the custom_ret
attribute parameter.
#[derive(serde::Serialize)]
struct Complex {
re: f64,
im: f64,
}
#[tarantool::proc(custom_ret)]
fn sqrt(x: f64) -> Complex {
if x < 0. {
Complex { re: 0., im: x.abs().sqrt() }
} else {
Complex { re: x.sqrt(), im: 0. }
}
}
// above is equivalent to this
use tarantool::proc::ReturnMsgpack;
#[tarantool::proc]
fn sqrt_explicit(x: f64) -> ReturnMsgpack<Complex> {
ReturnMsgpack(
if x < 0. {
Complex { re: 0., im: x.abs().sqrt() }
} else {
Complex { re: x.sqrt(), im: 0. }
}
)
}
Packed arguments
By default the stored procedure unpacks the received tuple and assigns the ith field of the tuple to the ith argument. If there are fewer arguments than there are fields in the input tuple, the unused tuple fields are ignored.
If you want to instead deserialize the tuple directly into your structure
you can use the packed_args
attribute parameter
#[tarantool::proc(packed_args)]
fn sum_all(vals: Vec<i32>) -> i32 {
vals.into_iter().sum()
}
#[tarantool::proc]
fn sum_first_3(a: i32, b: i32, c: i32) -> i32 {
a + b + c
}
In the above example sum_all
will sum all the inputs values it received
whereas sum_first_3
will only sum up the first 3 values
Injecting arguments
Because the return value of the stored procedure is immediately serialized it is in theory ok to return borrowed values. Rust however will not allow you to return references to the values owned by the function. In that case you can use an injected argument, which will be created just outside the stored procedure and will be passed to it as a corresponding argument.
fn global_data() -> &'static [String] {
todo!()
}
#[tarantool::proc]
fn get_ith<'a>(
#[inject(global_data())]
data: &'a [String],
i: usize,
) -> &'a str {
&data[i]
}
When calling the stored procedure only the actual arguments need to be
specified, so in the above example get_ith
will effectively have just 1
argument i
. And data
will be automatically injected and it’s value will
be set to global_data()
each time it is called.
Debugging
There’s also a debug
attribute parameter which enables debug printing of
the arguments received by the stored procedure
#[tarantool::proc(debug)]
fn print_what_you_got() {}
The above stored procedure will just print it’s any of it’s arguments to stderr and return immediately.