Skip to main content

oracledb_protocol/thin/
lob.rs

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