oracle_nosql_rust_sdk/
multi_delete_request.rs

1//
2// Copyright (c) 2024, 2025 Oracle and/or its affiliates. All rights reserved.
3//
4// Licensed under the Universal Permissive License v 1.0 as shown at
5//  https://oss.oracle.com/licenses/upl/
6//
7use crate::error::NoSQLError;
8use crate::handle::Handle;
9use crate::handle::SendOptions;
10use crate::nson::*;
11use crate::reader::Reader;
12use crate::types::{Capacity, FieldValue, OpCode};
13use crate::writer::Writer;
14use std::result::Result;
15use std::time::Duration;
16
17/// A range of values to be used in a [`MultiDeleteRequest`] operation.
18///
19/// `FieldRange` is used as the least significant component in a partially
20/// specified key value in order to create a value range for an operation that
21/// returns multiple rows or keys. The data types supported by `FieldRange` are
22/// limited to the atomic types which are valid for primary keys.
23///
24/// The least significant component of a key is the first component of the key
25/// that is not fully specified. For example, if the primary key for a table is
26/// defined as the tuple:
27///
28///   <a, b, c>
29///
30/// A `FieldRange` can be specified for:
31///
32///   "a" if the primary key supplied is empty.
33///   "b" if the primary key supplied to the operation has a concrete value for "a" but not for "b" or "c".
34///
35/// The `field_path` specified must name a field in a table's primary key.
36/// The `start` and `end` values used must be of the same type and that type must
37/// match the type of the field specified.
38///
39/// Validation of this struct is performed when it is used in an operation.
40/// Validation includes verifying that the field is in the required key and,
41/// in the case of a composite key, that the field is in the proper order
42/// relative to the key used in the operation.
43#[derive(Default, Debug)]
44pub struct FieldRange {
45    // field_path specifies the path to the field used in the range.
46    pub field_path: String,
47
48    // start specifies the start value of the range.
49    pub start: Option<FieldValue>,
50
51    // start_inclusive specifies whether start value is included in the range,
52    // i.e., start value is less than or equal to the first FieldValue in the range.
53    //
54    // This value is valid only if the start value is specified.
55    pub start_inclusive: bool,
56
57    // end specifies the end value of the range.
58    pub end: Option<FieldValue>,
59
60    // end_inclusive specifies whether end value is included in the range,
61    // i.e., end value is greater than or equal to the last FieldValue in the range.
62    //
63    // This value is valid only if the end value is specified.
64    pub end_inclusive: bool,
65}
66
67/// Struct used for deleting a range of rows from a NoSQL table.
68#[derive(Default, Debug)]
69pub struct MultiDeleteRequest {
70    pub(crate) table_name: String,
71    pub(crate) compartment_id: String,
72    pub(crate) key: FieldValue,
73    pub(crate) continuation_key: Option<Vec<u8>>,
74    pub(crate) field_range: Option<FieldRange>,
75    pub(crate) max_write_kb: i32,
76    pub(crate) timeout: Option<Duration>,
77    // Durability is currently only used in On-Prem installations.
78    // Added in SDK Version 1.3.0
79    // TODO durability: Option<Something>
80
81    // namespace is used on-premises only. It defines a namespace to use
82    // for the request. It is optional.
83    // If a namespace is specified in the table name for the request
84    // (using the namespace:tablename format), that value will override this
85    // setting.
86    // This is only available with on-premises installations using NoSQL
87    // Server versions 23.3 and above.
88    // TODO: pub namespace: String,
89
90    // TODO: limiters, retry stats, etc
91}
92
93/// Struct representing the result of a [`MultiDeleteRequest`] operation.
94#[derive(Default, Debug)]
95pub struct MultiDeleteResult {
96    pub(crate) num_deleted: i32,
97    pub(crate) continuation_key: Option<Vec<u8>>,
98    pub(crate) consumed: Option<Capacity>,
99}
100
101impl MultiDeleteResult {
102    /// Get the number of records deleted by the operation.
103    pub fn num_deleted(&self) -> i32 {
104        self.num_deleted
105    }
106    /// Get a continuation key that can be used in a subsequent MultiDelete operation.
107    /// This typically will be set when a `max_write_kb` is specified and there are more
108    /// records to delete.
109    pub fn continuation_key(&self) -> Option<Vec<u8>> {
110        if let Some(ck) = &self.continuation_key {
111            return Some(ck.clone());
112        }
113        None
114    }
115    /// Get the consumed capacity (read/write units) of the operation. This is only valid in the NoSQL Cloud Service.
116    pub fn consumed(&self) -> Option<&Capacity> {
117        if let Some(c) = &self.consumed {
118            return Some(c);
119        }
120        None
121    }
122}
123
124impl MultiDeleteRequest {
125    /// Create a new `MultiDeleteRequest`.
126    ///
127    /// `table_name` specifies the name of table for the request.
128    /// It is required and must be non-empty.
129    ///
130    /// `partial_key` specifies the partial key used for the request.
131    /// It is required and must be non-nil.
132    pub fn new(table_name: &str, partial_key: &FieldValue) -> MultiDeleteRequest {
133        MultiDeleteRequest {
134            table_name: table_name.to_string(),
135            key: partial_key.clone_internal(),
136            ..Default::default()
137        }
138    }
139
140    /// Specify the timeout value for the request.
141    ///
142    /// This is optional.
143    /// If set, it must be greater than or equal to 1 millisecond, otherwise an
144    /// IllegalArgument error will be returned.
145    /// If not set, the default timeout value configured for the [`Handle`](crate::HandleBuilder::timeout()) is used.
146    pub fn timeout(mut self, t: &Duration) -> MultiDeleteRequest {
147        self.timeout = Some(t.clone());
148        self
149    }
150
151    /// Cloud Service only: set the name or id of a compartment to be used for this operation.
152    ///
153    /// The compartment may be specified as either a name (or path for nested compartments) or as an id (OCID).
154    /// A name (vs id) can only be used when authenticated using a specific user identity. It is not available if
155    /// the associated handle authenticated as an Instance Principal (which can be done when calling the service from
156    /// a compute instance in the Oracle Cloud Infrastructure: see [`HandleBuilder::cloud_auth_from_instance()`](crate::HandleBuilder::cloud_auth_from_instance()).)
157    ///
158    /// If no compartment is given, the root compartment of the tenancy will be used.
159    pub fn compartment_id(mut self, compartment_id: &str) -> MultiDeleteRequest {
160        self.compartment_id = compartment_id.to_string();
161        self
162    }
163
164    /// Specifiy the [`FieldRange`] to be used for the operation.
165    ///
166    /// It is optional, but required to delete a specific range of rows.
167    pub fn field_range(mut self, field_range: FieldRange) -> MultiDeleteRequest {
168        self.field_range = Some(field_range);
169        self
170    }
171
172    /// Specify the continuation key to use to continue the operation.
173    ///
174    /// This is typically populated from a previous [`MultiDeleteResult::continuation_key()`].
175    pub fn continuation_key(mut self, key: Vec<u8>) -> MultiDeleteRequest {
176        self.continuation_key = Some(key);
177        self
178    }
179
180    /// Specify the limit on the total KB write during this operation.
181    ///
182    /// This is optional and has no effect for on-premise.
183    /// When used for the cloud service, if this value is not set, or set to 0, there
184    /// is no application-defined limit.
185    ///
186    ///  This value can only reduce the system defined limit. An attempt to increase the
187    /// limit beyond the system defined limit will cause an IllegalArgument error.
188    pub fn max_write_kb(mut self, max_write_kb: i32) -> MultiDeleteRequest {
189        self.max_write_kb = max_write_kb;
190        self
191    }
192
193    pub async fn execute(&self, h: &Handle) -> Result<MultiDeleteResult, NoSQLError> {
194        // TODO: validate: size > 0, etc
195        let mut w: Writer = Writer::new();
196        w.write_i16(h.inner.serial_version);
197        let timeout = h.get_timeout(&self.timeout);
198        self.serialize_internal(&mut w, &timeout);
199        // TODO: namespace in http header?
200        let mut opts = SendOptions {
201            timeout: timeout,
202            retryable: false,
203            compartment_id: self.compartment_id.clone(),
204            ..Default::default()
205        };
206        let mut r = h.send_and_receive(w, &mut opts).await?;
207        let resp = MultiDeleteRequest::nson_deserialize(&mut r)?;
208        Ok(resp)
209    }
210
211    fn serialize_internal(&self, w: &mut Writer, timeout: &Duration) {
212        let mut ns = NsonSerializer::start_request(w);
213        ns.start_header();
214        ns.write_header(OpCode::MultiDelete, timeout, &self.table_name);
215        ns.end_header();
216
217        ns.start_payload();
218        //ns.write_i32_field(DURABILITY, 0); // TODO
219
220        ns.write_i32_field(MAX_WRITE_KB, self.max_write_kb);
221
222        ns.write_field(KEY, &self.key);
223
224        if let Some(ckey) = &self.continuation_key {
225            ns.write_binary_field(CONTINUATION_KEY, ckey);
226        }
227
228        if let Some(range) = &self.field_range {
229            ns.start_map(RANGE);
230            ns.write_string_field(RANGE_PATH, &range.field_path);
231            if let Some(start) = &range.start {
232                ns.write_field(VALUE, start);
233                ns.write_bool_field(INCLUSIVE, range.start_inclusive);
234            }
235            if let Some(end) = &range.end {
236                ns.write_field(VALUE, end);
237                ns.write_bool_field(INCLUSIVE, range.end_inclusive);
238            }
239            ns.end_map(RANGE);
240        }
241
242        ns.end_payload();
243        ns.end_request();
244    }
245
246    pub(crate) fn nson_deserialize(r: &mut Reader) -> Result<MultiDeleteResult, NoSQLError> {
247        let mut walker = MapWalker::new(r)?;
248        let mut res: MultiDeleteResult = Default::default();
249        while walker.has_next() {
250            walker.next()?;
251            let name = walker.current_name();
252            match name.as_str() {
253                ERROR_CODE => {
254                    walker.handle_error_code()?;
255                }
256                CONSUMED => {
257                    res.consumed = Some(walker.read_nson_consumed_capacity()?);
258                    //println!(" consumed={:?}", res.consumed);
259                }
260                NUM_DELETIONS => {
261                    res.num_deleted = walker.read_nson_i32()?;
262                    //println!(" num_deleted={:?}", res.num_deleted);
263                }
264                CONTINUATION_KEY => {
265                    res.continuation_key = Some(walker.read_nson_binary()?);
266                    //println!(" continuation_key={:?}", res.continuation_key);
267                }
268                _ => {
269                    //println!("   multi_delete_result: skipping field '{}'", name);
270                    walker.skip_nson_field()?;
271                }
272            }
273        }
274        Ok(res)
275    }
276}
277
278impl NsonRequest for MultiDeleteRequest {
279    fn serialize(&self, w: &mut Writer, timeout: &Duration) {
280        self.serialize_internal(w, timeout);
281    }
282}