1use crate::{client::FirebirdWireConnection, consts};
2use bytes::{BufMut, Bytes, BytesMut};
3use rsfbclient_core::{FbError, SqlType};
4
5pub const MAX_DATA_LENGTH: usize = 32767;
7
8#[derive(Debug)]
9pub struct ParamsBlr {
11 pub(crate) blr: Bytes,
13 pub(crate) values: Bytes,
15}
16
17pub fn params_to_blr(
19 conn: &mut FirebirdWireConnection,
20 tr_handle: &mut crate::TrHandle,
21 params: &[SqlType],
22) -> Result<ParamsBlr, FbError> {
23 let mut blr = BytesMut::with_capacity(256);
24 let mut values = BytesMut::with_capacity(256);
25
26 blr.put_slice(&[
27 consts::blr::VERSION5,
28 consts::blr::BEGIN,
29 consts::blr::MESSAGE,
30 0, ]);
32 blr.put_u16_le(params.len() as u16 * 2);
34
35 if conn.version >= consts::ProtocolVersion::V13 {
36 null_bitmap(&mut values, params);
38 }
39
40 let mut handle_blob = |conn: &mut FirebirdWireConnection,
42 blr: &mut BytesMut,
43 values: &mut BytesMut,
44 data: &[u8]| {
45 let (blob_handle, id) = conn.create_blob(tr_handle)?;
46
47 conn.put_segments(blob_handle, data)?;
48
49 conn.close_blob(blob_handle)?;
50
51 blr.put_u8(consts::blr::QUAD);
52 blr.put_u8(0); values.put_u64(id.0);
55
56 Ok::<_, FbError>(())
57 };
58
59 for p in params {
60 match p {
61 SqlType::Text(s) => {
62 let bytes = conn.charset.encode(s)?;
63 if bytes.len() > MAX_DATA_LENGTH {
64 handle_blob(conn, &mut blr, &mut values, &bytes)?;
66 } else {
67 blr.put_u8(consts::blr::TEXT);
68 blr.put_u16_le(bytes.len() as u16);
69
70 values.put_slice(&bytes);
71 if bytes.len() % 4 != 0 {
72 values.put_slice(&[0; 4][..4 - (bytes.len() as usize % 4)])
74 }
75 }
76 }
77
78 SqlType::Binary(data) => handle_blob(conn, &mut blr, &mut values, data)?,
79
80 SqlType::Integer(i) => {
81 blr.put_slice(&[
82 consts::blr::INT64,
83 0, ]);
85
86 values.put_i64(*i);
87 }
88
89 SqlType::Floating(f) => {
90 blr.put_u8(consts::blr::DOUBLE);
91
92 values.put_f64(*f);
93 }
94
95 SqlType::Timestamp(dt) => {
96 blr.put_u8(consts::blr::TIMESTAMP);
97
98 let ts = rsfbclient_core::date_time::encode_timestamp(*dt);
99 values.put_i32(ts.timestamp_date);
100 values.put_u32(ts.timestamp_time);
101 }
102
103 SqlType::Boolean(b) => {
104 blr.put_u8(consts::blr::BOOL);
105
106 values.put_slice(if *b { &[1, 0, 0, 0] } else { &[0, 0, 0, 0] });
107 }
108
109 SqlType::Null => {
110 blr.put_u8(consts::blr::TEXT);
112 blr.put_u16_le(0);
113 }
114 }
115
116 if conn.version < consts::ProtocolVersion::V13 {
117 values.put_i32_le(if p.is_null() { -1 } else { 0 });
119 }
120
121 blr.put_slice(&[consts::blr::SHORT, 0]);
123 }
124
125 blr.put_slice(&[consts::blr::END, consts::blr::EOC]);
126
127 Ok(ParamsBlr {
128 blr: blr.freeze(),
129 values: values.freeze(),
130 })
131}
132
133fn null_bitmap(values: &mut BytesMut, params: &[SqlType]) {
141 for bitmap in params.chunks(32).map(|params| {
142 params.iter().enumerate().fold(0, |bitmap, (i, p)| {
143 if p.is_null() {
144 bitmap | (1 << i)
145 } else {
146 bitmap
147 }
148 })
149 }) {
150 values.put_u32_le(bitmap);
151 }
152}