yottadb/context_api/mod.rs
1/****************************************************************
2* *
3* Copyright (c) 2019-2023 YottaDB LLC and/or its subsidiaries. *
4* All rights reserved. *
5* *
6* This source code contains the intellectual property *
7* of its copyright holder(s), and is made available *
8* under a license. If you do not know the terms of *
9* the license, please stop and do not read further. *
10* *
11****************************************************************/
12
13/// This module is a very thin wrapper around the `simple_api`.
14///
15/// Provides a Rust-interface for YottaDB which hides some of the complexity related to
16/// managing error-return buffers and tptokens.
17///
18/// When adding new functions to `Context` or `KeyContext`, please remember to use the helper
19/// functions `tptoken`, `take_buffer` and `recover_buffer`; they not only simplify code, but make it
20/// easier to change `ContextInternal` if necessary.
21///
22/// The iterators could use some cleanup; in particular they have few tests.
23///
24/// Note that `Context` is separate from `ContextInternal` so that the fields can be grouped together
25/// into one `Rc<RefCell>`. The `Rc<RefCell>` is necessary because the *same* context needs to be
26/// reused among all keys, so that the tptoken is the same everywhere. Changing it to remove the
27/// `RefCell` would prevent using `Rc` (since it needs to borrow `buffer` mutably); removing the `Rc`
28/// would prevent it from being used in multiple keys.
29#[allow(unused)]
30const INTERNAL_DOCS: () = ();
31
32mod call_in;
33
34use std::cell::{Cell, RefCell};
35use std::error::Error;
36use std::rc::Rc;
37use std::str::FromStr;
38use std::ops::{AddAssign, Deref, DerefMut};
39use std::time::Duration;
40use std::fmt;
41
42use crate::craw::YDB_ERR_NODEEND;
43use crate::simple_api::{
44 self, tp_st, YDBResult, YDBError, DataReturn, DeleteType, Key, TransactionStatus, TpToken,
45};
46
47/// Private macros to help make iterators
48macro_rules! implement_iterator {
49 ($name:ident, $advance:ident, $return_type:ty, $next:expr) => {
50 #[allow(missing_docs)]
51 pub struct $name<'a> {
52 key: &'a mut KeyContext,
53 }
54
55 impl<'a> Iterator for $name<'a> {
56 type Item = YDBResult<$return_type>;
57
58 fn next(&mut self) -> Option<Self::Item> {
59 match self.key.$advance() {
60 #[allow(clippy::redundant_closure_call)]
61 Ok(_) => $next(self),
62 Err(YDBError { status: YDB_ERR_NODEEND, .. }) => None,
63 Err(x) => Some(Err(x)),
64 }
65 }
66 }
67 };
68}
69
70macro_rules! gen_iter_proto {
71 ($(#[$meta:meta])*
72 $name:ident, $return_type:tt) => {
73 $(#[$meta])*
74 pub fn $name(&mut self) -> $return_type {
75 $return_type {
76 key: self,
77 }
78 }
79 }
80}
81
82/// Create a [`KeyContext`] with the given subscripts, provided a context.
83///
84/// # Examples
85///
86/// ```
87/// use std::error::Error;
88/// use yottadb::Context;
89///
90/// fn main() -> Result<(), Box<dyn Error>> {
91/// let mut ctx = Context::new();
92/// let mut key = yottadb::make_ckey!(ctx, "^hello", "world");
93/// key.data()?;
94///
95/// Ok(())
96/// }
97/// ```
98#[macro_export]
99macro_rules! make_ckey {
100 ( $ctx:expr, $var:expr $(,)?) => (
101 $ctx.new_key($crate::Key::variable($var))
102 );
103 ( $ctx:expr, $gbl:expr $(, $x:expr)+ ) => (
104 $ctx.new_key(
105 $crate::make_key!( $gbl, $($x),+ )
106 )
107 );
108}
109
110/// NOTE: all fields in this struct must use interior mutability or they can't be mutated.
111#[derive(Debug)]
112struct ContextInternal {
113 tptoken: Cell<TpToken>,
114 buffer: RefCell<Vec<u8>>,
115 #[cfg(test)]
116 db_lock: RefCell<Option<crate::test_lock::LockGuard<'static>>>,
117}
118
119impl PartialEq for ContextInternal {
120 fn eq(&self, other: &Self) -> bool {
121 self.tptoken == other.tptoken && self.buffer == other.buffer
122 }
123}
124
125impl Eq for ContextInternal {}
126
127#[cfg(test)]
128impl Context {
129 fn write_lock(&self) {
130 drop(self.context.db_lock.borrow_mut().take());
131 *self.context.db_lock.borrow_mut() = Some(crate::test_lock::LockGuard::write());
132 }
133}
134
135/// A struct that keeps track of the current transaction and error buffer.
136///
137/// Since all functions in the YottaDB threaded API take a `tptoken` and `error_buffer`,
138/// it can be inconvenient to keep track of them manually, especially since
139///
140/// > Passing in a different or incorrect tptoken can result in hard-to-debug application behavior, including deadlocks. [1]
141///
142/// This struct keeps track of them for you
143/// so you don't have to clutter your application logic with resource management.
144///
145/// # See also
146/// - [Transaction processing](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#transaction-processing)
147/// - [Threads](https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#threads)
148/// - [Threads and transaction processing](https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#threads-and-transaction-processing)
149///
150/// `Context` is _not_ thread-safe, async-safe, or re-entrant.
151///
152/// Example:
153///
154/// ```compile_fail,E0277
155/// use yottadb::{Context, TransactionStatus, make_ckey};
156///
157/// let ctx = Context::new();
158/// let mut key1 = make_ckey!(ctx, "key1");
159/// let mut key2 = make_ckey!(ctx, "key2");
160/// tokio::spawn(async {
161/// // error[E0277]: `dyn std::error::Error` cannot be sent between threads safely
162/// ctx.tp(|_| Ok(TransactionStatus::Ok), "BATCH", &[])
163/// });
164/// ```
165///
166/// [1]: https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#threads-and-transaction-processing
167#[derive(Clone, Eq, PartialEq)]
168pub struct Context {
169 context: Rc<ContextInternal>,
170}
171
172impl fmt::Debug for Context {
173 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174 f.debug_struct("Context")
175 .field("tptoken", &self.tptoken())
176 .field("buffer", &String::from_utf8_lossy(&self.context.buffer.borrow()))
177 .finish()
178 }
179}
180
181impl Default for Context {
182 fn default() -> Self {
183 Self::new()
184 }
185}
186
187/// A key which keeps track of the current transaction and error buffer.
188///
189/// Keys are used to get, set, and delete values in the database.
190///
191/// # See also
192/// - [`Key`]
193/// - [Keys, values, nodes, variables, and subscripts](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#keys-values-nodes-variables-and-subscripts)
194/// - [Local and Global variables](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#local-and-global-variables)
195/// - [Intrinsic special variables](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#intrinsic-special-variables)
196#[derive(Debug, Clone, Eq, PartialEq)]
197pub struct KeyContext {
198 /// `KeyContext` implements `Deref<Target = Key>`
199 pub key: Key,
200 context: Context,
201}
202
203impl Context {
204 /// Create a new `Context`
205 pub fn new() -> Context {
206 Context {
207 context: Rc::new(ContextInternal {
208 tptoken: Cell::new(TpToken::default()),
209 buffer: RefCell::new(Vec::new()),
210 #[cfg(test)]
211 db_lock: RefCell::new(Some(crate::test_lock::LockGuard::read())),
212 }),
213 }
214 }
215
216 /// Create a `KeyContext` from this `Context`.
217 ///
218 /// # See also
219 /// - [`KeyContext::new()`]
220 /// - [`KeyContext::with_key`](KeyContext::with_key())
221 /// - [`impl From<(&Context, Key)> for KeyContext`](KeyContext#implementations)
222 pub fn new_key<K: Into<Key>>(&self, key: K) -> KeyContext {
223 KeyContext::with_key(self, key)
224 }
225
226 /// Return the token for the transaction associated with this `Context`.
227 ///
228 /// This allows calling yottadb functions in the `craw` API that have not yet been wrapped
229 /// and require a tptoken from inside a transaction.
230 ///
231 /// # Example
232 /// `tptoken()` can be used to call M FFI from within a transaction:
233 /// ```
234 /// use std::env;
235 /// use std::ffi::CStr;
236 /// use yottadb::{ci_t, Context, TransactionStatus, YDB_NOTTP};
237 ///
238 /// env::set_var("ydb_routines", "examples/m-ffi");
239 /// env::set_var("ydb_ci", "examples/m-ffi/calltab.ci");
240 /// let ctx = Context::new();
241 /// ctx.tp(|ctx| {
242 /// let tptoken = ctx.tptoken();
243 /// assert_ne!(tptoken, YDB_NOTTP);
244 /// let mut routine = CStr::from_bytes_with_nul(b"noop\0").unwrap();
245 /// unsafe { ci_t!(tptoken, Vec::new(), routine)?; }
246 /// Ok(TransactionStatus::Ok)
247 /// }, "BATCH", &[]).unwrap();
248 /// ```
249 ///
250 /// # See also
251 /// - [`Context::tp`](Context::tp())
252 #[inline]
253 pub fn tptoken(&self) -> TpToken {
254 self.context.tptoken.get()
255 }
256
257 /// Start a new transaction, where `f` is the transaction to execute.
258 ///
259 /// `tp` stands for 'transaction processing'.
260 ///
261 /// The parameter `trans_id` is the name logged for the transaction.
262 /// If `trans_id` has the special value `"BATCH"`, durability is not enforced by YottaDB.
263 /// See the [C documentation] for details.
264 ///
265 /// The argument passed to `f` is a [transaction processing token][threads and transactions].
266 ///
267 /// # Rollbacks and Restarts
268 /// Application code can return a [`TransactionStatus`] in order to rollback or restart.
269 /// `tp_st` behaves as follows:
270 /// - If `f` panics, the transaction is rolled back and the panic resumes afterwards.
271 /// - If `f` returns `Ok(TransactionStatus)`,
272 /// the transaction will have the behavior documented under `TransactionStatus` (commit, restart, and rollback, respectively).
273 /// - If `f` returns an `Err(YDBError)`, the status from that error will be returned to the YottaDB engine.
274 /// As a result, if the status for the `YDBError` is `YDB_TP_RESTART`, the transaction will be restarted.
275 /// Otherwise, the transaction will be rolled back and the error returned from `tp_st`.
276 /// - If `f` returns any other `Err` variant, the transaction will be rolled back and the error returned from `tp_st`.
277 ///
278 /// `f` must be `FnMut`, not `FnOnce`, since the YottaDB engine may
279 /// call `f` many times if necessary to ensure ACID properties.
280 /// This may affect your application logic; if you need to know how many
281 /// times the callback has been executed, get the [intrinsic variable][intrinsics]
282 /// [`$trestart`](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#trestart).
283 ///
284 /// # Errors
285 /// - YDB_ERR_TPTIMEOUT - The transaction took more than [`$zmaxtptime`] seconds to execute,
286 /// where `$zmaxtptime` is an [intrinsic special variable][intrinsics].
287 /// - YDB_TP_ROLLBACK — application logic indicates that the transaction should not be committed.
288 /// - A `YDBError` returned by a YottaDB function called by `f`.
289 /// - Another arbitrary error returned by `f`.
290 ///
291 /// # Examples
292 /// Rollback a transaction if an operation fails:
293 /// ```
294 /// use yottadb::{Context, KeyContext, TpToken, TransactionStatus};
295 ///
296 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
297 /// let ctx = Context::new();
298 /// let var = KeyContext::variable(&ctx, "tpRollbackTest");
299 /// var.set("initial value")?;
300 /// println!("starting tp");
301 /// let maybe_err = ctx.tp(|ctx| {
302 /// println!("in tp");
303 /// fallible_operation()?;
304 /// println!("succeeded");
305 /// var.set("new value")?;
306 /// Ok(TransactionStatus::Ok)
307 /// }, "BATCH", &[]);
308 /// let expected_val: &[_] = if maybe_err.is_ok() {
309 /// b"new value"
310 /// } else {
311 /// b"initial value"
312 /// };
313 /// assert_eq!(var.get()?, expected_val);
314 /// # Ok(())
315 /// # }
316 ///
317 /// fn fallible_operation() -> Result<(), &'static str> {
318 /// if rand::random() {
319 /// Ok(())
320 /// } else {
321 /// Err("the operation failed")
322 /// }
323 /// }
324 /// ```
325 ///
326 /// Retry a transaction until it succeeds:
327 /// ```
328 /// use yottadb::{Context, TpToken, TransactionStatus};
329 ///
330 /// let ctx = Context::new();
331 /// ctx.tp(|tptoken| {
332 /// if fallible_operation().is_ok() {
333 /// Ok(TransactionStatus::Ok)
334 /// } else {
335 /// Ok(TransactionStatus::Restart)
336 /// }
337 /// }, "BATCH", &[]).unwrap();
338 ///
339 /// fn fallible_operation() -> Result<(), ()> {
340 /// if rand::random() {
341 /// Ok(())
342 /// } else {
343 /// Err(())
344 /// }
345 /// }
346 /// ```
347 ///
348 /// # See Also
349 /// - [More details about the underlying FFI call][C documentation]
350 /// - [Transaction Processing in YottaDB](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#transaction-processing)
351 /// - [Threads and Transaction Processing][threads and transactions]
352 ///
353 /// [`$zmaxtptime`]: https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#zmaxtptime
354 /// [intrinsics]: crate#intrinsic-variables
355 /// [threads and transactions]: https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#threads-and-transaction-processing
356 /// [C documentation]: https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#ydb-tp-s-ydb-tp-st
357 pub fn tp<'a, F>(
358 &'a self, mut f: F, trans_id: &str, locals_to_reset: &[&str],
359 ) -> Result<(), Box<dyn Error + Send + Sync>>
360 where
361 // NOTE: `FnMut(&'a Self)` is a distinct type from `FnMut(&Self)`: the first takes a specific
362 // lifetime, the second is sugar for `for<'s> FnMut(&'s Self)`. See
363 // <https://doc.rust-lang.org/nomicon/hrtb.html> for details. The reason to take `&'a Self` is
364 // it imposes fewer requirements on the caller.
365 F: FnMut(&'a Self) -> Result<TransactionStatus, Box<dyn Error + Send + Sync>>,
366 {
367 let initial_token = self.tptoken();
368 let result = tp_st(
369 initial_token,
370 self.take_buffer(),
371 |tptoken: TpToken| {
372 self.context.tptoken.set(tptoken);
373 f(self)
374 },
375 trans_id,
376 locals_to_reset,
377 );
378 self.context.tptoken.set(initial_token);
379 result.map(|x| {
380 *self.context.buffer.borrow_mut() = x;
381 })
382 }
383
384 /// Delete all local variables _except_ for those passed in `saved_variable`.
385 ///
386 /// Passing an empty `saved_variables` slice deletes all local variables.
387 /// Attempting to save a global or intrinsic variable is an error.
388 ///
389 /// # Errors
390 /// - YDB_ERR_NAMECOUNT2HI if `saved_variables.len() > YDB_MAX_NAMES`
391 /// - YDB_ERR_INVVARNAME if attempting to save a global or intrinsic variable
392 /// - Another system [error return code](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
393 ///
394 /// # Examples
395 ///
396 /// ```
397 /// # fn main() -> yottadb::YDBResult<()> {
398 /// use yottadb::{Context, KeyContext, TpToken, YDB_ERR_LVUNDEF};
399 ///
400 /// // Create three variables and set all
401 /// let ctx = Context::new();
402 /// let a = KeyContext::variable(&ctx, "deleteExclTestA");
403 /// a.set("test data")?;
404 /// let b = KeyContext::variable(&ctx, "deleteExclTestB");
405 /// b.set("test data 2")?;
406 /// let c = KeyContext::variable(&ctx, "deleteExclTestC");
407 /// c.set("test data 3")?;
408 ///
409 /// // Delete all variables except `a`
410 /// ctx.delete_excl(&[&a.variable])?;
411 /// assert_eq!(a.get()?, b"test data");
412 /// assert_eq!(b.get().unwrap_err().status, YDB_ERR_LVUNDEF);
413 /// assert_eq!(c.get().unwrap_err().status, YDB_ERR_LVUNDEF);
414 ///
415 /// // Delete `a` too
416 /// ctx.delete_excl(&[])?;
417 /// assert_eq!(a.get().unwrap_err().status, YDB_ERR_LVUNDEF);
418 ///
419 /// # Ok(())
420 /// # }
421 /// ```
422 ///
423 /// # See also
424 /// - The [Simple API documentation](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#ydb-delete-excl-s-ydb-delete-excl-st)
425 /// - [Local and global variables](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#local-and-global-variables)
426 /// - [Instrinsic special variables](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#intrinsic-special-variables)
427 pub fn delete_excl(&self, saved_variables: &[&str]) -> YDBResult<()> {
428 use simple_api::delete_excl_st;
429
430 let tptoken = self.tptoken();
431 let buffer = self.take_buffer();
432 self.recover_buffer(delete_excl_st(tptoken, buffer, saved_variables))
433 }
434
435 /// Runs the YottaDB deferred signal handler (if necessary).
436 ///
437 /// This function must be called if an application has a tight loop inside a transaction which never calls a YDB function.
438 ///
439 /// # See also
440 /// - [Signal Handling](super#signal-handling)
441 /// - [`Context::tp`](Context::tp())
442 /// - The [C documentation](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#ydb-eintr-handler-ydb-eintr-handler-t)
443 pub fn eintr_handler(&self) -> YDBResult<()> {
444 use simple_api::eintr_handler_t;
445
446 let tptoken = self.tptoken();
447 let buffer = self.take_buffer();
448 self.recover_buffer(eintr_handler_t(tptoken, buffer))
449 }
450
451 /// Given a binary sequence, serialize it to 'Zwrite format', which is ASCII printable.
452 ///
453 /// # Errors
454 /// - If YDB is in UTF8 mode, will return [`BADCHAR`] on invalid UTF8.
455 /// - Another [error code](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
456 ///
457 /// # Examples
458 ///
459 /// When `ydb_chset=UTF-8` is set, this will preserve UTF-8 characters:
460 ///
461 /// ```
462 /// # fn main() -> Result<(), yottadb::YDBError> {
463 /// use yottadb::Context;
464 ///
465 /// let ctx = Context::new();
466 /// let str2zwr = ctx.str2zwr("💖".as_bytes())?;
467 /// if std::env::var("ydb_chset").as_deref() == Ok("UTF-8") {
468 /// assert_eq!(str2zwr, "\"💖\"".as_bytes());
469 /// } else {
470 /// // Note: The "$C" below cannot be expanded to "$CH" or "$CHAR" as that is the output returned by "str2zwr()" in M mode.
471 /// assert_eq!(str2zwr, b"\"\xf0\"_$C(159,146,150)");
472 /// }
473 /// # Ok(())
474 /// # }
475 /// ```
476 ///
477 /// When the input is invalid UTF-8, it will use the more verbose Zwrite format:
478 /// ```
479 /// # fn main() -> Result<(), yottadb::YDBError> {
480 /// use yottadb::Context;
481 ///
482 /// let ctx = Context::new();
483 /// let input = b"\xff";
484 /// assert!(std::str::from_utf8(input).is_err());
485 /// if std::env::var("ydb_chset").as_deref() == Ok("UTF-8") {
486 /// assert_eq!(ctx.str2zwr(input)?, b"$ZCH(255)");
487 /// } else {
488 /// assert_eq!(ctx.str2zwr(input)?, b"$C(255)");
489 /// }
490 /// # Ok(()) }
491 /// ```
492 ///
493 /// # See also
494 /// - [Zwrite format](https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#zwrite-formatted)
495 /// - [`zwr2str`](Context::zwr2str()), which deserializes a buffer in Zwrite format back to the original binary.
496 ///
497 /// [`BADCHAR`]: https://docs.yottadb.com/MessageRecovery/errors.html#badchar
498 pub fn str2zwr(&self, original: &[u8]) -> YDBResult<Vec<u8>> {
499 use simple_api::str2zwr_st;
500
501 let tptoken = self.tptoken();
502 str2zwr_st(tptoken, self.take_buffer(), original)
503 }
504
505 /// Given a buffer in 'Zwrite format', deserialize it to the original binary buffer.
506 ///
507 /// `zwr2str_st` writes directly to `out_buf` to avoid returning multiple output buffers.
508 ///
509 /// # Errors
510 /// This function returns an empty array if `serialized` is not in Zwrite format.
511 /// It can also return another [error code](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code).
512 ///
513 /// # Examples
514 ///
515 /// ```
516 /// # use yottadb::YDBError;
517 /// # fn main() -> Result<(), YDBError> {
518 /// use yottadb::Context;
519 ///
520 /// let ctx = Context::new();
521 /// // Use "$ZCH" (instead of "$C") below as that will work in both M and UTF-8 modes (of "ydb_chset" env var)
522 /// // Note: Cannot use "$ZCHAR" below as "$ZCH" is the only input format recognized by "zwr2str()".
523 /// let out_buf = ctx.zwr2str(b"\"\xf0\"_$ZCH(159,146,150)")?;
524 /// assert_eq!(out_buf.as_slice(), "💖".as_bytes());
525 /// # Ok(())
526 /// # }
527 /// ```
528 ///
529 /// # See also
530 /// - [Zwrite format](https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#zwrite-formatted)
531 /// - [str2zwr](Context::str2zwr()), the inverse of `zwr2str`.
532 pub fn zwr2str(&self, serialized: &[u8]) -> Result<Vec<u8>, YDBError> {
533 use simple_api::zwr2str_st;
534
535 let tptoken = self.tptoken();
536 zwr2str_st(tptoken, self.take_buffer(), serialized)
537 }
538
539 fn take_buffer(&self) -> Vec<u8> {
540 std::mem::take(&mut self.context.buffer.borrow_mut())
541 }
542
543 fn recover_buffer(&self, result: YDBResult<Vec<u8>>) -> YDBResult<()> {
544 result.map(|x| {
545 *self.context.buffer.borrow_mut() = x;
546 })
547 }
548
549 /// Acquires locks specified in `locks` and releases all others.
550 ///
551 /// This operation is atomic. If any lock cannot be acquired, all locks are released.
552 /// The `timeout` specifies the maximum time to wait before returning an error.
553 /// If no locks are specified, all locks are released.
554 ///
555 /// Note that YottaDB locks are per-process, not per-thread.
556 ///
557 /// # Limitations
558 ///
559 /// For implementation reasons, there is a hard limit to the number of `Key`s that can be passed in `locks`:
560 // floor( (36 - 4)/3 ) = 10
561 /// - 64-bit: 10 `Key`s
562 // floor( (36 - 7)/3 ) = 9
563 /// - 32-bit: 9 `Key`s
564 ///
565 /// If more than this number of keys are passed, `lock_st` will return `YDB_ERR_MAXARGCNT`.
566 ///
567 /// For implementation reasons, `lock_st` only works on 64-bit platforms, or on 32-bit ARM.
568 ///
569 /// `lock_st` will not be compiled on 16, 8, or 128 bit platforms
570 /// (i.e. will fail with 'cannot find function `lock_st` in module `yottadb::simple_api`').
571 ///
572 /// On non-ARM 32-bit platforms, the compiler will allow `lock_st` to be called,
573 /// but it will have unspecified behavior and has not been tested.
574 /// Use [`KeyContext::lock_incr`] and [`KeyContext::lock_decr`] instead.
575 ///
576 /// # Errors
577 ///
578 /// Possible errors for this function include:
579 /// - `YDB_LOCK_TIMEOUT` if all locks could not be acquired within the timeout period.
580 /// In this case, no locks are acquired.
581 /// - `YDB_ERR_TIME2LONG` if `timeout` is greater than `YDB_MAX_TIME_NSEC`
582 /// - `YDB_ERR_MAXARGCNT` if too many locks have been passed (see [Limitations](#limitations))
583 /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
584 ///
585 /// # Examples
586 /// ```
587 /// use std::slice;
588 /// use std::time::Duration;
589 /// use yottadb::{Context, KeyContext, Key, TpToken};
590 ///
591 /// // You can use either a `Key` or a `KeyContext` to acquire a lock.
592 /// // This uses a `KeyContext` to show that you need to use `.key` to get the inner `Key`.
593 /// let ctx = Context::new();
594 /// let a = KeyContext::variable(&ctx, "lockA");
595 ///
596 /// // Acquire a new lock
597 /// // using `from_ref` here allows us to use `a` later without moving it
598 /// ctx.lock(Duration::from_secs(1), slice::from_ref(&a.key)).unwrap();
599 ///
600 /// // Acquire multiple locks
601 /// let locks = vec![a.key, Key::variable("lockB")];
602 /// ctx.lock(Duration::from_secs(1), &locks).unwrap();
603 ///
604 /// // Release all locks
605 /// ctx.lock(Duration::from_secs(1), &[]).unwrap();
606 /// ```
607 ///
608 /// # See also
609 ///
610 /// - The C [Simple API documentation](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#ydb-lock-s-ydb-lock-st)
611 /// - [Locks](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#locks)
612 ///
613 /// [`KeyContext::lock_incr`]: KeyContext::lock_incr()
614 /// [`KeyContext::lock_decr`]: KeyContext::lock_decr()
615 pub fn lock(&self, timeout: Duration, locks: &[Key]) -> YDBResult<()> {
616 use simple_api::lock_st;
617
618 let tptoken = self.tptoken();
619 let buffer = self.take_buffer();
620 self.recover_buffer(lock_st(tptoken, buffer, timeout, locks))
621 }
622}
623
624/// Utility functions
625impl Context {
626 /// Return the message corresponding to a YottaDB error code
627 ///
628 /// # Errors
629 /// - `YDB_ERR_UNKNOWNSYSERR` if `status` is an unrecognized status code
630 ///
631 /// # See also
632 /// - [`impl Display for YDBError`][`impl Display`], which should meet most use cases for `message_t`.
633 /// - [Function return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#function-return-codes)
634 /// - [ZMessage codes](https://docs.yottadb.com/MessageRecovery/errormsgref.html#zmessage-codes)
635 /// - The [C documentation](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#ydb-message-ydb-message-t)
636 ///
637 /// [`impl Display`]: YDBError#impl-Display-for-YDBError
638 ///
639 /// # Example
640 /// Look up the error message for an undefined local variable:
641 /// ```
642 /// use yottadb::{Context, KeyContext, TpToken, YDB_ERR_LVUNDEF};
643 ///
644 /// let ctx = Context::new();
645 /// let key = KeyContext::variable(&ctx, "oopsNotDefined");
646 ///
647 /// let err = key.get().unwrap_err();
648 /// assert_eq!(err.status, YDB_ERR_LVUNDEF);
649 ///
650 /// let buf = ctx.message(err.status).unwrap();
651 /// let msg = String::from_utf8(buf).unwrap();
652 /// assert!(msg.contains("Undefined local variable"));
653 /// ```
654 pub fn message(&self, status: i32) -> YDBResult<Vec<u8>> {
655 let tptoken = self.tptoken();
656 simple_api::message_t(tptoken, Vec::new(), status)
657 }
658
659 /// Return a string in the format `rustwr <rust wrapper version> <$ZYRELEASE>`
660 ///
661 /// [`$ZYRELEASE`] is the [intrinsic variable] containing the version of the underlying C database
662 /// and `<rust wrapper version>` is the version of `yottadb` published to crates.io.
663 ///
664 /// # Errors
665 /// No errors should occur in normal operation.
666 /// However, in case of system failure, an [error code] may be returned.
667 ///
668 /// [error code]: https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code
669 /// [intrinsic variable]: https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#intrinsic-special-variables
670 /// [`$ZYRELEASE`]: https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#zyrelease
671 ///
672 /// # Example
673 /// ```
674 /// # fn main() -> yottadb::YDBResult<()> {
675 /// use yottadb::Context;
676 /// let ctx = Context::new();
677 /// let release = ctx.release()?;
678 /// # Ok(())
679 /// # }
680 /// ```
681 pub fn release(&self) -> YDBResult<String> {
682 let tptoken = self.tptoken();
683 simple_api::release_t(tptoken, Vec::new())
684 }
685}
686
687impl std::borrow::Borrow<Key> for KeyContext {
688 fn borrow(&self) -> &Key {
689 &self.key
690 }
691}
692
693impl std::borrow::BorrowMut<Key> for KeyContext {
694 fn borrow_mut(&mut self) -> &mut Key {
695 &mut self.key
696 }
697}
698
699impl Deref for KeyContext {
700 type Target = Key;
701
702 fn deref(&self) -> &Self::Target {
703 &self.key
704 }
705}
706
707impl DerefMut for KeyContext {
708 fn deref_mut(&mut self) -> &mut Self::Target {
709 &mut self.key
710 }
711}
712
713impl AddAssign<i32> for KeyContext {
714 fn add_assign(&mut self, rhs: i32) {
715 self.increment(Some(rhs.to_string().as_bytes())).expect("failed to increment node");
716 }
717}
718
719impl From<(&Context, Key)> for KeyContext {
720 fn from((ctx, key): (&Context, Key)) -> Self {
721 KeyContext::with_key(ctx, key)
722 }
723}
724
725/// The error type returned by [`KeyContext::get_and_parse()`]
726#[derive(Debug)]
727pub enum ParseError<T> {
728 /// There was an error retrieving the value from the database.
729 YDB(YDBError),
730 /// Retrieving the value succeeded, but it was not a valid `String`.
731 ///
732 /// The bytes of the value are still available using `.into_bytes()`.
733 Utf8(std::string::FromUtf8Error),
734 /// A valid `String` was retrieved but did not parse successfully.
735 /// The `String` is still available.
736 ///
737 /// The `T` is the type of `FromStr::Err` for the value being parsed.
738 Parse(T, String),
739}
740
741impl<T: fmt::Display> fmt::Display for ParseError<T> {
742 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
743 match self {
744 ParseError::YDB(err) => write!(f, "{}", err),
745 ParseError::Utf8(utf8) => write!(f, "{}", utf8),
746 ParseError::Parse(err, _) => write!(f, "{}", err),
747 }
748 }
749}
750
751impl<T: Error + 'static> Error for ParseError<T> {
752 fn source(&self) -> Option<&(dyn Error + 'static)> {
753 let err = match self {
754 ParseError::YDB(err) => err as &dyn Error,
755 ParseError::Utf8(not_utf8) => not_utf8,
756 ParseError::Parse(not_valid, _) => not_valid,
757 };
758 Some(err)
759 }
760}
761
762impl KeyContext {
763 /// Create a new `KeyContext`, creating the `Key` at the same time.
764 ///
765 /// # See also
766 /// - [`KeyContext::with_key`](KeyContext::with_key())
767 /// - [`Context::new_key()`]
768 /// - [`impl From<(&Context, Key)> for KeyContext`](KeyContext#implementations)
769 pub fn new<V, S>(ctx: &Context, variable: V, subscripts: &[S]) -> KeyContext
770 where
771 V: Into<String>,
772 S: Into<Vec<u8>> + Clone,
773 {
774 Self::with_key(ctx, Key::new(variable, subscripts))
775 }
776 /// Shortcut for creating a `KeyContext` with no subscripts.
777 // this should be kept in sync with `Key::variable`
778 pub fn variable<V: Into<String>>(ctx: &Context, var: V) -> Self {
779 Self::with_key(ctx, var)
780 }
781 /// Create a new `KeyContext` using an existing key.
782 ///
783 /// # See also
784 /// - [`KeyContext::new`](KeyContext::new())
785 /// - [`Context::new_key()`]
786 /// - [`impl From<(&Context, Key)> for KeyContext`](KeyContext#implementations)
787 pub fn with_key<K: Into<Key>>(ctx: &Context, key: K) -> Self {
788 Self { context: ctx.clone(), key: key.into() }
789 }
790
791 fn take_buffer(&self) -> Vec<u8> {
792 self.context.take_buffer()
793 }
794
795 fn recover_buffer(&self, result: YDBResult<Vec<u8>>) -> YDBResult<()> {
796 self.context.recover_buffer(result)
797 }
798
799 /// Gets the value of this key from the database and returns the value.
800 ///
801 /// # Errors
802 ///
803 /// Possible errors for this function include:
804 /// - YDB_ERR_GVUNDEF, YDB_ERR_INVSVN, YDB_ERR_LVUNDEF as appropriate if no such variable or node exists
805 /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
806 ///
807 /// # Examples
808 ///
809 /// ```
810 /// # #[macro_use] extern crate yottadb;
811 /// use yottadb::Context;
812 /// use std::error::Error;
813 ///
814 /// fn main() -> Result<(), Box<dyn Error>> {
815 /// let ctx = Context::new();
816 /// let mut key = make_ckey!(ctx, "^hello");
817 ///
818 /// key.set("Hello world!")?;
819 /// let output_buffer = key.get()?;
820 ///
821 /// assert_eq!(output_buffer, b"Hello world!");
822 ///
823 /// Ok(())
824 /// }
825 /// ```
826 pub fn get(&self) -> YDBResult<Vec<u8>> {
827 let tptoken = self.context.tptoken();
828 self.key.get_st(tptoken, Vec::new())
829 }
830
831 /// Retrieve a value from the database and parse it into a Rust data structure.
832 ///
833 /// This is a shorthand for `String::from_utf8(key.get()).parse()`
834 /// that collects the errors into a single enum.
835 ///
836 /// # Examples
837 /// Set and retrieve an integer, with error handling.
838 /// ```
839 /// use yottadb::{Context, ParseError};
840 /// let ctx = Context::new();
841 /// let mut key = ctx.new_key("weekday");
842 /// key.set(5.to_string())?;
843 /// let day: u8 = match key.get_and_parse() {
844 /// Ok(day) => day,
845 /// Err(ParseError::YDB(err)) => return Err(err),
846 /// Err(ParseError::Utf8(err)) => {
847 /// eprintln!("warning: had an invalid string");
848 /// String::from_utf8_lossy(&err.as_bytes()).parse().unwrap()
849 /// }
850 /// Err(ParseError::Parse(err, original)) => {
851 /// panic!("{} is not a valid string: {}", original, err);
852 /// }
853 /// };
854 /// Ok(())
855 /// ```
856 ///
857 /// Set and retrieve an integer, without error handling.
858 /// ```
859 /// # use yottadb::YDBResult;
860 /// # fn main() -> YDBResult<()> {
861 /// use yottadb::Context;
862 /// let ctx = Context::new();
863 /// let mut key = ctx.new_key("weekday");
864 /// key.set(5.to_string())?;
865 /// let day: u8 = key.get_and_parse().unwrap();
866 /// Ok(())
867 /// # }
868 /// ```
869 pub fn get_and_parse<T: FromStr>(&self) -> Result<T, ParseError<T::Err>> {
870 self.get()
871 .map_err(ParseError::YDB)
872 .and_then(|bytes| String::from_utf8(bytes).map_err(ParseError::Utf8))
873 .and_then(|s| s.parse().map_err(|err| ParseError::Parse(err, s)))
874 }
875
876 /// Sets the value of a key in the database.
877 ///
878 /// # Errors
879 ///
880 /// Possible errors for this function include:
881 /// - YDB_ERR_INVSVN if no such intrinsic special variable exists
882 /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
883 ///
884 /// # Examples
885 ///
886 /// ```
887 /// # #[macro_use] extern crate yottadb;
888 /// use yottadb::Context;
889 /// use std::error::Error;
890 ///
891 /// fn main() -> Result<(), Box<dyn Error>> {
892 /// let ctx = Context::new();
893 /// let mut key = make_ckey!(ctx, "^hello");
894 ///
895 /// key.set("Hello world!")?;
896 ///
897 /// Ok(())
898 /// }
899 /// ```
900 pub fn set<U: AsRef<[u8]>>(&self, new_val: U) -> YDBResult<()> {
901 let tptoken = self.context.tptoken();
902 let out_buffer = self.take_buffer();
903 let result = self.key.set_st(tptoken, out_buffer, new_val);
904 self.recover_buffer(result)
905 }
906
907 /// Returns the following information in DataReturn about a local or global variable node:
908 ///
909 /// * NoData: There is neither a value nor a subtree; i.e it is undefined.
910 /// * ValueData: There is a value, but no subtree.
911 /// * TreeData: There is no value, but there is a subtree.
912 /// * ValueTreeData: There are both a value and a subtree.
913 ///
914 /// # Errors
915 ///
916 /// Possible errors for this function include:
917 /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
918 ///
919 /// # Examples
920 ///
921 /// ```
922 /// # #[macro_use] extern crate yottadb;
923 /// use yottadb::{Context, DataReturn};
924 /// use std::error::Error;
925 ///
926 /// fn main() -> Result<(), Box<dyn Error>> {
927 /// let ctx = Context::new();
928 /// let mut key = make_ckey!(ctx, "^helloDoesNotExist");
929 ///
930 /// assert_eq!(key.data()?, DataReturn::NoData);
931 ///
932 /// Ok(())
933 /// }
934 /// ```
935 pub fn data(&self) -> YDBResult<DataReturn> {
936 let tptoken = self.context.tptoken();
937 let out_buffer = self.take_buffer();
938 self.key.data_st(tptoken, out_buffer).map(|(y, x)| {
939 *self.context.context.buffer.borrow_mut() = x;
940 y
941 })
942 }
943
944 /// Delete nodes in the local or global variable tree or subtree specified. A value of DelNode or DelTree for DeleteType
945 /// specifies whether to delete just the node at the root, leaving the (sub)tree intact, or to delete the node as well as the (sub)tree.
946 ///
947 /// # Errors
948 ///
949 /// Possible errors for this function include:
950 /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
951 ///
952 /// # Examples
953 ///
954 /// ```
955 /// # #[macro_use] extern crate yottadb;
956 /// use yottadb::{Context, DataReturn, DeleteType};
957 /// use std::error::Error;
958 ///
959 /// fn main() -> Result<(), Box<dyn Error>> {
960 /// let ctx = Context::new();
961 /// let mut key = make_ckey!(ctx, "^helloDeleteMe");
962 ///
963 /// key.set("Hello world!")?;
964 /// key.delete(DeleteType::DelTree)?;
965 ///
966 /// assert_eq!(key.data()?, DataReturn::NoData);
967 ///
968 /// Ok(())
969 /// }
970 /// ```
971 pub fn delete(&self, delete_type: DeleteType) -> YDBResult<()> {
972 let tptoken = self.context.tptoken();
973 let out_buffer = self.take_buffer();
974 let result = self.key.delete_st(tptoken, out_buffer, delete_type);
975 self.recover_buffer(result)
976 }
977
978 /// Converts the value to a [number](https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#canonical-numbers)
979 /// and increments it based on the value specifed by Option.
980 ///
981 /// `increment` defaults to 1 if the value is None.
982 ///
983 /// # Errors
984 ///
985 /// Possible errors for this function include:
986 /// - YDB_ERR_NUMOFLOW
987 /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
988 ///
989 /// # See also
990 ///
991 /// - [How YDB stores numbers internally](https://docs.yottadb.com/MultiLangProgGuide/programmingnotes.html#numeric-considerations)
992 ///
993 /// # Examples
994 ///
995 /// ```
996 /// # #[macro_use] extern crate yottadb;
997 /// use yottadb::Context;
998 /// use std::error::Error;
999 ///
1000 /// fn main() -> Result<(), Box<dyn Error>> {
1001 /// let ctx = Context::new();
1002 /// let mut key = make_ckey!(ctx, "helloIncrementMe");
1003 ///
1004 /// key.set("0")?;
1005 /// key.increment(None)?;
1006 /// let output_buffer = key.get()?;
1007 /// assert_eq!(output_buffer, b"1");
1008 ///
1009 /// assert_eq!(key.increment(Some(b"100"))?, b"101");
1010 /// let output_buffer = key.get()?;
1011 /// assert_eq!(output_buffer, b"101");
1012 ///
1013 /// Ok(())
1014 /// }
1015 /// ```
1016 ///
1017 /// As a shorthand, you can use `+=` to increment a key.
1018 ///
1019 /// ```
1020 /// # use yottadb::{Context, KeyContext, DeleteType};
1021 /// # use std::error::Error;
1022 /// # fn main() -> Result<(), Box<dyn Error>> {
1023 /// let ctx = Context::new();
1024 /// let mut key = KeyContext::variable(&ctx, "helloAddAssign");
1025 /// key += 100;
1026 /// let output_buffer = key.get()?;
1027 /// assert_eq!(output_buffer, b"100");
1028 /// # Ok(()) }
1029 /// ```
1030 pub fn increment(&self, increment: Option<&[u8]>) -> YDBResult<Vec<u8>> {
1031 let tptoken = self.context.tptoken();
1032 self.key.incr_st(tptoken, Vec::new(), increment)
1033 }
1034
1035 /// Increment the count of a lock held by the process, or acquire a new lock.
1036 ///
1037 /// If the lock is not currently held by this process, it is acquired.
1038 /// Otherwise, the lock count is incremented.
1039 ///
1040 /// `timeout` specifies a time that the function waits to acquire the requested locks.
1041 /// If `timeout` is 0, the function makes exactly one attempt to acquire the lock.
1042 ///
1043 /// # Errors
1044 /// - `YDB_ERR_INVVARNAME` if `self.variable` is not a valid variable name.
1045 /// - `YDB_LOCK_TIMEOUT` if the lock could not be acquired within the specific time.
1046 /// - `YDB_ERR_TIME2LONG` if `timeout.as_nanos()` exceeds `YDB_MAX_TIME_NSEC`
1047 /// or if `timeout.as_nanos()` does not fit into a `c_ulonglong`.
1048 /// - Another [error code](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1049 ///
1050 /// # Examples
1051 ///
1052 /// ```
1053 /// # fn main() -> Result<(), yottadb::YDBError> {
1054 /// use yottadb::{Context, KeyContext};
1055 /// use std::time::Duration;
1056 ///
1057 /// let ctx = Context::new();
1058 /// let key = KeyContext::variable(&ctx, "lockIncrTest");
1059 /// key.lock_incr(Duration::from_secs(1))?;
1060 /// # Ok(())
1061 /// # }
1062 /// ```
1063 ///
1064 /// # See also
1065 /// - The C [Simple API documentation](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#ydb-lock-decr-s-ydb-lock-decr-st)
1066 /// - [Locks](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#locks)
1067 /// - [Variables](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#variables-vs-subscripts-vs-values)
1068 pub fn lock_incr(&self, timeout: std::time::Duration) -> YDBResult<()> {
1069 let tptoken = self.context.tptoken();
1070 let buffer = self.take_buffer();
1071 self.recover_buffer(self.key.lock_incr_st(tptoken, buffer, timeout))
1072 }
1073
1074 /// Decrement the count of a lock held by the process.
1075 ///
1076 /// When a lock goes from 1 to 0, it is released.
1077 /// Attempting to decrement a lock not owned by the current process has no effect.
1078 ///
1079 /// # Errors
1080 /// - `YDB_ERR_INVVARNAME` if `self.variable` is not a valid variable name.
1081 ///
1082 /// # Examples
1083 ///
1084 /// ```
1085 /// # fn main() -> Result<(), yottadb::YDBError> {
1086 /// use yottadb::{Context, KeyContext};
1087 /// use std::time::Duration;
1088 ///
1089 /// let ctx = Context::new();
1090 /// let key = KeyContext::variable(&ctx, "lockDecrTest");
1091 /// key.lock_incr(Duration::from_secs(1))?;
1092 /// key.lock_decr()?;
1093 /// # Ok(())
1094 /// # }
1095 /// ```
1096 ///
1097 /// # See also
1098 /// - The C [Simple API documentation](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#ydb-lock-decr-s-ydb-lock-decr-st)
1099 /// - [Locks](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#locks)
1100 /// - [Variables](https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#variables-vs-subscripts-vs-values)
1101 pub fn lock_decr(&self) -> YDBResult<()> {
1102 let tptoken = self.context.tptoken();
1103 let buffer = self.take_buffer();
1104 self.recover_buffer(self.key.lock_decr_st(tptoken, buffer))
1105 }
1106
1107 /// Implements breadth-first traversal of a tree by searching for the next subscript, and passes itself in as the output parameter.
1108 ///
1109 /// # Errors
1110 ///
1111 /// Possible errors for this function include:
1112 /// - YDB_ERR_NODEEND
1113 /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1114 ///
1115 /// # Examples
1116 ///
1117 /// ```
1118 /// # #[macro_use] extern crate yottadb;
1119 /// use yottadb::Context;
1120 /// use std::error::Error;
1121 ///
1122 /// fn main() -> Result<(), Box<dyn Error>> {
1123 /// let ctx = Context::new();
1124 /// let mut key = make_ckey!(ctx, "^hello", "a");
1125 ///
1126 /// key.set("Hello world!")?;
1127 /// key[0] = Vec::from("b");
1128 /// key.set("Hello world!")?;
1129 /// key[0] = Vec::from("a");
1130 /// // Starting at a, the next sub should be b
1131 /// key.next_sub_self()?;
1132 ///
1133 /// assert_eq!(key[0], b"b");
1134 ///
1135 /// Ok(())
1136 /// }
1137 /// ```
1138 pub fn next_sub_self(&mut self) -> YDBResult<()> {
1139 let tptoken = self.context.tptoken();
1140 let out_buffer = self.take_buffer();
1141 let result = self.key.sub_next_self_st(tptoken, out_buffer);
1142 self.recover_buffer(result)
1143 }
1144
1145 /// Implements reverse breadth-first traversal of a tree by searching for the previous subscript, and passes itself in as the output parameter.
1146 ///
1147 /// # Errors
1148 ///
1149 /// Possible errors for this function include:
1150 /// - YDB_ERR_NODEEND
1151 /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1152 ///
1153 /// # Examples
1154 ///
1155 /// ```
1156 /// # #[macro_use] extern crate yottadb;
1157 /// use yottadb::Context;
1158 /// use std::error::Error;
1159 ///
1160 /// fn main() -> Result<(), Box<dyn Error>> {
1161 /// let ctx = Context::new();
1162 /// let mut key = make_ckey!(ctx, "^hello", "0");
1163 ///
1164 /// key.set("Hello world!")?;
1165 /// key[0] = Vec::from("1");
1166 /// key.set("Hello world!")?;
1167 /// key[0] = Vec::from("1");
1168 /// key.prev_sub_self()?;
1169 ///
1170 /// assert_eq!(key[0], b"0");
1171 ///
1172 /// Ok(())
1173 /// }
1174 /// ```
1175 pub fn prev_sub_self(&mut self) -> YDBResult<()> {
1176 let tptoken = self.context.tptoken();
1177 let out_buffer = self.take_buffer();
1178 let result = self.key.sub_prev_self_st(tptoken, out_buffer);
1179 self.recover_buffer(result)
1180 }
1181
1182 /// Implements breadth-first traversal of a tree by searching for the next subscript.
1183 ///
1184 /// # Errors
1185 ///
1186 /// Possible errors for this function include:
1187 /// - YDB_ERR_NODEEND
1188 /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1189 ///
1190 /// # Examples
1191 ///
1192 /// ```
1193 /// # #[macro_use] extern crate yottadb;
1194 /// use yottadb::Context;
1195 /// use std::error::Error;
1196 ///
1197 /// fn main() -> Result<(), Box<dyn Error>> {
1198 /// let ctx = Context::new();
1199 /// let mut key = make_ckey!(ctx, "^hello", "0");
1200 ///
1201 /// key.set("Hello world!")?;
1202 /// key[0] = Vec::from("1");
1203 /// key.set("Hello world!")?;
1204 /// key[0] = Vec::from("0");
1205 /// let subscript = key.next_sub()?;
1206 ///
1207 /// assert_eq!(subscript, b"1");
1208 ///
1209 /// Ok(())
1210 /// }
1211 /// ```
1212 pub fn next_sub(&self) -> YDBResult<Vec<u8>> {
1213 let tptoken = self.context.tptoken();
1214 let out_buffer = self.take_buffer();
1215 self.key.sub_next_st(tptoken, out_buffer)
1216 }
1217
1218 /// Implements reverse breadth-first traversal of a tree by searching for the previous subscript.
1219 ///
1220 /// # Errors
1221 ///
1222 /// Possible errors for this function include:
1223 /// - YDB_ERR_NODEEND
1224 /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1225 ///
1226 /// # Examples
1227 ///
1228 /// ```
1229 /// # #[macro_use] extern crate yottadb;
1230 /// use yottadb::Context;
1231 /// use std::error::Error;
1232 ///
1233 /// fn main() -> Result<(), Box<dyn Error>> {
1234 /// let ctx = Context::new();
1235 /// let mut key = make_ckey!(ctx, "^hello", "0");
1236 ///
1237 /// key.set(b"Hello world!")?;
1238 /// key[0] = Vec::from("1");
1239 /// key.set("Hello world!")?;
1240 /// key[0] = Vec::from("1");
1241 /// let subscript = key.prev_sub()?;
1242 ///
1243 /// assert_eq!(subscript, b"0");
1244 ///
1245 /// Ok(())
1246 /// }
1247 /// ```
1248 pub fn prev_sub(&self) -> YDBResult<Vec<u8>> {
1249 let tptoken = self.context.tptoken();
1250 let out_buffer = self.take_buffer();
1251 self.key.sub_prev_st(tptoken, out_buffer)
1252 }
1253
1254 /// Facilitates depth-first traversal of a local or global variable tree, and passes itself in as the output parameter.
1255 ///
1256 /// For more information on variable trees, see the [overview of YottaDB][how-it-works]
1257 /// as well as the section on [variables and nodes][vars-nodes].
1258 ///
1259 /// # Errors
1260 ///
1261 /// Possible errors for this function include:
1262 /// - YDB_ERR_NODEEND
1263 /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1264 ///
1265 /// # Examples
1266 ///
1267 /// ```
1268 /// # #[macro_use] extern crate yottadb;
1269 /// use yottadb::Context;
1270 /// use std::error::Error;
1271 ///
1272 /// fn main() -> Result<(), Box<dyn Error>> {
1273 /// let ctx = Context::new();
1274 /// let mut key = make_ckey!(ctx, "^hello", "0", "0");
1275 ///
1276 /// key.set("Hello world!")?;
1277 /// // Forget the second subscript
1278 /// key.truncate(1);
1279 /// key.next_node_self()?;
1280 ///
1281 /// assert_eq!(key[1], b"0");
1282 ///
1283 /// Ok(())
1284 /// }
1285 /// ```
1286 ///
1287 /// [how-it-works]: https://yottadb.com/product/how-it-works/
1288 /// [vars-nodes]: https://docs.yottadb.com/MultiLangProgGuide/MultiLangProgGuide.html#keys-values-nodes-variables-and-subscripts
1289 pub fn next_node_self(&mut self) -> YDBResult<()> {
1290 let tptoken = self.context.tptoken();
1291 let out_buffer = self.take_buffer();
1292 let result = self.key.node_next_self_st(tptoken, out_buffer);
1293 self.recover_buffer(result)
1294 }
1295
1296 /// Facilitates reverse depth-first traversal of a local or global variable tree and reports the predecessor node, passing itself in as the output parameter.
1297 ///
1298 /// # Errors
1299 ///
1300 /// Possible errors for this function include:
1301 /// - YDB_ERR_NODEEND
1302 /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1303 ///
1304 /// # Examples
1305 ///
1306 /// ```
1307 /// # #[macro_use] extern crate yottadb;
1308 /// use yottadb::Context;
1309 /// use std::error::Error;
1310 ///
1311 /// fn main() -> Result<(), Box<dyn Error>> {
1312 /// let ctx = Context::new();
1313 /// let mut key = make_ckey!(ctx, "^hello", "0", "0");
1314 ///
1315 /// key.set("Hello world!")?;
1316 /// // Forget the second subscript
1317 /// key.truncate(1);
1318 /// // Go to the next node, then walk backward
1319 /// key[0] = Vec::from("1");
1320 /// key.prev_node_self()?;
1321 ///
1322 /// assert_eq!(key[1], b"0");
1323 ///
1324 /// Ok(())
1325 /// }
1326 /// ```
1327 pub fn prev_node_self(&mut self) -> YDBResult<()> {
1328 let tptoken = self.context.tptoken();
1329 let out_buffer = self.take_buffer();
1330 let result = self.key.node_prev_self_st(tptoken, out_buffer);
1331 self.recover_buffer(result)
1332 }
1333
1334 /// Facilitate depth-first traversal of a local or global variable tree.
1335 ///
1336 /// # Errors
1337 ///
1338 /// Possible errors for this function include:
1339 /// - YDB_ERR_NODEEND
1340 /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1341 ///
1342 /// # Examples
1343 ///
1344 /// ```
1345 /// # #[macro_use] extern crate yottadb;
1346 /// use yottadb::Context;
1347 /// use std::error::Error;
1348 ///
1349 /// fn main() -> Result<(), Box<dyn Error>> {
1350 /// let ctx = Context::new();
1351 /// let mut key = make_ckey!(ctx, "^hello", "0", "0");
1352 ///
1353 /// key.set("Hello world!")?;
1354 /// // Forget the second subscript
1355 /// key.truncate(1);
1356 /// let k2 = key.next_node()?;
1357 ///
1358 /// assert_eq!(k2[1], b"0");
1359 ///
1360 /// Ok(())
1361 /// }
1362 /// ```
1363 pub fn next_node(&mut self) -> YDBResult<KeyContext> {
1364 let mut ret = self.clone();
1365 ret.next_node_self()?;
1366 Ok(ret)
1367 }
1368
1369 /// Facilitates reverse depth-first traversal of a local or global variable tree, and returns
1370 /// the previous node.
1371 ///
1372 /// # Errors
1373 ///
1374 /// Possible errors for this function include:
1375 /// - YDB_ERR_NODEEND
1376 /// - [error return codes](https://docs.yottadb.com/MultiLangProgGuide/cprogram.html#error-return-code)
1377 ///
1378 /// # Examples
1379 ///
1380 /// ```
1381 /// # #[macro_use] extern crate yottadb;
1382 /// use yottadb::Context;
1383 /// use std::error::Error;
1384 ///
1385 /// fn main() -> Result<(), Box<dyn Error>> {
1386 /// let ctx = Context::new();
1387 /// let mut key = make_ckey!(ctx, "^helloPrevNode", "0", "0");
1388 ///
1389 /// key.set("Hello world!")?;
1390 /// // Forget the second subscript
1391 /// key.truncate(1);
1392 /// // Go to the next node, then walk backward
1393 /// key[0] = "1".into();
1394 /// let k2 = key.prev_node()?;
1395 ///
1396 /// assert_eq!(&k2.variable, "^helloPrevNode");
1397 /// assert_eq!(k2[0], b"0");
1398 /// assert_eq!(k2[1], b"0");
1399 ///
1400 /// Ok(())
1401 /// }
1402 /// ```
1403 pub fn prev_node(&mut self) -> YDBResult<KeyContext> {
1404 let mut ret = self.clone();
1405 ret.prev_node_self()?;
1406 Ok(ret)
1407 }
1408
1409 gen_iter_proto!(
1410 /// Iterates over all the values at this level of the database tree and returns the value for
1411 /// each node.
1412 iter_values,
1413 ForwardValueIterator
1414 );
1415
1416 gen_iter_proto!(
1417 /// Iterates over all the subscripts at this level of the database tree and returns the
1418 /// subscript for each node.
1419 iter_subs,
1420 ForwardSubIterator
1421 );
1422
1423 gen_iter_proto!(
1424 /// Iterates over all the subscripts at this level of the database tree and returns the subscript and value for each node.
1425 iter_subs_values,
1426 ForwardSubValueIterator
1427 );
1428
1429 gen_iter_proto!(
1430 /// Iterates over all subscripts at this level of the database tree and returns a copy of the key at each subscript.
1431 iter_key_subs,
1432 ForwardKeySubIterator
1433 );
1434
1435 gen_iter_proto!(
1436 /// Iterates over all nodes for the global pointed to by the key and returns the value at each node.
1437 iter_nodes,
1438 ForwardNodeIterator
1439 );
1440
1441 gen_iter_proto!(
1442 /// Iterates over all nodes for the global pointed to by the key and returns a copy of the key at each node.
1443 iter_key_nodes,
1444 ForwardKeyNodeIterator
1445 );
1446
1447 gen_iter_proto!(
1448 /// Iterates in reverse order over all the values at this level of the database tree and returns the value for
1449 /// each node.
1450 iter_values_reverse,
1451 ReverseValueIterator
1452 );
1453
1454 gen_iter_proto!(
1455 /// Iterates in reverse order over all the subscripts at this level of the database tree and returns the
1456 /// subscript for each node.
1457 iter_subs_reverse,
1458 ReverseSubIterator
1459 );
1460
1461 gen_iter_proto!(
1462 /// Iterates in reverse order over all the subscripts at this level of the database tree and returns the subscript and value for each node.
1463 iter_subs_values_reverse,
1464 ReverseSubValueIterator
1465 );
1466
1467 gen_iter_proto!(
1468 /// Iterates in reverse order over all subscripts at this level of the database tree and returns a copy of the key at each subscript.
1469 iter_key_subs_reverse,
1470 ReverseKeySubIterator
1471 );
1472
1473 gen_iter_proto!(
1474 /// Iterates in reverse order over all nodes for the global pointed to by the key and returns the value at each node.
1475 iter_nodes_reverse,
1476 ReverseNodeIterator
1477 );
1478
1479 gen_iter_proto!(
1480 /// Iterates in reverse oder over all nodes for the global pointed to by the key and returns a copy of the key at each node.
1481 iter_key_nodes_reverse,
1482 ReverseKeyNodeIterator
1483 );
1484}
1485
1486implement_iterator!(
1487 ForwardValueIterator,
1488 next_sub_self,
1489 Vec<u8>,
1490 |me: &mut ForwardValueIterator| { Some(me.key.get()) }
1491);
1492
1493implement_iterator!(ForwardSubIterator, next_sub_self, Vec<u8>, |me: &mut ForwardSubIterator| {
1494 Some(Ok(me.key.last().unwrap().clone()))
1495});
1496
1497implement_iterator!(
1498 ForwardSubValueIterator,
1499 next_sub_self,
1500 (Vec<u8>, Vec<u8>),
1501 |me: &mut ForwardSubValueIterator| {
1502 let val = me.key.get();
1503 if val.is_err() {
1504 let err = val.err().unwrap();
1505 return Some(Err(err));
1506 }
1507 Some(Ok((me.key.last().unwrap().clone(), val.unwrap())))
1508 }
1509);
1510
1511implement_iterator!(
1512 ForwardKeySubIterator,
1513 next_sub_self,
1514 KeyContext,
1515 |me: &mut ForwardKeySubIterator| { Some(Ok(me.key.clone())) }
1516);
1517
1518implement_iterator!(
1519 ForwardNodeIterator,
1520 next_node_self,
1521 Vec<u8>,
1522 |me: &mut ForwardNodeIterator| {
1523 let data = me.key.data().unwrap();
1524 if data != DataReturn::ValueData && data != DataReturn::ValueTreeData {
1525 return me.next();
1526 }
1527 Some(me.key.get())
1528 }
1529);
1530
1531implement_iterator!(
1532 ForwardKeyNodeIterator,
1533 next_node_self,
1534 KeyContext,
1535 |me: &mut ForwardKeyNodeIterator| { Some(Ok(me.key.clone())) }
1536);
1537
1538implement_iterator!(
1539 ReverseValueIterator,
1540 prev_sub_self,
1541 Vec<u8>,
1542 |me: &mut ReverseValueIterator| { Some(me.key.get()) }
1543);
1544
1545implement_iterator!(ReverseSubIterator, prev_sub_self, Vec<u8>, |me: &mut ReverseSubIterator| {
1546 Some(Ok(me.key.last().unwrap().clone()))
1547});
1548
1549implement_iterator!(
1550 ReverseSubValueIterator,
1551 prev_sub_self,
1552 (Vec<u8>, Vec<u8>),
1553 |me: &mut ReverseSubValueIterator| {
1554 let val = me.key.get();
1555 if val.is_err() {
1556 let err = val.err().unwrap();
1557 return Some(Err(err));
1558 }
1559 Some(Ok((me.key.last().unwrap().clone(), val.unwrap())))
1560 }
1561);
1562
1563implement_iterator!(
1564 ReverseKeySubIterator,
1565 prev_sub_self,
1566 KeyContext,
1567 |me: &mut ReverseKeySubIterator| { Some(Ok(me.key.clone())) }
1568);
1569
1570implement_iterator!(
1571 ReverseNodeIterator,
1572 prev_node_self,
1573 Vec<u8>,
1574 |me: &mut ReverseNodeIterator| { Some(me.key.get()) }
1575);
1576
1577implement_iterator!(
1578 ReverseKeyNodeIterator,
1579 prev_node_self,
1580 KeyContext,
1581 |me: &mut ReverseKeyNodeIterator| { Some(Ok(me.key.clone())) }
1582);
1583
1584#[cfg(test)]
1585mod tests;