#![forbid(unsafe_code)]
use super::*;
use crate::wire::ProtocolLimits;
pub fn build_lob_read_payload_with_seq(
locator: &[u8],
offset: u64,
amount: u64,
seq_num: u8,
ttc_field_version: u8,
) -> Result<Vec<u8>> {
let locator_len =
u32::try_from(locator.len()).map_err(|_| ProtocolError::InvalidPacketLength {
length: locator.len(),
minimum: 0,
})?;
let mut writer = TtcWriter::new();
writer.write_function_code_with_seq(TNS_FUNC_LOB_OP, seq_num);
if ttc_field_version >= TNS_CCAP_FIELD_VERSION_23_1_EXT_1 {
writer.write_ub8(0);
}
writer.write_u8(1);
writer.write_ub4(locator_len);
writer.write_u8(0);
writer.write_ub4(0);
writer.write_ub4(0);
writer.write_ub4(0);
writer.write_u8(0);
writer.write_u8(0);
writer.write_u8(0);
writer.write_ub4(TNS_LOB_OP_READ);
writer.write_u8(0);
writer.write_u8(0);
writer.write_ub8(offset);
writer.write_ub8(0);
writer.write_u8(1);
for _ in 0..3 {
writer.write_u16be(0);
}
writer.write_raw(locator);
writer.write_ub8(amount);
Ok(writer.into_bytes())
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn write_lob_op_header(
writer: &mut TtcWriter,
locator: &[u8],
seq_num: u8,
ttc_field_version: u8,
operation: u32,
dest_length: u32,
source_offset: u64,
dest_offset: u64,
pointer_charset: bool,
pointer_null_lob: bool,
send_amount: bool,
) -> Result<()> {
let locator_len =
u32::try_from(locator.len()).map_err(|_| ProtocolError::InvalidPacketLength {
length: locator.len(),
minimum: 0,
})?;
writer.write_function_code_with_seq(TNS_FUNC_LOB_OP, seq_num);
if ttc_field_version >= TNS_CCAP_FIELD_VERSION_23_1_EXT_1 {
writer.write_ub8(0);
}
writer.write_u8(1);
writer.write_ub4(locator_len);
writer.write_u8(0);
writer.write_ub4(dest_length);
writer.write_ub4(0);
writer.write_ub4(0);
writer.write_u8(u8::from(pointer_charset));
writer.write_u8(0);
writer.write_u8(u8::from(pointer_null_lob));
writer.write_ub4(operation);
writer.write_u8(0);
writer.write_u8(0);
writer.write_ub8(source_offset);
writer.write_ub8(dest_offset);
writer.write_u8(u8::from(send_amount));
for _ in 0..3 {
writer.write_u16be(0);
}
writer.write_raw(locator);
Ok(())
}
pub fn build_lob_create_temp_payload_with_seq(
ora_type_num: u8,
csfrm: u8,
seq_num: u8,
ttc_field_version: u8,
) -> Result<Vec<u8>> {
let mut writer = TtcWriter::new();
write_lob_op_header(
&mut writer,
&[0; 40],
seq_num,
ttc_field_version,
TNS_LOB_OP_CREATE_TEMP,
TNS_DURATION_SESSION,
u64::from(csfrm),
u64::from(ora_type_num),
true,
true,
false,
)?;
writer.write_ub4(TNS_CHARSET_UTF8.into());
Ok(writer.into_bytes())
}
pub fn build_lob_write_payload_with_seq(
locator: &[u8],
offset: u64,
data: &[u8],
seq_num: u8,
ttc_field_version: u8,
) -> Result<Vec<u8>> {
let mut writer = TtcWriter::new();
write_lob_op_header(
&mut writer,
locator,
seq_num,
ttc_field_version,
TNS_LOB_OP_WRITE,
0,
offset,
0,
false,
false,
false,
)?;
writer.write_u8(TNS_MSG_TYPE_LOB_DATA);
writer.write_bytes_with_length(data)?;
Ok(writer.into_bytes())
}
pub fn build_lob_trim_payload_with_seq(
locator: &[u8],
new_size: u64,
seq_num: u8,
ttc_field_version: u8,
) -> Result<Vec<u8>> {
let mut writer = TtcWriter::new();
write_lob_op_header(
&mut writer,
locator,
seq_num,
ttc_field_version,
TNS_LOB_OP_TRIM,
0,
0,
0,
false,
false,
true,
)?;
writer.write_ub8(new_size);
Ok(writer.into_bytes())
}
pub fn lob_locator_is_temporary(locator: &[u8]) -> bool {
locator
.get(TNS_LOB_LOC_OFFSET_FLAG_1)
.is_some_and(|flags| flags & TNS_LOB_LOC_FLAGS_ABSTRACT != 0)
|| locator
.get(TNS_LOB_LOC_OFFSET_FLAG_4)
.is_some_and(|flags| flags & TNS_LOB_LOC_FLAGS_TEMP != 0)
}
pub fn build_lob_free_temp_payload_with_seq(
locators: &[Vec<u8>],
seq_num: u8,
ttc_field_version: u8,
) -> Result<Vec<u8>> {
let total_size = locators.iter().try_fold(0u32, |total, locator| {
let locator_len =
u32::try_from(locator.len()).map_err(|_| ProtocolError::InvalidPacketLength {
length: locator.len(),
minimum: 0,
})?;
total
.checked_add(locator_len)
.ok_or(ProtocolError::PacketTooLarge { length: usize::MAX })
})?;
let mut writer = TtcWriter::new();
writer.write_function_code_with_seq(TNS_FUNC_LOB_OP, seq_num);
if ttc_field_version >= TNS_CCAP_FIELD_VERSION_23_1_EXT_1 {
writer.write_ub8(0);
}
writer.write_u8(1);
writer.write_ub4(total_size);
writer.write_u8(0);
writer.write_ub4(0);
writer.write_ub4(0);
writer.write_ub4(0);
writer.write_u8(0);
writer.write_u8(0);
writer.write_u8(0);
writer.write_ub4(TNS_LOB_OP_FREE_TEMP | TNS_LOB_OP_ARRAY);
writer.write_u8(0);
writer.write_ub4(0);
writer.write_ub8(0);
writer.write_ub8(0);
writer.write_u8(0);
writer.write_u8(0);
writer.write_ub4(0);
writer.write_u8(0);
writer.write_ub4(0);
writer.write_u8(0);
writer.write_ub4(0);
for locator in locators {
writer.write_raw(locator);
}
Ok(writer.into_bytes())
}
pub fn parse_lob_read_response(
payload: &[u8],
capabilities: ClientCapabilities,
locator: &[u8],
) -> Result<LobReadResult> {
parse_lob_read_response_with_limits(payload, capabilities, locator, ProtocolLimits::DEFAULT)
}
pub fn parse_lob_read_response_with_limits(
payload: &[u8],
capabilities: ClientCapabilities,
locator: &[u8],
limits: ProtocolLimits,
) -> Result<LobReadResult> {
parse_lob_op_response_with_limits(payload, capabilities, locator, false, true, limits)
}
pub fn parse_lob_create_temp_response(
payload: &[u8],
capabilities: ClientCapabilities,
) -> Result<LobReadResult> {
parse_lob_create_temp_response_with_limits(payload, capabilities, ProtocolLimits::DEFAULT)
}
pub fn parse_lob_create_temp_response_with_limits(
payload: &[u8],
capabilities: ClientCapabilities,
limits: ProtocolLimits,
) -> Result<LobReadResult> {
parse_lob_op_response_with_limits(payload, capabilities, &[0; 40], true, false, limits)
}
pub fn parse_lob_write_response(
payload: &[u8],
capabilities: ClientCapabilities,
locator: &[u8],
) -> Result<LobReadResult> {
parse_lob_write_response_with_limits(payload, capabilities, locator, ProtocolLimits::DEFAULT)
}
pub fn parse_lob_write_response_with_limits(
payload: &[u8],
capabilities: ClientCapabilities,
locator: &[u8],
limits: ProtocolLimits,
) -> Result<LobReadResult> {
parse_lob_op_response_with_limits(payload, capabilities, locator, false, false, limits)
}
pub fn parse_lob_trim_response(
payload: &[u8],
capabilities: ClientCapabilities,
locator: &[u8],
) -> Result<LobReadResult> {
parse_lob_trim_response_with_limits(payload, capabilities, locator, ProtocolLimits::DEFAULT)
}
pub fn parse_lob_trim_response_with_limits(
payload: &[u8],
capabilities: ClientCapabilities,
locator: &[u8],
limits: ProtocolLimits,
) -> Result<LobReadResult> {
parse_lob_op_response_with_limits(payload, capabilities, locator, false, true, limits)
}
pub fn parse_lob_free_temp_response(
payload: &[u8],
capabilities: ClientCapabilities,
returned_parameter_len: usize,
) -> Result<()> {
parse_lob_free_temp_response_with_limits(
payload,
capabilities,
returned_parameter_len,
ProtocolLimits::DEFAULT,
)
}
pub fn parse_lob_free_temp_response_with_limits(
payload: &[u8],
capabilities: ClientCapabilities,
returned_parameter_len: usize,
limits: ProtocolLimits,
) -> Result<()> {
limits.check_frame_bytes(returned_parameter_len)?;
let mut reader = TtcReader::with_limits(payload, limits)?;
while reader.remaining() > 0 {
let message_type = reader.read_u8()?;
match message_type {
0 => {}
TNS_MSG_TYPE_STATUS => {
let _call_status = reader.read_ub4()?;
let _seq = reader.read_ub2()?;
}
TNS_MSG_TYPE_SERVER_SIDE_PIGGYBACK => {
let _ = skip_server_side_piggyback(&mut reader)?;
}
TNS_MSG_TYPE_END_OF_RESPONSE => break,
TNS_MSG_TYPE_ERROR => {
let info = parse_server_error_info(&mut reader, capabilities.ttc_field_version)?;
if info.number != 0 {
return Err(ProtocolError::ServerError(info.message));
}
}
TNS_MSG_TYPE_PARAMETER => reader.skip(returned_parameter_len)?,
_ => {
return Err(ProtocolError::UnknownMessageType {
message_type,
position: reader.position().saturating_sub(1),
})
}
}
}
Ok(())
}
pub fn parse_plain_function_response(
payload: &[u8],
capabilities: ClientCapabilities,
) -> Result<bool> {
parse_plain_function_response_with_limits(payload, capabilities, ProtocolLimits::DEFAULT)
}
pub fn parse_plain_function_response_with_limits(
payload: &[u8],
capabilities: ClientCapabilities,
limits: ProtocolLimits,
) -> Result<bool> {
let mut reader = TtcReader::with_limits(payload, limits)?;
let mut txn_in_progress = false;
while reader.remaining() > 0 {
let message_type = reader.read_u8()?;
match message_type {
0 => {}
TNS_MSG_TYPE_STATUS => {
let call_status = reader.read_ub4()?;
let _seq = reader.read_ub2()?;
txn_in_progress = call_status & TNS_EOCS_FLAGS_TXN_IN_PROGRESS != 0;
}
TNS_MSG_TYPE_SERVER_SIDE_PIGGYBACK => {
let _ = skip_server_side_piggyback(&mut reader)?;
}
TNS_MSG_TYPE_END_OF_RESPONSE => break,
TNS_MSG_TYPE_ERROR => {
let info = parse_server_error_info(&mut reader, capabilities.ttc_field_version)?;
txn_in_progress = info.call_status & TNS_EOCS_FLAGS_TXN_IN_PROGRESS != 0;
if info.number != 0 {
return Err(ProtocolError::ServerError(info.message));
}
}
_ => break,
}
}
Ok(txn_in_progress)
}
pub(crate) fn parse_lob_op_response_with_limits(
payload: &[u8],
capabilities: ClientCapabilities,
locator: &[u8],
is_create_temp: bool,
read_amount: bool,
limits: ProtocolLimits,
) -> Result<LobReadResult> {
let mut reader = TtcReader::with_limits(payload, limits)?;
let mut result = LobReadResult {
locator: locator.to_vec(),
..LobReadResult::default()
};
while reader.remaining() > 0 {
let message_type = reader.read_u8()?;
match message_type {
0 => {}
TNS_MSG_TYPE_LOB_DATA => {
result.data = reader.read_bytes()?;
}
TNS_MSG_TYPE_PARAMETER => {
if !result.locator.is_empty() {
result.locator = reader.read_raw(result.locator.len())?.to_vec();
}
if is_create_temp {
let _charset = reader.read_ub2()?;
reader.skip(1)?;
} else if read_amount {
let amount = reader.read_sb8()?;
if amount > 0 {
result.amount = amount as u64;
}
}
}
TNS_MSG_TYPE_STATUS => {
let _call_status = reader.read_ub4()?;
let _seq = reader.read_ub2()?;
}
TNS_MSG_TYPE_SERVER_SIDE_PIGGYBACK => {
let _ = skip_server_side_piggyback(&mut reader)?;
}
TNS_MSG_TYPE_END_OF_RESPONSE => break,
TNS_MSG_TYPE_ERROR => {
let info = parse_server_error_info(&mut reader, capabilities.ttc_field_version)?;
if info.number != 0 {
return Err(ProtocolError::ServerError(info.message));
}
}
_ => {
return Err(ProtocolError::UnknownMessageType {
message_type,
position: reader.position().saturating_sub(1),
})
}
}
}
Ok(result)
}