Skip to main content

oracledb_protocol/thin/
lob.rs

1#![forbid(unsafe_code)]
2
3use super::*;
4use crate::wire::ProtocolLimits;
5
6pub fn build_lob_read_payload_with_seq(
7    locator: &[u8],
8    offset: u64,
9    amount: u64,
10    seq_num: u8,
11    ttc_field_version: u8,
12) -> Result<Vec<u8>> {
13    let locator_len =
14        u32::try_from(locator.len()).map_err(|_| ProtocolError::InvalidPacketLength {
15            length: locator.len(),
16            minimum: 0,
17        })?;
18    let mut writer = TtcWriter::new();
19    writer.write_function_code_with_seq(TNS_FUNC_LOB_OP, seq_num);
20    if ttc_field_version >= TNS_CCAP_FIELD_VERSION_23_1_EXT_1 {
21        writer.write_ub8(0);
22    }
23    writer.write_u8(1);
24    writer.write_ub4(locator_len);
25    writer.write_u8(0);
26    writer.write_ub4(0);
27    writer.write_ub4(0);
28    writer.write_ub4(0);
29    writer.write_u8(0);
30    writer.write_u8(0);
31    writer.write_u8(0);
32    writer.write_ub4(TNS_LOB_OP_READ);
33    writer.write_u8(0);
34    writer.write_u8(0);
35    writer.write_ub8(offset);
36    writer.write_ub8(0);
37    writer.write_u8(1);
38    for _ in 0..3 {
39        writer.write_u16be(0);
40    }
41    writer.write_raw(locator);
42    writer.write_ub8(amount);
43    Ok(writer.into_bytes())
44}
45
46#[allow(clippy::too_many_arguments)]
47pub(crate) fn write_lob_op_header(
48    writer: &mut TtcWriter,
49    locator: &[u8],
50    seq_num: u8,
51    ttc_field_version: u8,
52    operation: u32,
53    dest_length: u32,
54    source_offset: u64,
55    dest_offset: u64,
56    pointer_charset: bool,
57    pointer_null_lob: bool,
58    send_amount: bool,
59) -> Result<()> {
60    let locator_len =
61        u32::try_from(locator.len()).map_err(|_| ProtocolError::InvalidPacketLength {
62            length: locator.len(),
63            minimum: 0,
64        })?;
65    writer.write_function_code_with_seq(TNS_FUNC_LOB_OP, seq_num);
66    if ttc_field_version >= TNS_CCAP_FIELD_VERSION_23_1_EXT_1 {
67        writer.write_ub8(0);
68    }
69    writer.write_u8(1);
70    writer.write_ub4(locator_len);
71    writer.write_u8(0);
72    writer.write_ub4(dest_length);
73    writer.write_ub4(0);
74    writer.write_ub4(0);
75    writer.write_u8(u8::from(pointer_charset));
76    writer.write_u8(0);
77    writer.write_u8(u8::from(pointer_null_lob));
78    writer.write_ub4(operation);
79    writer.write_u8(0);
80    writer.write_u8(0);
81    writer.write_ub8(source_offset);
82    writer.write_ub8(dest_offset);
83    writer.write_u8(u8::from(send_amount));
84    for _ in 0..3 {
85        writer.write_u16be(0);
86    }
87    writer.write_raw(locator);
88    Ok(())
89}
90
91pub fn build_lob_create_temp_payload_with_seq(
92    ora_type_num: u8,
93    csfrm: u8,
94    seq_num: u8,
95    ttc_field_version: u8,
96) -> Result<Vec<u8>> {
97    let mut writer = TtcWriter::new();
98    write_lob_op_header(
99        &mut writer,
100        &[0; 40],
101        seq_num,
102        ttc_field_version,
103        TNS_LOB_OP_CREATE_TEMP,
104        TNS_DURATION_SESSION,
105        u64::from(csfrm),
106        u64::from(ora_type_num),
107        true,
108        true,
109        false,
110    )?;
111    writer.write_ub4(TNS_CHARSET_UTF8.into());
112    Ok(writer.into_bytes())
113}
114
115pub fn build_lob_write_payload_with_seq(
116    locator: &[u8],
117    offset: u64,
118    data: &[u8],
119    seq_num: u8,
120    ttc_field_version: u8,
121) -> Result<Vec<u8>> {
122    let mut writer = TtcWriter::new();
123    write_lob_op_header(
124        &mut writer,
125        locator,
126        seq_num,
127        ttc_field_version,
128        TNS_LOB_OP_WRITE,
129        0,
130        offset,
131        0,
132        false,
133        false,
134        false,
135    )?;
136    writer.write_u8(TNS_MSG_TYPE_LOB_DATA);
137    writer.write_bytes_with_length(data)?;
138    Ok(writer.into_bytes())
139}
140
141pub fn build_lob_trim_payload_with_seq(
142    locator: &[u8],
143    new_size: u64,
144    seq_num: u8,
145    ttc_field_version: u8,
146) -> Result<Vec<u8>> {
147    let mut writer = TtcWriter::new();
148    write_lob_op_header(
149        &mut writer,
150        locator,
151        seq_num,
152        ttc_field_version,
153        TNS_LOB_OP_TRIM,
154        0,
155        0,
156        0,
157        false,
158        false,
159        true,
160    )?;
161    writer.write_ub8(new_size);
162    Ok(writer.into_bytes())
163}
164
165pub fn lob_locator_is_temporary(locator: &[u8]) -> bool {
166    locator
167        .get(TNS_LOB_LOC_OFFSET_FLAG_1)
168        .is_some_and(|flags| flags & TNS_LOB_LOC_FLAGS_ABSTRACT != 0)
169        || locator
170            .get(TNS_LOB_LOC_OFFSET_FLAG_4)
171            .is_some_and(|flags| flags & TNS_LOB_LOC_FLAGS_TEMP != 0)
172}
173
174pub fn build_lob_free_temp_payload_with_seq(
175    locators: &[Vec<u8>],
176    seq_num: u8,
177    ttc_field_version: u8,
178) -> Result<Vec<u8>> {
179    let total_size = locators.iter().try_fold(0u32, |total, locator| {
180        let locator_len =
181            u32::try_from(locator.len()).map_err(|_| ProtocolError::InvalidPacketLength {
182                length: locator.len(),
183                minimum: 0,
184            })?;
185        total
186            .checked_add(locator_len)
187            .ok_or(ProtocolError::PacketTooLarge { length: usize::MAX })
188    })?;
189    let mut writer = TtcWriter::new();
190    writer.write_function_code_with_seq(TNS_FUNC_LOB_OP, seq_num);
191    if ttc_field_version >= TNS_CCAP_FIELD_VERSION_23_1_EXT_1 {
192        writer.write_ub8(0);
193    }
194    writer.write_u8(1);
195    writer.write_ub4(total_size);
196    writer.write_u8(0);
197    writer.write_ub4(0);
198    writer.write_ub4(0);
199    writer.write_ub4(0);
200    writer.write_u8(0);
201    writer.write_u8(0);
202    writer.write_u8(0);
203    writer.write_ub4(TNS_LOB_OP_FREE_TEMP | TNS_LOB_OP_ARRAY);
204    writer.write_u8(0);
205    writer.write_ub4(0);
206    writer.write_ub8(0);
207    writer.write_ub8(0);
208    writer.write_u8(0);
209    writer.write_u8(0);
210    writer.write_ub4(0);
211    writer.write_u8(0);
212    writer.write_ub4(0);
213    writer.write_u8(0);
214    writer.write_ub4(0);
215    for locator in locators {
216        writer.write_raw(locator);
217    }
218    Ok(writer.into_bytes())
219}
220
221pub fn parse_lob_read_response(
222    payload: &[u8],
223    capabilities: ClientCapabilities,
224    locator: &[u8],
225) -> Result<LobReadResult> {
226    parse_lob_read_response_with_limits(payload, capabilities, locator, ProtocolLimits::DEFAULT)
227}
228
229pub fn parse_lob_read_response_with_limits(
230    payload: &[u8],
231    capabilities: ClientCapabilities,
232    locator: &[u8],
233    limits: ProtocolLimits,
234) -> Result<LobReadResult> {
235    parse_lob_op_response_with_limits(payload, capabilities, locator, false, true, limits)
236}
237
238pub fn parse_lob_create_temp_response(
239    payload: &[u8],
240    capabilities: ClientCapabilities,
241) -> Result<LobReadResult> {
242    parse_lob_create_temp_response_with_limits(payload, capabilities, ProtocolLimits::DEFAULT)
243}
244
245pub fn parse_lob_create_temp_response_with_limits(
246    payload: &[u8],
247    capabilities: ClientCapabilities,
248    limits: ProtocolLimits,
249) -> Result<LobReadResult> {
250    parse_lob_op_response_with_limits(payload, capabilities, &[0; 40], true, false, limits)
251}
252
253pub fn parse_lob_write_response(
254    payload: &[u8],
255    capabilities: ClientCapabilities,
256    locator: &[u8],
257) -> Result<LobReadResult> {
258    parse_lob_write_response_with_limits(payload, capabilities, locator, ProtocolLimits::DEFAULT)
259}
260
261pub fn parse_lob_write_response_with_limits(
262    payload: &[u8],
263    capabilities: ClientCapabilities,
264    locator: &[u8],
265    limits: ProtocolLimits,
266) -> Result<LobReadResult> {
267    parse_lob_op_response_with_limits(payload, capabilities, locator, false, false, limits)
268}
269
270pub fn parse_lob_trim_response(
271    payload: &[u8],
272    capabilities: ClientCapabilities,
273    locator: &[u8],
274) -> Result<LobReadResult> {
275    parse_lob_trim_response_with_limits(payload, capabilities, locator, ProtocolLimits::DEFAULT)
276}
277
278pub fn parse_lob_trim_response_with_limits(
279    payload: &[u8],
280    capabilities: ClientCapabilities,
281    locator: &[u8],
282    limits: ProtocolLimits,
283) -> Result<LobReadResult> {
284    parse_lob_op_response_with_limits(payload, capabilities, locator, false, true, limits)
285}
286
287pub fn parse_lob_free_temp_response(
288    payload: &[u8],
289    capabilities: ClientCapabilities,
290    returned_parameter_len: usize,
291) -> Result<()> {
292    parse_lob_free_temp_response_with_limits(
293        payload,
294        capabilities,
295        returned_parameter_len,
296        ProtocolLimits::DEFAULT,
297    )
298}
299
300pub fn parse_lob_free_temp_response_with_limits(
301    payload: &[u8],
302    capabilities: ClientCapabilities,
303    returned_parameter_len: usize,
304    limits: ProtocolLimits,
305) -> Result<()> {
306    limits.check_frame_bytes(returned_parameter_len)?;
307    let mut reader = TtcReader::with_limits(payload, limits)?;
308    while reader.remaining() > 0 {
309        let message_type = reader.read_u8()?;
310        match message_type {
311            0 => {}
312            TNS_MSG_TYPE_STATUS => {
313                let _call_status = reader.read_ub4()?;
314                let _seq = reader.read_ub2()?;
315            }
316            TNS_MSG_TYPE_SERVER_SIDE_PIGGYBACK => {
317                let _ = skip_server_side_piggyback(&mut reader)?;
318            }
319            TNS_MSG_TYPE_END_OF_RESPONSE => break,
320            TNS_MSG_TYPE_ERROR => {
321                let info = parse_server_error_info(&mut reader, capabilities.ttc_field_version)?;
322                if info.number != 0 {
323                    return Err(ProtocolError::ServerError(info.message));
324                }
325            }
326            TNS_MSG_TYPE_PARAMETER => reader.skip(returned_parameter_len)?,
327            _ => {
328                return Err(ProtocolError::UnknownMessageType {
329                    message_type,
330                    position: reader.position().saturating_sub(1),
331                })
332            }
333        }
334    }
335    Ok(())
336}
337
338/// Scan a plain function response (ping/commit/rollback) for a server error.
339/// Unknown message types end the scan without error so payload shapes that
340/// were previously tolerated (responses used to go unparsed) keep working.
341/// Returns whether a server-side transaction is in progress, sampled from the
342/// final end-of-call status bit (reference protocol.pyx `_process_call_status`).
343pub fn parse_plain_function_response(
344    payload: &[u8],
345    capabilities: ClientCapabilities,
346) -> Result<bool> {
347    parse_plain_function_response_with_limits(payload, capabilities, ProtocolLimits::DEFAULT)
348}
349
350pub fn parse_plain_function_response_with_limits(
351    payload: &[u8],
352    capabilities: ClientCapabilities,
353    limits: ProtocolLimits,
354) -> Result<bool> {
355    let mut reader = TtcReader::with_limits(payload, limits)?;
356    let mut txn_in_progress = false;
357    while reader.remaining() > 0 {
358        let message_type = reader.read_u8()?;
359        match message_type {
360            0 => {}
361            TNS_MSG_TYPE_STATUS => {
362                let call_status = reader.read_ub4()?;
363                let _seq = reader.read_ub2()?;
364                txn_in_progress = call_status & TNS_EOCS_FLAGS_TXN_IN_PROGRESS != 0;
365            }
366            TNS_MSG_TYPE_SERVER_SIDE_PIGGYBACK => {
367                let _ = skip_server_side_piggyback(&mut reader)?;
368            }
369            TNS_MSG_TYPE_END_OF_RESPONSE => break,
370            TNS_MSG_TYPE_ERROR => {
371                let info = parse_server_error_info(&mut reader, capabilities.ttc_field_version)?;
372                // The end-of-call ERROR (number 0 on success) carries the
373                // end-of-call status; sample the transaction-in-progress bit.
374                txn_in_progress = info.call_status & TNS_EOCS_FLAGS_TXN_IN_PROGRESS != 0;
375                if info.number != 0 {
376                    return Err(ProtocolError::ServerError(info.message));
377                }
378            }
379            _ => break,
380        }
381    }
382    Ok(txn_in_progress)
383}
384
385pub(crate) fn parse_lob_op_response_with_limits(
386    payload: &[u8],
387    capabilities: ClientCapabilities,
388    locator: &[u8],
389    is_create_temp: bool,
390    read_amount: bool,
391    limits: ProtocolLimits,
392) -> Result<LobReadResult> {
393    let mut reader = TtcReader::with_limits(payload, limits)?;
394    let mut result = LobReadResult {
395        locator: locator.to_vec(),
396        ..LobReadResult::default()
397    };
398    while reader.remaining() > 0 {
399        let message_type = reader.read_u8()?;
400        match message_type {
401            0 => {}
402            TNS_MSG_TYPE_LOB_DATA => {
403                result.data = reader.read_bytes()?;
404            }
405            TNS_MSG_TYPE_PARAMETER => {
406                if !result.locator.is_empty() {
407                    result.locator = reader.read_raw(result.locator.len())?.to_vec();
408                }
409                if is_create_temp {
410                    let _charset = reader.read_ub2()?;
411                    reader.skip(1)?;
412                } else if read_amount {
413                    let amount = reader.read_sb8()?;
414                    if amount > 0 {
415                        result.amount = amount as u64;
416                    }
417                }
418            }
419            TNS_MSG_TYPE_STATUS => {
420                let _call_status = reader.read_ub4()?;
421                let _seq = reader.read_ub2()?;
422            }
423            TNS_MSG_TYPE_SERVER_SIDE_PIGGYBACK => {
424                let _ = skip_server_side_piggyback(&mut reader)?;
425            }
426            TNS_MSG_TYPE_END_OF_RESPONSE => break,
427            TNS_MSG_TYPE_ERROR => {
428                let info = parse_server_error_info(&mut reader, capabilities.ttc_field_version)?;
429                if info.number != 0 {
430                    return Err(ProtocolError::ServerError(info.message));
431                }
432            }
433            _ => {
434                return Err(ProtocolError::UnknownMessageType {
435                    message_type,
436                    position: reader.position().saturating_sub(1),
437                })
438            }
439        }
440    }
441    Ok(result)
442}