use crate::mysql::mysql_param::Param;
use crate::mysql::mysql_types::FieldType;
use crate::mysql::protocol::column_definition41::ColumnFlags;
use crate::mysql::protocol::command_type::CommandType;
use crate::mysql::protocol::new_writer::{NewWriter, WriterContext};
use crate::shared::data::Data;
bun_core::declare_scope!(MySQLQuery, visible);
pub struct Execute<'a> {
pub query: &'a [u8],
pub params: &'a mut [Data],
pub param_types: &'a [Param],
}
impl<'a> Execute<'a> {
pub fn write_internal<C: WriterContext>(
&self,
writer: NewWriter<C>,
) -> Result<(), bun_core::Error> {
let mut packet = writer.start(0)?;
writer.int1(CommandType::COM_QUERY as u8)?;
writer.write(self.query)?;
if !self.params.is_empty() {
writer.write_null_bitmap(self.params)?;
writer.int1(1)?;
let mut param_name_buf = [0u8; 22];
for (param_type, i) in self.param_types.iter().zip(1usize..) {
let unsigned = param_type.flags.contains(ColumnFlags::UNSIGNED);
bun_core::scoped_log!(
MySQLQuery,
"New params bind flag {} unsigned? {}",
<&'static str>::from(param_type.r#type),
unsigned,
);
writer.int1(param_type.r#type as u8)?;
writer.int1(if unsigned { 0x80 } else { 0 })?;
let param_name = {
use std::io::Write;
let mut cursor = std::io::Cursor::new(&mut param_name_buf[..]);
write!(&mut cursor, ":p{}", i)
.map_err(|_| bun_core::err!("TooManyParameters"))?;
let len = usize::try_from(cursor.position()).expect("int cast");
¶m_name_buf[..len]
};
writer.write_length_encoded_string(param_name)?;
}
debug_assert_eq!(self.params.len(), self.param_types.len());
for (param, param_type) in self.params.iter().zip(self.param_types.iter()) {
if matches!(param, Data::Empty) || param_type.r#type == FieldType::MYSQL_TYPE_NULL {
continue;
}
let value = param.slice();
bun_core::scoped_log!(
MySQLQuery,
"Write param type {} len {} hex {:02x?}",
<&'static str>::from(param_type.r#type),
value.len(),
value,
);
if param_type.r#type.is_binary_format_supported() {
writer.write(value)?;
} else {
writer.write_length_encoded_string(value)?;
}
}
}
packet.end()?;
Ok(())
}
pub fn write<C: WriterContext>(&self, writer: NewWriter<C>) -> Result<(), bun_core::Error> {
self.write_internal(writer)
}
}
pub fn execute<C: WriterContext>(
query: &[u8],
writer: NewWriter<C>,
) -> Result<(), bun_core::Error> {
let mut packet = writer.start(0)?;
writer.int1(CommandType::COM_QUERY as u8)?;
writer.write(query)?;
packet.end()?;
Ok(())
}