use crate::{client::FirebirdWireConnection, consts};
use bytes::{BufMut, Bytes, BytesMut};
use rsfbclient_core::{FbError, SqlType};
pub const MAX_DATA_LENGTH: usize = 32767;
#[derive(Debug)]
pub struct ParamsBlr {
pub(crate) blr: Bytes,
pub(crate) values: Bytes,
}
pub fn params_to_blr(
conn: &mut FirebirdWireConnection,
tr_handle: &mut crate::TrHandle,
params: &[SqlType],
) -> Result<ParamsBlr, FbError> {
let mut blr = BytesMut::with_capacity(256);
let mut values = BytesMut::with_capacity(256);
blr.put_slice(&[
consts::blr::VERSION5,
consts::blr::BEGIN,
consts::blr::MESSAGE,
0, ]);
blr.put_u16_le(params.len() as u16 * 2);
if conn.version >= consts::ProtocolVersion::V13 {
null_bitmap(&mut values, params);
}
let mut handle_blob = |conn: &mut FirebirdWireConnection,
blr: &mut BytesMut,
values: &mut BytesMut,
data: &[u8]| {
let (blob_handle, id) = conn.create_blob(tr_handle)?;
conn.put_segments(blob_handle, data)?;
conn.close_blob(blob_handle)?;
blr.put_u8(consts::blr::QUAD);
blr.put_u8(0);
values.put_u64(id.0);
Ok::<_, FbError>(())
};
for p in params {
match p {
SqlType::Text(s) => {
let bytes = conn.charset.encode(s)?;
if bytes.len() > MAX_DATA_LENGTH {
handle_blob(conn, &mut blr, &mut values, &bytes)?;
} else {
blr.put_u8(consts::blr::TEXT);
blr.put_u16_le(bytes.len() as u16);
values.put_slice(&bytes);
if bytes.len() % 4 != 0 {
values.put_slice(&[0; 4][..4 - (bytes.len() as usize % 4)])
}
}
}
SqlType::Binary(data) => handle_blob(conn, &mut blr, &mut values, data)?,
SqlType::Integer(i) => {
blr.put_slice(&[
consts::blr::INT64,
0, ]);
values.put_i64(*i);
}
SqlType::Floating(f) => {
blr.put_u8(consts::blr::DOUBLE);
values.put_f64(*f);
}
SqlType::Timestamp(dt) => {
blr.put_u8(consts::blr::TIMESTAMP);
let ts = rsfbclient_core::date_time::encode_timestamp(*dt);
values.put_i32(ts.timestamp_date);
values.put_u32(ts.timestamp_time);
}
SqlType::Boolean(b) => {
blr.put_u8(consts::blr::BOOL);
values.put_slice(if *b { &[1, 0, 0, 0] } else { &[0, 0, 0, 0] });
}
SqlType::Null => {
blr.put_u8(consts::blr::TEXT);
blr.put_u16_le(0);
}
}
if conn.version < consts::ProtocolVersion::V13 {
values.put_i32_le(if p.is_null() { -1 } else { 0 });
}
blr.put_slice(&[consts::blr::SHORT, 0]);
}
blr.put_slice(&[consts::blr::END, consts::blr::EOC]);
Ok(ParamsBlr {
blr: blr.freeze(),
values: values.freeze(),
})
}
fn null_bitmap(values: &mut BytesMut, params: &[SqlType]) {
for bitmap in params.chunks(32).map(|params| {
params.iter().enumerate().fold(0, |bitmap, (i, p)| {
if p.is_null() {
bitmap | (1 << i)
} else {
bitmap
}
})
}) {
values.put_u32_le(bitmap);
}
}