use bytes::BytesMut;
use qail_core::ast::Value;
#[inline]
pub fn encode_copy_value(buf: &mut BytesMut, value: &Value) {
match value {
Value::Null | Value::NullUuid => buf.extend_from_slice(b"\\N"),
Value::Bool(b) => buf.extend_from_slice(if *b { b"t" } else { b"f" }),
Value::Int(n) => {
let mut tmp = itoa::Buffer::new();
buf.extend_from_slice(tmp.format(*n).as_bytes());
}
Value::Float(n) => {
let mut tmp = ryu::Buffer::new();
buf.extend_from_slice(tmp.format(*n).as_bytes());
}
Value::String(s) => {
for c in s.bytes() {
match c {
b'\\' => buf.extend_from_slice(b"\\\\"),
b'\t' => buf.extend_from_slice(b"\\t"),
b'\n' => buf.extend_from_slice(b"\\n"),
b'\r' => buf.extend_from_slice(b"\\r"),
_ => buf.extend_from_slice(&[c]),
}
}
}
Value::Uuid(u) => {
let mut uuid_buf = [0u8; 36];
u.hyphenated().encode_lower(&mut uuid_buf);
buf.extend_from_slice(&uuid_buf);
}
Value::Timestamp(ts) => buf.extend_from_slice(ts.as_bytes()),
Value::Column(s) => buf.extend_from_slice(s.as_bytes()),
Value::Function(s) => buf.extend_from_slice(s.as_bytes()),
Value::Param(n) => {
buf.extend_from_slice(b"$");
let mut tmp = itoa::Buffer::new();
buf.extend_from_slice(tmp.format(*n).as_bytes());
}
Value::NamedParam(name) => {
buf.extend_from_slice(b":");
buf.extend_from_slice(name.as_bytes());
}
Value::Array(arr) => {
buf.extend_from_slice(b"{");
for (i, v) in arr.iter().enumerate() {
if i > 0 {
buf.extend_from_slice(b",");
}
encode_copy_value(buf, v);
}
buf.extend_from_slice(b"}");
}
Value::Interval { amount, unit } => {
let mut tmp = itoa::Buffer::new();
buf.extend_from_slice(tmp.format(*amount).as_bytes());
buf.extend_from_slice(b" ");
buf.extend_from_slice(unit.to_string().as_bytes());
}
Value::Subquery(_) => {
buf.extend_from_slice(b"\\N");
}
Value::Bytes(bytes) => {
buf.extend_from_slice(b"\\\\x");
for byte in bytes {
let hi = byte >> 4;
let lo = byte & 0x0f;
buf.extend_from_slice(&[
if hi < 10 { b'0' + hi } else { b'a' + hi - 10 },
if lo < 10 { b'0' + lo } else { b'a' + lo - 10 },
]);
}
}
Value::Expr(_) => {
buf.extend_from_slice(b"\\N");
}
Value::Vector(vec) => {
buf.extend_from_slice(b"{");
for (i, v) in vec.iter().enumerate() {
if i > 0 {
buf.extend_from_slice(b",");
}
let mut tmp = ryu::Buffer::new();
buf.extend_from_slice(tmp.format(*v).as_bytes());
}
buf.extend_from_slice(b"}");
}
Value::Json(json) => {
for c in json.bytes() {
match c {
b'\\' => buf.extend_from_slice(b"\\\\"),
b'\t' => buf.extend_from_slice(b"\\t"),
b'\n' => buf.extend_from_slice(b"\\n"),
b'\r' => buf.extend_from_slice(b"\\r"),
_ => buf.extend_from_slice(&[c]),
}
}
}
}
}
#[inline]
pub fn encode_copy_batch(rows: &[Vec<Value>]) -> BytesMut {
let estimated_size = rows.len() * 7 * 50;
let mut buf = BytesMut::with_capacity(estimated_size);
for row in rows {
for (i, val) in row.iter().enumerate() {
if i > 0 {
buf.extend_from_slice(b"\t");
}
encode_copy_value(&mut buf, val);
}
buf.extend_from_slice(b"\n");
}
buf
}
#[cfg(test)]
mod tests {
use super::*;
use uuid::Uuid;
#[test]
fn test_encode_int() {
let mut buf = BytesMut::new();
encode_copy_value(&mut buf, &Value::Int(12345));
assert_eq!(&buf[..], b"12345");
}
#[test]
fn test_encode_float() {
let mut buf = BytesMut::new();
encode_copy_value(&mut buf, &Value::Float(9.87654));
assert!(buf.starts_with(b"9.87"));
}
#[test]
fn test_encode_string_escaping() {
let mut buf = BytesMut::new();
encode_copy_value(&mut buf, &Value::String("hello\tworld\n".to_string()));
assert_eq!(&buf[..], b"hello\\tworld\\n");
}
#[test]
fn test_encode_null() {
let mut buf = BytesMut::new();
encode_copy_value(&mut buf, &Value::Null);
assert_eq!(&buf[..], b"\\N");
}
#[test]
fn test_encode_batch() {
let rows = vec![
vec![Value::Int(1), Value::String("foo".to_string())],
vec![Value::Int(2), Value::String("bar".to_string())],
];
let buf = encode_copy_batch(&rows);
assert_eq!(&buf[..], b"1\tfoo\n2\tbar\n");
}
#[test]
fn test_encode_uuid() {
let mut buf = BytesMut::new();
let uuid = Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap();
encode_copy_value(&mut buf, &Value::Uuid(uuid));
assert_eq!(&buf[..], b"550e8400-e29b-41d4-a716-446655440000");
}
}