Struct yottadb::Context

source ·
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

source

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)?;
source

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

source

pub fn new() -> Context

Create a new Context

source

pub fn new_key<K: Into<Key>>(&self, key: K) -> KeyContext

source

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
source

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 returns Ok(TransactionStatus), the transaction will have the behavior documented under TransactionStatus (commit, restart, and rollback, respectively).
  • If f returns an Err(YDBError), the status from that error will be returned to the YottaDB engine. As a result, if the status for the YDBError is YDB_TP_RESTART, the transaction will be restarted. Otherwise, the transaction will be rolled back and the error returned from tp_st.
  • If f returns any other Err variant, the transaction will be rolled back and the error returned from tp_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 by f.
  • 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
source

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
source

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
source

pub fn str2zwr(&self, original: &[u8]) -> YDBResult<Vec<u8>>

Given a binary sequence, serialize it to ‘Zwrite format’, which is ASCII printable.

§Errors
§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
source

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
source

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 Keys that can be passed in locks:

  • 64-bit: 10 Keys
  • 32-bit: 9 Keys

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 if timeout is greater than YDB_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

source

pub fn message(&self, status: i32) -> YDBResult<Vec<u8>>

Return the message corresponding to a YottaDB error code

§Errors
  • YDB_ERR_UNKNOWNSYSERR if status is an unrecognized status code
§See also
§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"));
source

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()?;

Trait Implementations§

source§

impl Clone for Context

source§

fn clone(&self) -> Context

Returns a copy of the value. Read more
1.0.0 · source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
source§

impl Debug for Context

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for Context

source§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl PartialEq for Context

source§

fn eq(&self, other: &Context) -> bool

This method tests for self and other values to be equal, and is used by ==.
1.0.0 · source§

fn ne(&self, other: &Rhs) -> bool

This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
source§

impl Eq for Context

source§

impl StructuralPartialEq for Context

Auto Trait Implementations§

§

impl !RefUnwindSafe for Context

§

impl !Send for Context

§

impl !Sync for Context

§

impl Unpin for Context

§

impl !UnwindSafe for Context

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> ToOwned for T
where T: Clone,

§

type Owned = T

The resulting type after obtaining ownership.
source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.