diff --git a/diesel/src/sqlite/types/json.rs b/diesel/src/sqlite/types/json.rs index 91fe45ee0b7f..58511806f306 100644 --- a/diesel/src/sqlite/types/json.rs +++ b/diesel/src/sqlite/types/json.rs @@ -1,30 +1,18 @@ -extern crate serde_json; +//! Support for JSON and JSONB values under PostgreSQL. use crate::deserialize::{self, FromSql}; use crate::serialize::{self, IsNull, Output, ToSql}; use crate::sql_types; use crate::sqlite::{Sqlite, SqliteValue}; -const JSONB_NULL: u8 = 0x00; -const JSONB_TRUE: u8 = 0x01; -const JSONB_FALSE: u8 = 0x02; -const JSONB_INT: u8 = 0x03; -const JSONB_INT5: u8 = 0x04; -const JSONB_FLOAT: u8 = 0x05; -const JSONB_FLOAT5: u8 = 0x06; -const JSONB_TEXT: u8 = 0x07; -const JSONB_TEXTJ: u8 = 0x08; -const JSONB_TEXT5: u8 = 0x09; -const JSONB_TEXTRAW: u8 = 0x0A; -const JSONB_ARRAY: u8 = 0x0B; -const JSONB_OBJECT: u8 = 0x0C; - +#[cfg(all(feature = "sqlite", feature = "serde_json"))] impl FromSql for serde_json::Value { fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result { serde_json::from_str(value.read_text()).map_err(|_| "Invalid Json".into()) } } +#[cfg(all(feature = "sqlite", feature = "serde_json"))] impl ToSql for serde_json::Value { fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result { out.set_value(serde_json::to_string(self)?); @@ -32,8 +20,11 @@ impl ToSql for serde_json::Value { } } +#[cfg(all(feature = "sqlite", feature = "serde_json"))] impl FromSql for serde_json::Value { fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result { + use self::jsonb::*; + let bytes = value.read_blob(); if bytes.is_empty() { @@ -47,8 +38,11 @@ impl FromSql for serde_json::Value { } } +#[cfg(all(feature = "sqlite", feature = "serde_json"))] impl ToSql for serde_json::Value { fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result { + use self::jsonb::*; + // Create a buffer to hold the binary JSONB encoding let mut buffer = Vec::new(); @@ -62,396 +56,469 @@ impl ToSql for serde_json::Value { } } -// Helper function to read a JSONB value from the byte stream -fn read_jsonb_value(bytes: &[u8]) -> deserialize::Result<(serde_json::Value, usize)> { - if bytes.is_empty() { - return Err("Empty JSONB data".into()); - } +#[cfg(all(feature = "sqlite", feature = "serde_json"))] +mod jsonb { + extern crate serde_json; - // The first byte contains both the element type and potentially the payload size - let first_byte = bytes[0]; - let element_type = first_byte & 0x0F; - let size_hint = (first_byte & 0xF0) >> 4; + use super::*; - let (payload_size, header_size) = match size_hint { - 0x00..=0x0B => (size_hint as usize, 1), // Payload size is directly in the upper nibble - 0x0C => { - if bytes.len() < 2 { - return Err("Invalid JSONB data: insufficient bytes for payload size".into()); - } - (bytes[1] as usize, 2) // 1 additional byte for payload size + pub(super) const JSONB_NULL: u8 = 0x00; + pub(super) const JSONB_TRUE: u8 = 0x01; + pub(super) const JSONB_FALSE: u8 = 0x02; + pub(super) const JSONB_INT: u8 = 0x03; + pub(super) const JSONB_INT5: u8 = 0x04; + pub(super) const JSONB_FLOAT: u8 = 0x05; + pub(super) const JSONB_FLOAT5: u8 = 0x06; + pub(super) const JSONB_TEXT: u8 = 0x07; + pub(super) const JSONB_TEXTJ: u8 = 0x08; + pub(super) const JSONB_TEXT5: u8 = 0x09; + pub(super) const JSONB_TEXTRAW: u8 = 0x0A; + pub(super) const JSONB_ARRAY: u8 = 0x0B; + pub(super) const JSONB_OBJECT: u8 = 0x0C; + + // Helper function to read a JSONB value from the byte stream + pub(super) fn read_jsonb_value( + bytes: &[u8], + ) -> deserialize::Result<(serde_json::Value, usize)> { + if bytes.is_empty() { + return Err("Empty JSONB data".into()); } - 0x0D => { - if bytes.len() < 3 { - return Err("Invalid JSONB data: insufficient bytes for payload size".into()); + + // The first byte contains both the element type and potentially the payload size + let first_byte = bytes[0]; + let element_type = first_byte & 0x0F; + let size_hint = (first_byte & 0xF0) >> 4; + + let (payload_size, header_size) = match size_hint { + 0x00..=0x0B => (size_hint as usize, 1), // Payload size is directly in the upper nibble + 0x0C => { + if bytes.len() < 2 { + return Err("Invalid JSONB data: insufficient bytes for payload size".into()); + } + (bytes[1] as usize, 2) // 1 additional byte for payload size } - (u16::from_be_bytes([bytes[1], bytes[2]]) as usize, 3) // 2 additional bytes - } - 0x0E => { - if bytes.len() < 5 { - return Err("Invalid JSONB data: insufficient bytes for payload size".into()); + 0x0D => { + if bytes.len() < 3 { + return Err("Invalid JSONB data: insufficient bytes for payload size".into()); + } + (u16::from_be_bytes([bytes[1], bytes[2]]) as usize, 3) // 2 additional bytes } - ( - u32::from_be_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]) as usize, - 5, - ) // 4 additional bytes - } - 0x0F => { - if bytes.len() < 9 { - return Err("Invalid JSONB data: insufficient bytes for payload size".into()); + 0x0E => { + if bytes.len() < 5 { + return Err("Invalid JSONB data: insufficient bytes for payload size".into()); + } + ( + u32::from_be_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]) as usize, + 5, + ) // 4 additional bytes } - ( - usize::try_from(u64::from_be_bytes([ - bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], - ])) - .map_err(Box::new)?, - 9, - ) // 8 additional bytes + 0x0F => { + if bytes.len() < 9 { + return Err("Invalid JSONB data: insufficient bytes for payload size".into()); + } + ( + usize::try_from(u64::from_be_bytes([ + bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], + bytes[8], + ])) + .map_err(Box::new)?, + 9, + ) // 8 additional bytes + } + _ => return Err("Invalid payload size hint".into()), + }; + + let total_size = header_size + payload_size; + if bytes.len() < total_size { + return Err(format!( + "Invalid JSONB data: insufficient bytes for value of type {}, expected {} bytes, got {}", + element_type, + total_size, + bytes.len() + ) + .into()); } - _ => return Err("Invalid payload size hint".into()), - }; - - let total_size = header_size + payload_size; - if bytes.len() < total_size { - return Err(format!( - "Invalid JSONB data: insufficient bytes for value of type {}, expected {} bytes, got {}", - element_type, - total_size, - bytes.len() - ) - .into()); - } - - let payload_bytes = &bytes[header_size..total_size]; - - let value = match element_type { - JSONB_NULL => Ok(serde_json::Value::Null), - JSONB_TRUE => Ok(serde_json::Value::Bool(true)), - JSONB_FALSE => Ok(serde_json::Value::Bool(false)), - JSONB_INT => read_jsonb_int(payload_bytes, payload_size), - JSONB_INT5 => Err("INT5 is not supported".into()), - JSONB_FLOAT => read_jsonb_float(payload_bytes, payload_size), - JSONB_FLOAT5 => Err("FLOAT5 is not supported".into()), - JSONB_TEXT => read_jsonb_text(payload_bytes, payload_size), - JSONB_TEXTJ => read_jsonb_textj(payload_bytes, payload_size), - JSONB_TEXTRAW => Err("TEXTRAW is not supported".into()), - JSONB_TEXT5 => Err("TEXT5 is not supported".into()), - JSONB_ARRAY => read_jsonb_array(payload_bytes, payload_size), - JSONB_OBJECT => read_jsonb_object(payload_bytes, payload_size), - _ => Err(format!("Unsupported or reserved JSONB type: {}", element_type).into()), - }?; - - Ok((value, total_size)) -} - -// Read a JSON integer in canonical format (INT) -fn read_jsonb_int(bytes: &[u8], payload_size: usize) -> deserialize::Result { - // Ensure the bytes are at least as large as the payload size - if bytes.len() < payload_size { - return Err(format!( - "Expected payload of size {}, but got {}", - payload_size, - bytes.len() - ) - .into()); - } - // Read only the number of bytes specified by the payload size - let int_str = std::str::from_utf8(bytes).map_err(|_| "Invalid ASCII in JSONB integer")?; - // Parse the integer string into an i64 - let int_value = int_str - .parse::() - .map_err(|_| "Failed to parse JSONB integer")?; + let payload_bytes = &bytes[header_size..total_size]; + + let value = match element_type { + JSONB_NULL => Ok(serde_json::Value::Null), + JSONB_TRUE => Ok(serde_json::Value::Bool(true)), + JSONB_FALSE => Ok(serde_json::Value::Bool(false)), + JSONB_INT => read_jsonb_int(payload_bytes, payload_size), + JSONB_INT5 => Err("INT5 is not supported".into()), + JSONB_FLOAT => read_jsonb_float(payload_bytes, payload_size), + JSONB_FLOAT5 => Err("FLOAT5 is not supported".into()), + JSONB_TEXT => read_jsonb_text(payload_bytes, payload_size), + JSONB_TEXTJ => read_jsonb_textj(payload_bytes, payload_size), + JSONB_TEXTRAW => Err("TEXTRAW is not supported".into()), + JSONB_TEXT5 => Err("TEXT5 is not supported".into()), + JSONB_ARRAY => read_jsonb_array(payload_bytes, payload_size), + JSONB_OBJECT => read_jsonb_object(payload_bytes, payload_size), + _ => Err(format!("Unsupported or reserved JSONB type: {}", element_type).into()), + }?; + + Ok((value, total_size)) + } + + // Read a JSON integer in canonical format (INT) + pub(super) fn read_jsonb_int( + bytes: &[u8], + payload_size: usize, + ) -> deserialize::Result { + // Ensure the bytes are at least as large as the payload size + if bytes.len() < payload_size { + return Err(format!( + "Expected payload of size {}, but got {}", + payload_size, + bytes.len() + ) + .into()); + } - Ok(serde_json::Value::Number(serde_json::Number::from( - int_value, - ))) -} + // Read only the number of bytes specified by the payload size + let int_str = std::str::from_utf8(bytes).map_err(|_| "Invalid ASCII in JSONB integer")?; + let int_value = serde_json::from_str(int_str) + .map_err(|_| "Failed to parse JSONB") + .and_then(|v: serde_json::Value| { + v.is_i64() + .then_some(v) + .ok_or("Failed to parse JSONB integer") + })?; + + Ok(int_value) + } + + // Read a JSON float in canonical format (FLOAT) + pub(super) fn read_jsonb_float( + bytes: &[u8], + payload_size: usize, + ) -> deserialize::Result { + if bytes.len() < payload_size { + return Err(format!( + "Expected payload of size {}, but got {}", + payload_size, + bytes.len() + ) + .into()); + } -// Read a JSON float in canonical format (FLOAT) -fn read_jsonb_float(bytes: &[u8], payload_size: usize) -> deserialize::Result { - if bytes.len() < payload_size { - return Err(format!( - "Expected payload of size {}, but got {}", - payload_size, - bytes.len() - ) - .into()); - } + let float_str = std::str::from_utf8(bytes).map_err(|_| "Invalid UTF-8 in JSONB float")?; + let float_value = serde_json::from_str(float_str) + .map_err(|_| "Failed to parse JSONB") + .and_then(|v: serde_json::Value| { + v.is_f64() + .then_some(v) + .ok_or("Failed to parse JSONB number") + })?; + + Ok(float_value) + } + + // Read a JSON string + pub(super) fn read_jsonb_text( + bytes: &[u8], + payload_size: usize, + ) -> deserialize::Result { + if bytes.len() < payload_size { + return Err(format!( + "Expected payload of size {}, but got {}", + payload_size, + bytes.len() + ) + .into()); + } - let float_str = std::str::from_utf8(bytes).map_err(|_| "Invalid UTF-8 in JSONB float")?; - let float_value = float_str - .parse::() - .map_err(|_| "Failed to parse JSONB float")?; - Ok(serde_json::Value::Number( - serde_json::Number::from_f64(float_value).ok_or("Invalid float value")?, - )) -} + let text = std::str::from_utf8(bytes).map_err(|_| "Invalid UTF-8 in JSONB string")?; + Ok(serde_json::Value::String(text.to_string())) + } + + pub(super) fn read_jsonb_textj( + bytes: &[u8], + payload_size: usize, + ) -> deserialize::Result { + if bytes.len() < payload_size { + return Err(format!( + "Expected payload of size {}, but got {}", + payload_size, + bytes.len() + ) + .into()); + } -// Read a JSON string -fn read_jsonb_text(bytes: &[u8], payload_size: usize) -> deserialize::Result { - if bytes.len() < payload_size { - return Err(format!( - "Expected payload of size {}, but got {}", - payload_size, - bytes.len() - ) - .into()); - } + let text = std::str::from_utf8(bytes).map_err(|_| "Invalid UTF-8 in JSONB string")?; - let text = std::str::from_utf8(bytes).map_err(|_| "Invalid UTF-8 in JSONB string")?; - Ok(serde_json::Value::String(text.to_string())) -} + // Unescape JSON escape sequences (e.g., "\n", "\u0020") + let unescaped_text = serde_json::from_str(&format!("\"{}\"", text)) + .map_err(|_| "Failed to parse JSON-escaped text in TEXTJ")?; -fn read_jsonb_textj(bytes: &[u8], payload_size: usize) -> deserialize::Result { - if bytes.len() < payload_size { - return Err(format!( - "Expected payload of size {}, but got {}", - payload_size, - bytes.len() - ) - .into()); + Ok(unescaped_text) } - let text = std::str::from_utf8(bytes).map_err(|_| "Invalid UTF-8 in JSONB string")?; + // Read a JSON array + pub(super) fn read_jsonb_array( + bytes: &[u8], + payload_size: usize, + ) -> deserialize::Result { + let mut elements = Vec::new(); + let mut total_read = 0; - // Unescape JSON escape sequences (e.g., "\n", "\u0020") - let unescaped_text = serde_json::from_str(&format!("\"{}\"", text)) - .map_err(|_| "Failed to parse JSON-escaped text in TEXTJ")?; + while total_read < payload_size { + let (element, consumed) = read_jsonb_value(&bytes[total_read..payload_size])?; - Ok(unescaped_text) -} + elements.push(element); + total_read += consumed; + } -// Read a JSON array -fn read_jsonb_array(bytes: &[u8], payload_size: usize) -> deserialize::Result { - let mut elements = Vec::new(); - let mut total_read = 0; + if total_read != payload_size { + return Err("Array payload size mismatch".into()); + } - while total_read < payload_size { - let (element, consumed) = read_jsonb_value(&bytes[total_read..payload_size])?; + Ok(serde_json::Value::Array(elements)) + } + + pub(super) fn read_jsonb_object( + bytes: &[u8], + payload_size: usize, + ) -> deserialize::Result { + let mut object = serde_json::Map::new(); + let mut total_read = 0; + + while total_read < payload_size { + // Read the key (must be a valid JSONB text type) + let (key_value, key_consumed) = read_jsonb_value(&bytes[total_read..])?; + let key_str = key_value + .as_str() + .ok_or("Invalid object key in JSONB, must be a string")? + .to_string(); + total_read += key_consumed; + + // Read the value associated with the key + let (value, value_consumed) = read_jsonb_value(&bytes[total_read..])?; + object.insert(key_str, value); + total_read += value_consumed; + } - elements.push(element); - total_read += consumed; - } + // Ensure the total bytes read match the payload size + if total_read != payload_size { + return Err("Object payload size mismatch".into()); + } - if total_read != payload_size { - return Err("Array payload size mismatch".into()); + Ok(serde_json::Value::Object(object)) } - Ok(serde_json::Value::Array(elements)) -} - -fn read_jsonb_object(bytes: &[u8], payload_size: usize) -> deserialize::Result { - let mut object = serde_json::Map::new(); - let mut total_read = 0; - - while total_read < payload_size { - // Read the key (must be a valid JSONB text type) - let (key_value, key_consumed) = read_jsonb_value(&bytes[total_read..])?; - let key_str = key_value - .as_str() - .ok_or("Invalid object key in JSONB, must be a string")? - .to_string(); - total_read += key_consumed; + // Helper function to create the correct JsonbHeader based on the payload size + pub(super) fn create_jsonb_header( + element_type: u8, + payload_size: usize, + ) -> Result, String> { + // Check if payload size exceeds the maximum allowed size + if payload_size > 2_147_483_647 { + return Err("Payload size exceeds the maximum allowed size of 2GB".into()); + } - // Read the value associated with the key - let (value, value_consumed) = read_jsonb_value(&bytes[total_read..])?; - object.insert(key_str, value); - total_read += value_consumed; + let header = if payload_size <= 0x0B { + // Small payloads, 0 additional byte for size + vec![(u8::try_from(payload_size).map_err(|e| e.to_string())?) << 4 | element_type] + } else if payload_size <= 0xFF { + // Medium payloads, 1 additional byte for size + vec![ + 0x0C << 4 | element_type, + u8::try_from(payload_size).map_err(|e| e.to_string())?, + ] + } else if payload_size <= 0xFFFF { + let mut header = Vec::with_capacity(3); + + // Larger payloads, 2 additional bytes for size + header.push(0x0D << 4 | element_type); + header.extend_from_slice( + &(u16::try_from(payload_size).map_err(|e| e.to_string())?).to_be_bytes(), + ); + + header + } else { + let mut header = Vec::with_capacity(5); + + // Very large payloads, 4 additional bytes for size (up to 2 GiB) + header.push(0x0E << 4 | element_type); + header.extend_from_slice( + &(u32::try_from(payload_size).map_err(|e| e.to_string())?).to_be_bytes(), + ); + + header + }; + + Ok(header) + } + + pub(super) fn write_jsonb_header( + buffer: &mut Vec, + element_type: u8, + payload_size: usize, + ) -> serialize::Result { + // Create the header and append it to the buffer + let header = create_jsonb_header(element_type, payload_size)?; + buffer.extend(header); + Ok(IsNull::No) } - // Ensure the total bytes read match the payload size - if total_read != payload_size { - return Err("Object payload size mismatch".into()); + // Helper function to write a JSON value into a JSONB binary format + pub(super) fn write_jsonb_value( + value: &serde_json::Value, + buffer: &mut Vec, + ) -> serialize::Result { + if value.is_null() { + write_jsonb_null(buffer) + } else if value.is_boolean() { + write_jsonb_bool(value.as_bool().ok_or("Failed to read JSONB value")?, buffer) + } else if value.is_number() { + write_jsonb_number(value, buffer) + } else if value.is_string() { + write_jsonb_string(value.as_str().ok_or("Failed to read JSONB value")?, buffer) + } else if value.is_array() { + write_jsonb_array( + value.as_array().ok_or("Failed to read JSONB value")?, + buffer, + ) + } else if value.is_object() { + write_jsonb_object( + value.as_object().ok_or("Failed to read JSONB value")?, + buffer, + ) + } else { + Err("Unsupported JSONB value type".into()) + } } - Ok(serde_json::Value::Object(object)) -} - -// Helper function to create the correct JsonbHeader based on the payload size -fn create_jsonb_header(element_type: u8, payload_size: usize) -> Result, String> { - // Check if payload size exceeds the maximum allowed size - if payload_size > 2_147_483_647 { - return Err("Payload size exceeds the maximum allowed size of 2GB".into()); - } - - let header = if payload_size <= 0x0B { - // Small payloads, 0 additional byte for size - vec![(u8::try_from(payload_size).map_err(|e| e.to_string())?) << 4 | element_type] - } else if payload_size <= 0xFF { - // Medium payloads, 1 additional byte for size - vec![ - 0x0C << 4 | element_type, - u8::try_from(payload_size).map_err(|e| e.to_string())?, - ] - } else if payload_size <= 0xFFFF { - let mut header = Vec::with_capacity(3); - - // Larger payloads, 2 additional bytes for size - header.push(0x0D << 4 | element_type); - header.extend_from_slice( - &(u16::try_from(payload_size).map_err(|e| e.to_string())?).to_be_bytes(), - ); + // Write a JSON null + pub(super) fn write_jsonb_null(buffer: &mut Vec) -> serialize::Result { + write_jsonb_header(buffer, JSONB_NULL, 0x0)?; + Ok(IsNull::No) + } - header - } else { - let mut header = Vec::with_capacity(5); + // Write a JSON boolean + pub(super) fn write_jsonb_bool(b: bool, buffer: &mut Vec) -> serialize::Result { + // Use the constants for true and false + write_jsonb_header(buffer, if b { JSONB_TRUE } else { JSONB_FALSE }, 0x0)?; + Ok(IsNull::No) + } - // Very large payloads, 4 additional bytes for size (up to 2 GiB) - header.push(0x0E << 4 | element_type); - header.extend_from_slice( - &(u32::try_from(payload_size).map_err(|e| e.to_string())?).to_be_bytes(), - ); + // Write a JSON number (integers and floats) + pub(super) fn write_jsonb_number( + n: &serde_json::Value, + buffer: &mut Vec, + ) -> serialize::Result { + if let Some(i) = n.as_i64() { + // Write an integer (INT type) + write_jsonb_int(i, buffer) + } else if let Some(f) = n.as_f64() { + // Write a float (FLOAT type) + write_jsonb_float(f, buffer) + } else { + Err("Invalid JSONB number type".into()) + } + } - header - }; + // Write an integer in JSONB format + pub(super) fn write_jsonb_int(i: i64, buffer: &mut Vec) -> serialize::Result { + let int_str = i.to_string(); - Ok(header) -} + write_jsonb_header(buffer, JSONB_INT, int_str.len())?; -fn write_jsonb_header( - buffer: &mut Vec, - element_type: u8, - payload_size: usize, -) -> serialize::Result { - // Create the header and append it to the buffer - let header = create_jsonb_header(element_type, payload_size)?; - buffer.extend(header); - Ok(IsNull::No) -} + // Write the ASCII text representation of the integer as the payload + buffer.extend_from_slice(int_str.as_bytes()); -// Helper function to write a JSON value into a JSONB binary format -fn write_jsonb_value(value: &serde_json::Value, buffer: &mut Vec) -> serialize::Result { - match value { - serde_json::Value::Null => write_jsonb_null(buffer), - serde_json::Value::Bool(b) => write_jsonb_bool(*b, buffer), - serde_json::Value::Number(n) => write_jsonb_number(n, buffer), - serde_json::Value::String(s) => write_jsonb_string(s, buffer), - serde_json::Value::Array(arr) => write_jsonb_array(arr, buffer), - serde_json::Value::Object(obj) => write_jsonb_object(obj, buffer), + Ok(IsNull::No) } -} -// Write a JSON null -fn write_jsonb_null(buffer: &mut Vec) -> serialize::Result { - write_jsonb_header(buffer, JSONB_NULL, 0x0)?; - Ok(IsNull::No) -} - -// Write a JSON boolean -fn write_jsonb_bool(b: bool, buffer: &mut Vec) -> serialize::Result { - // Use the constants for true and false - write_jsonb_header(buffer, if b { JSONB_TRUE } else { JSONB_FALSE }, 0x0)?; - Ok(IsNull::No) -} + // Write a floating-point number in JSONB format + pub(super) fn write_jsonb_float(f: f64, buffer: &mut Vec) -> serialize::Result { + let float_str = f.to_string(); -// Write a JSON number (integers and floats) -fn write_jsonb_number(n: &serde_json::Number, buffer: &mut Vec) -> serialize::Result { - if let Some(i) = n.as_i64() { - // Write an integer (INT type) - write_jsonb_int(i, buffer) - } else if let Some(f) = n.as_f64() { - // Write a float (FLOAT type) - write_jsonb_float(f, buffer) - } else { - Err("Invalid number type".into()) - } -} + write_jsonb_header(buffer, JSONB_FLOAT, float_str.len())?; -// Write an integer in JSONB format -fn write_jsonb_int(i: i64, buffer: &mut Vec) -> serialize::Result { - let int_str = i.to_string(); + // Write the ASCII text representation of the float as the payload + buffer.extend_from_slice(float_str.as_bytes()); - write_jsonb_header(buffer, JSONB_INT, int_str.len())?; - - // Write the ASCII text representation of the integer as the payload - buffer.extend_from_slice(int_str.as_bytes()); + Ok(IsNull::No) + } - Ok(IsNull::No) -} + pub(super) fn write_jsonb_string(s: &str, buffer: &mut Vec) -> serialize::Result { + if s.chars().any(|c| c.is_control()) { + // If the string contains control characters, treat it as TEXTJ (escaped JSON) + write_jsonb_textj(s, buffer) + } else { + write_jsonb_header(buffer, JSONB_TEXT, s.len())?; + // Write the UTF-8 text of the string as the payload (no delimiters) + buffer.extend_from_slice(s.as_bytes()); + Ok(IsNull::No) + } + } -// Write a floating-point number in JSONB format -fn write_jsonb_float(f: f64, buffer: &mut Vec) -> serialize::Result { - let float_str = f.to_string(); + pub(super) fn write_jsonb_textj(s: &str, buffer: &mut Vec) -> serialize::Result { + // Escaping the string for JSON (e.g., \n, \uXXXX) + let escaped_string = serde_json::to_string(&String::from(s)) + .map_err(|_| "Failed to serialize string for TEXTJ")?; - write_jsonb_header(buffer, JSONB_FLOAT, float_str.len())?; + // Remove the surrounding quotes from serde_json::to_string result + let escaped_string = &escaped_string[1..escaped_string.len() - 1]; - // Write the ASCII text representation of the float as the payload - buffer.extend_from_slice(float_str.as_bytes()); + // Write the header (JSONB_TEXTJ) and the length of the escaped string + write_jsonb_header(buffer, JSONB_TEXTJ, escaped_string.len())?; - Ok(IsNull::No) -} + // Write the escaped string as the payload + buffer.extend_from_slice(escaped_string.as_bytes()); -fn write_jsonb_string(s: &str, buffer: &mut Vec) -> serialize::Result { - if s.chars().any(|c| c.is_control()) { - // If the string contains control characters, treat it as TEXTJ (escaped JSON) - write_jsonb_textj(s, buffer) - } else { - write_jsonb_header(buffer, JSONB_TEXT, s.len())?; - // Write the UTF-8 text of the string as the payload (no delimiters) - buffer.extend_from_slice(s.as_bytes()); Ok(IsNull::No) } -} - -fn write_jsonb_textj(s: &str, buffer: &mut Vec) -> serialize::Result { - // Escaping the string for JSON (e.g., \n, \uXXXX) - let escaped_string = - serde_json::to_string(s).map_err(|_| "Failed to serialize string for TEXTJ")?; - - // Remove the surrounding quotes from serde_json::to_string result - let escaped_string = &escaped_string[1..escaped_string.len() - 1]; - // Write the header (JSONB_TEXTJ) and the length of the escaped string - write_jsonb_header(buffer, JSONB_TEXTJ, escaped_string.len())?; + // Write a JSON array + pub(super) fn write_jsonb_array( + arr: &[serde_json::Value], + buffer: &mut Vec, + ) -> serialize::Result { + let mut tmp_buffer = Vec::new(); - // Write the escaped string as the payload - buffer.extend_from_slice(escaped_string.as_bytes()); + // Recursively write each element of the array + for element in arr { + write_jsonb_value(element, &mut tmp_buffer)?; + } - Ok(IsNull::No) -} + write_jsonb_header(buffer, JSONB_ARRAY, tmp_buffer.len())?; -// Write a JSON array -fn write_jsonb_array(arr: &[serde_json::Value], buffer: &mut Vec) -> serialize::Result { - let mut tmp_buffer = Vec::new(); + buffer.extend_from_slice(&tmp_buffer); - // Recursively write each element of the array - for element in arr { - write_jsonb_value(element, &mut tmp_buffer)?; + Ok(IsNull::No) } - write_jsonb_header(buffer, JSONB_ARRAY, tmp_buffer.len())?; + // Write a JSON object + pub(super) fn write_jsonb_object( + obj: &serde_json::Map, + buffer: &mut Vec, + ) -> serialize::Result { + let mut tmp_buffer = Vec::new(); - buffer.extend_from_slice(&tmp_buffer); + // Recursively write each key-value pair of the object + for (key, value) in obj { + // Write the key (which must be a string) + write_jsonb_string(key, &mut tmp_buffer)?; - Ok(IsNull::No) -} + // Write the value + write_jsonb_value(value, &mut tmp_buffer)?; + } -// Write a JSON object -fn write_jsonb_object( - obj: &serde_json::Map, - buffer: &mut Vec, -) -> serialize::Result { - let mut tmp_buffer = Vec::new(); + write_jsonb_header(buffer, JSONB_OBJECT, tmp_buffer.len())?; - // Recursively write each key-value pair of the object - for (key, value) in obj { - // Write the key (which must be a string) - write_jsonb_string(key, &mut tmp_buffer)?; + buffer.extend_from_slice(&tmp_buffer); - // Write the value - write_jsonb_value(value, &mut tmp_buffer)?; + Ok(IsNull::No) } - - write_jsonb_header(buffer, JSONB_OBJECT, tmp_buffer.len())?; - - buffer.extend_from_slice(&tmp_buffer); - - Ok(IsNull::No) } #[cfg(test)] +#[cfg(all(feature = "sqlite", feature = "serde_json"))] mod tests { + use super::jsonb::*; use super::*; use crate::query_dsl::RunQueryDsl; use crate::test_helpers::connection;