pub struct Context { /* private fields */ }
Expand description
A struct that keeps track of the current transaction and error buffer.
Since all functions in the YottaDB threaded API take a tptoken
and error_buffer
,
it can be inconvenient to keep track of them manually, especially since
Passing in a different or incorrect tptoken can result in hard-to-debug application behavior, including deadlocks. 1
This struct keeps track of them for you so you don’t have to clutter your application logic with resource management.
§See also
Context
is not thread-safe, async-safe, or re-entrant.
Example:
use yottadb::{Context, TransactionStatus, make_ckey};
let ctx = Context::new();
let mut key1 = make_ckey!(ctx, "key1");
let mut key2 = make_ckey!(ctx, "key2");
tokio::spawn(async {
// error[E0277]: `dyn std::error::Error` cannot be sent between threads safely
ctx.tp(|_| Ok(TransactionStatus::Ok), "BATCH", &[])
});
Implementations§
Source§impl Context
Call-in functions
impl Context
Call-in functions
Sourcepub fn ci_tab_open(&self, file: &CStr) -> YDBResult<CallInTableDescriptor>
pub fn ci_tab_open(&self, file: &CStr) -> YDBResult<CallInTableDescriptor>
Open the call-in table stored in file
and return its file descriptor.
You can later switch the active call-in table by calling ci_tab_switch
with the file descriptor.
§See also
§Errors
- a negative error return code (for example, if the call-in table in the file had parse errors).
§Example
use std::ffi::CString;
use yottadb::Context;
let ctx = Context::new();
let file = CString::new("examples/m-ffi/calltab.ci").unwrap();
let descriptor = ctx.ci_tab_open(&file)?;
Sourcepub fn ci_tab_switch(
&self,
new_handle: CallInTableDescriptor,
) -> YDBResult<CallInTableDescriptor>
pub fn ci_tab_switch( &self, new_handle: CallInTableDescriptor, ) -> YDBResult<CallInTableDescriptor>
Switch the active call-in table to new_handle
. Returns the previously active table.
new_handle
is a file descriptor returned by ci_tab_open
.
§Errors
§Example
use std::ffi::CString;
use yottadb::Context;
let ctx = Context::new();
let file = CString::new("examples/m-ffi/calltab.ci").unwrap();
let descriptor = ctx.ci_tab_open(&file)?;
let old_ci_table = ctx.ci_tab_switch(descriptor)?;
Source§impl Context
impl Context
Sourcepub fn new_key<K: Into<Key>>(&self, key: K) -> KeyContext
pub fn new_key<K: Into<Key>>(&self, key: K) -> KeyContext
Create a KeyContext
from this Context
.
§See also
Sourcepub fn tptoken(&self) -> TpToken
pub fn tptoken(&self) -> TpToken
Return the token for the transaction associated with this Context
.
This allows calling yottadb functions in the craw
API that have not yet been wrapped
and require a tptoken from inside a transaction.
§Example
tptoken()
can be used to call M FFI from within a transaction:
use std::env;
use std::ffi::CStr;
use yottadb::{ci_t, Context, TransactionStatus, YDB_NOTTP};
env::set_var("ydb_routines", "examples/m-ffi");
env::set_var("ydb_ci", "examples/m-ffi/calltab.ci");
let ctx = Context::new();
ctx.tp(|ctx| {
let tptoken = ctx.tptoken();
assert_ne!(tptoken, YDB_NOTTP);
let mut routine = CStr::from_bytes_with_nul(b"noop\0").unwrap();
unsafe { ci_t!(tptoken, Vec::new(), routine)?; }
Ok(TransactionStatus::Ok)
}, "BATCH", &[]).unwrap();
§See also
Sourcepub fn tp<'a, F>(
&'a self,
f: F,
trans_id: &str,
locals_to_reset: &[&str],
) -> Result<(), Box<dyn Error + Send + Sync>>
pub fn tp<'a, F>( &'a self, f: F, trans_id: &str, locals_to_reset: &[&str], ) -> Result<(), Box<dyn Error + Send + Sync>>
Start a new transaction, where f
is the transaction to execute.
tp
stands for ‘transaction processing’.
The parameter trans_id
is the name logged for the transaction.
If trans_id
has the special value "BATCH"
, durability is not enforced by YottaDB.
See the C documentation for details.
The argument passed to f
is a transaction processing token.
§Rollbacks and Restarts
Application code can return a TransactionStatus
in order to rollback or restart.
tp_st
behaves as follows:
- If
f
panics, the transaction is rolled back and the panic resumes afterwards. - If
f
returnsOk(TransactionStatus)
, the transaction will have the behavior documented underTransactionStatus
(commit, restart, and rollback, respectively). - If
f
returns anErr(YDBError)
, the status from that error will be returned to the YottaDB engine. As a result, if the status for theYDBError
isYDB_TP_RESTART
, the transaction will be restarted. Otherwise, the transaction will be rolled back and the error returned fromtp_st
. - If
f
returns any otherErr
variant, the transaction will be rolled back and the error returned fromtp_st
.
f
must be FnMut
, not FnOnce
, since the YottaDB engine may
call f
many times if necessary to ensure ACID properties.
This may affect your application logic; if you need to know how many
times the callback has been executed, get the intrinsic variable
$trestart
.
§Errors
- YDB_ERR_TPTIMEOUT - The transaction took more than
$zmaxtptime
seconds to execute, where$zmaxtptime
is an intrinsic special variable. - YDB_TP_ROLLBACK — application logic indicates that the transaction should not be committed.
- A
YDBError
returned by a YottaDB function called byf
. - Another arbitrary error returned by
f
.
§Examples
Rollback a transaction if an operation fails:
use yottadb::{Context, KeyContext, TpToken, TransactionStatus};
let ctx = Context::new();
let var = KeyContext::variable(&ctx, "tpRollbackTest");
var.set("initial value")?;
println!("starting tp");
let maybe_err = ctx.tp(|ctx| {
println!("in tp");
fallible_operation()?;
println!("succeeded");
var.set("new value")?;
Ok(TransactionStatus::Ok)
}, "BATCH", &[]);
let expected_val: &[_] = if maybe_err.is_ok() {
b"new value"
} else {
b"initial value"
};
assert_eq!(var.get()?, expected_val);
fn fallible_operation() -> Result<(), &'static str> {
if rand::random() {
Ok(())
} else {
Err("the operation failed")
}
}
Retry a transaction until it succeeds:
use yottadb::{Context, TpToken, TransactionStatus};
let ctx = Context::new();
ctx.tp(|tptoken| {
if fallible_operation().is_ok() {
Ok(TransactionStatus::Ok)
} else {
Ok(TransactionStatus::Restart)
}
}, "BATCH", &[]).unwrap();
fn fallible_operation() -> Result<(), ()> {
if rand::random() {
Ok(())
} else {
Err(())
}
}
§See Also
Sourcepub fn delete_excl(&self, saved_variables: &[&str]) -> YDBResult<()>
pub fn delete_excl(&self, saved_variables: &[&str]) -> YDBResult<()>
Delete all local variables except for those passed in saved_variable
.
Passing an empty saved_variables
slice deletes all local variables.
Attempting to save a global or intrinsic variable is an error.
§Errors
- YDB_ERR_NAMECOUNT2HI if
saved_variables.len() > YDB_MAX_NAMES
- YDB_ERR_INVVARNAME if attempting to save a global or intrinsic variable
- Another system error return code
§Examples
use yottadb::{Context, KeyContext, TpToken, YDB_ERR_LVUNDEF};
// Create three variables and set all
let ctx = Context::new();
let a = KeyContext::variable(&ctx, "deleteExclTestA");
a.set("test data")?;
let b = KeyContext::variable(&ctx, "deleteExclTestB");
b.set("test data 2")?;
let c = KeyContext::variable(&ctx, "deleteExclTestC");
c.set("test data 3")?;
// Delete all variables except `a`
ctx.delete_excl(&[&a.variable])?;
assert_eq!(a.get()?, b"test data");
assert_eq!(b.get().unwrap_err().status, YDB_ERR_LVUNDEF);
assert_eq!(c.get().unwrap_err().status, YDB_ERR_LVUNDEF);
// Delete `a` too
ctx.delete_excl(&[])?;
assert_eq!(a.get().unwrap_err().status, YDB_ERR_LVUNDEF);
§See also
Sourcepub fn eintr_handler(&self) -> YDBResult<()>
pub fn eintr_handler(&self) -> YDBResult<()>
Runs the YottaDB deferred signal handler (if necessary).
This function must be called if an application has a tight loop inside a transaction which never calls a YDB function.
§See also
Sourcepub fn str2zwr(&self, original: &[u8]) -> YDBResult<Vec<u8>>
pub fn str2zwr(&self, original: &[u8]) -> YDBResult<Vec<u8>>
Given a binary sequence, serialize it to ‘Zwrite format’, which is ASCII printable.
§Errors
- If YDB is in UTF8 mode, will return
BADCHAR
on invalid UTF8. - Another error code
§Examples
When ydb_chset=UTF-8
is set, this will preserve UTF-8 characters:
use yottadb::Context;
let ctx = Context::new();
let str2zwr = ctx.str2zwr("💖".as_bytes())?;
if std::env::var("ydb_chset").as_deref() == Ok("UTF-8") {
assert_eq!(str2zwr, "\"💖\"".as_bytes());
} else {
// Note: The "$C" below cannot be expanded to "$CH" or "$CHAR" as that is the output returned by "str2zwr()" in M mode.
assert_eq!(str2zwr, b"\"\xf0\"_$C(159,146,150)");
}
When the input is invalid UTF-8, it will use the more verbose Zwrite format:
use yottadb::Context;
let ctx = Context::new();
let input = b"\xff";
assert!(std::str::from_utf8(input).is_err());
if std::env::var("ydb_chset").as_deref() == Ok("UTF-8") {
assert_eq!(ctx.str2zwr(input)?, b"$ZCH(255)");
} else {
assert_eq!(ctx.str2zwr(input)?, b"$C(255)");
}
§See also
- Zwrite format
zwr2str
, which deserializes a buffer in Zwrite format back to the original binary.
Sourcepub fn zwr2str(&self, serialized: &[u8]) -> Result<Vec<u8>, YDBError>
pub fn zwr2str(&self, serialized: &[u8]) -> Result<Vec<u8>, YDBError>
Given a buffer in ‘Zwrite format’, deserialize it to the original binary buffer.
zwr2str_st
writes directly to out_buf
to avoid returning multiple output buffers.
§Errors
This function returns an empty array if serialized
is not in Zwrite format.
It can also return another error code.
§Examples
use yottadb::Context;
let ctx = Context::new();
// Use "$ZCH" (instead of "$C") below as that will work in both M and UTF-8 modes (of "ydb_chset" env var)
// Note: Cannot use "$ZCHAR" below as "$ZCH" is the only input format recognized by "zwr2str()".
let out_buf = ctx.zwr2str(b"\"\xf0\"_$ZCH(159,146,150)")?;
assert_eq!(out_buf.as_slice(), "💖".as_bytes());
§See also
- Zwrite format
- str2zwr, the inverse of
zwr2str
.
Sourcepub fn lock(&self, timeout: Duration, locks: &[Key]) -> YDBResult<()>
pub fn lock(&self, timeout: Duration, locks: &[Key]) -> YDBResult<()>
Acquires locks specified in locks
and releases all others.
This operation is atomic. If any lock cannot be acquired, all locks are released.
The timeout
specifies the maximum time to wait before returning an error.
If no locks are specified, all locks are released.
Note that YottaDB locks are per-process, not per-thread.
§Limitations
For implementation reasons, there is a hard limit to the number of Key
s that can be passed in locks
:
- 64-bit: 10
Key
s - 32-bit: 9
Key
s
If more than this number of keys are passed, lock_st
will return YDB_ERR_MAXARGCNT
.
For implementation reasons, lock_st
only works on 64-bit platforms, or on 32-bit ARM.
lock_st
will not be compiled on 16, 8, or 128 bit platforms
(i.e. will fail with ‘cannot find function lock_st
in module yottadb::simple_api
’).
On non-ARM 32-bit platforms, the compiler will allow lock_st
to be called,
but it will have unspecified behavior and has not been tested.
Use KeyContext::lock_incr
and KeyContext::lock_decr
instead.
§Errors
Possible errors for this function include:
YDB_LOCK_TIMEOUT
if all locks could not be acquired within the timeout period. In this case, no locks are acquired.YDB_ERR_TIME2LONG
iftimeout
is greater thanYDB_MAX_TIME_NSEC
YDB_ERR_MAXARGCNT
if too many locks have been passed (see Limitations)- error return codes
§Examples
use std::slice;
use std::time::Duration;
use yottadb::{Context, KeyContext, Key, TpToken};
// You can use either a `Key` or a `KeyContext` to acquire a lock.
// This uses a `KeyContext` to show that you need to use `.key` to get the inner `Key`.
let ctx = Context::new();
let a = KeyContext::variable(&ctx, "lockA");
// Acquire a new lock
// using `from_ref` here allows us to use `a` later without moving it
ctx.lock(Duration::from_secs(1), slice::from_ref(&a.key)).unwrap();
// Acquire multiple locks
let locks = vec![a.key, Key::variable("lockB")];
ctx.lock(Duration::from_secs(1), &locks).unwrap();
// Release all locks
ctx.lock(Duration::from_secs(1), &[]).unwrap();
§See also
Source§impl Context
Utility functions
impl Context
Utility functions
Sourcepub fn message(&self, status: i32) -> YDBResult<Vec<u8>>
pub fn message(&self, status: i32) -> YDBResult<Vec<u8>>
Return the message corresponding to a YottaDB error code
§Errors
YDB_ERR_UNKNOWNSYSERR
ifstatus
is an unrecognized status code
§See also
impl Display for YDBError
, which should meet most use cases formessage_t
.- Function return codes
- ZMessage codes
- The C documentation
§Example
Look up the error message for an undefined local variable:
use yottadb::{Context, KeyContext, TpToken, YDB_ERR_LVUNDEF};
let ctx = Context::new();
let key = KeyContext::variable(&ctx, "oopsNotDefined");
let err = key.get().unwrap_err();
assert_eq!(err.status, YDB_ERR_LVUNDEF);
let buf = ctx.message(err.status).unwrap();
let msg = String::from_utf8(buf).unwrap();
assert!(msg.contains("Undefined local variable"));
Sourcepub fn release(&self) -> YDBResult<String>
pub fn release(&self) -> YDBResult<String>
Return a string in the format rustwr <rust wrapper version> <$ZYRELEASE>
$ZYRELEASE
is the intrinsic variable containing the version of the underlying C database
and <rust wrapper version>
is the version of yottadb
published to crates.io.
§Errors
No errors should occur in normal operation. However, in case of system failure, an error code may be returned.
§Example
use yottadb::Context;
let ctx = Context::new();
let release = ctx.release()?;