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}