diff --git a/Cargo.toml b/Cargo.toml index 8cea716..a6dd6a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ byteorder = "1.5.0" ethnum = "1.5.2" fast-float2 = "0.2.3" itoa = "1.0" +jaq-json = "2.0.0" jiff = "0.2.10" nom = "8.0.0" num-traits = "0.2.19" diff --git a/src/core/databend/builder.rs b/src/core/databend/builder.rs index b7370a8..4e4436e 100644 --- a/src/core/databend/builder.rs +++ b/src/core/databend/builder.rs @@ -13,6 +13,7 @@ // limitations under the License. use core::ops::Range; +use std::collections::btree_map::Entry; use std::collections::BTreeMap; use byteorder::BigEndian; @@ -137,11 +138,13 @@ impl<'a> ObjectBuilder<'a> { } pub(crate) fn push_jsonb_item(&mut self, key: &'a str, val_item: JsonbItem<'a>) -> Result<()> { - if self.entries.contains_key(key) { - return Err(Error::ObjectDuplicateKey); + match self.entries.entry(key) { + Entry::Vacant(entry) => { + entry.insert(val_item); + Ok(()) + } + Entry::Occupied(_) => Err(Error::ObjectDuplicateKey), } - self.entries.insert(key, val_item); - Ok(()) } pub(crate) fn push_raw_jsonb(&mut self, key: &'a str, raw: RawJsonb<'a>) -> Result<()> { @@ -164,17 +167,49 @@ impl<'a> ObjectBuilder<'a> { buf.write_u32::(header)?; let mut jentry_index = reserve_jentries(&mut buf, self.entries.len() * 8); - for (key, _) in self.entries.iter() { - let key_len = key.len(); - buf.extend_from_slice(key.as_bytes()); - let jentry = JEntry::make_string_jentry(key_len); - replace_jentry(&mut buf, jentry, &mut jentry_index) + for key in self.entries.keys() { + append_object_key(&mut buf, &mut jentry_index, key); } for (_, item) in self.entries.into_iter() { append_jsonb_item(&mut buf, &mut jentry_index, item)?; } Ok(OwnedJsonb::new(buf)) } + + pub(crate) fn build_from_entries( + mut entries: Vec<(String, JsonbItem<'a>)>, + ) -> Result { + entries.sort_by(|(left, _), (right, _)| left.cmp(right)); + if entries.windows(2).any(|entry| entry[0].0 == entry[1].0) { + return Err(Error::ObjectDuplicateKey); + } + build_object_vec(entries) + } +} + +fn build_object_vec<'a, K>(entries: Vec<(K, JsonbItem<'a>)>) -> Result +where + K: AsRef, +{ + let mut buf = Vec::new(); + let header = OBJECT_CONTAINER_TAG | entries.len() as u32; + buf.write_u32::(header)?; + + let mut jentry_index = reserve_jentries(&mut buf, entries.len() * 8); + for (key, _) in entries.iter() { + append_object_key(&mut buf, &mut jentry_index, key.as_ref()); + } + for (_, item) in entries.into_iter() { + append_jsonb_item(&mut buf, &mut jentry_index, item)?; + } + Ok(OwnedJsonb::new(buf)) +} + +fn append_object_key(buf: &mut Vec, jentry_index: &mut usize, key: &str) { + let key_len = key.len(); + buf.extend_from_slice(key.as_bytes()); + let jentry = JEntry::make_string_jentry(key_len); + replace_jentry(buf, jentry, jentry_index) } fn append_jsonb_item(buf: &mut Vec, jentry_index: &mut usize, item: JsonbItem) -> Result<()> { diff --git a/src/core/databend/iterator.rs b/src/core/databend/iterator.rs index fae6129..d6d836c 100644 --- a/src/core/databend/iterator.rs +++ b/src/core/databend/iterator.rs @@ -12,11 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::collections::VecDeque; use std::ops::Range; use super::constants::*; -use super::jentry::JEntry; use crate::core::databend::util::jentry_to_jsonb_item; use crate::core::JsonbItem; use crate::error::Result; @@ -34,23 +32,29 @@ impl<'a> ArrayIterator<'a> { pub(crate) fn new(raw_jsonb: RawJsonb<'a>) -> Result> { let (header_type, header_len) = raw_jsonb.read_header(0)?; if header_type == ARRAY_CONTAINER_TAG { - let jentry_offset = 4; - let item_offset = 4 + 4 * header_len; - Ok(Some(Self { - raw_jsonb, - jentry_offset, - item_offset, - length: header_len, - index: 0, - })) + Ok(Some(Self::new_with_len(raw_jsonb, header_len))) } else { Ok(None) } } + pub(crate) fn new_with_len(raw_jsonb: RawJsonb<'a>, length: usize) -> Self { + Self { + raw_jsonb, + jentry_offset: 4, + item_offset: 4 + 4 * length, + length, + index: 0, + } + } + pub(crate) fn len(&self) -> usize { self.length } + + fn remaining(&self) -> usize { + self.length - self.index + } } impl<'a> Iterator for ArrayIterator<'a> { @@ -82,8 +86,15 @@ impl<'a> Iterator for ArrayIterator<'a> { Some(Ok(item)) } + + fn size_hint(&self) -> (usize, Option) { + let remaining = self.remaining(); + (remaining, Some(remaining)) + } } +impl ExactSizeIterator for ArrayIterator<'_> {} + pub(crate) struct ObjectKeyIterator<'a> { raw_jsonb: RawJsonb<'a>, jentry_offset: usize, @@ -113,6 +124,10 @@ impl<'a> ObjectKeyIterator<'a> { pub(crate) fn len(&self) -> usize { self.length } + + fn remaining(&self) -> usize { + self.length - self.index + } } impl<'a> Iterator for ObjectKeyIterator<'a> { @@ -144,8 +159,15 @@ impl<'a> Iterator for ObjectKeyIterator<'a> { Some(Ok(key_item)) } + + fn size_hint(&self) -> (usize, Option) { + let remaining = self.remaining(); + (remaining, Some(remaining)) + } } +impl ExactSizeIterator for ObjectKeyIterator<'_> {} + pub(crate) struct ObjectValueIterator<'a> { raw_jsonb: RawJsonb<'a>, jentry_offset: usize, @@ -182,6 +204,10 @@ impl<'a> ObjectValueIterator<'a> { pub(crate) fn len(&self) -> usize { self.length } + + fn remaining(&self) -> usize { + self.length - self.index + } } impl<'a> Iterator for ObjectValueIterator<'a> { @@ -213,90 +239,228 @@ impl<'a> Iterator for ObjectValueIterator<'a> { Some(Ok(val_item)) } + + fn size_hint(&self) -> (usize, Option) { + let remaining = self.remaining(); + (remaining, Some(remaining)) + } } +impl ExactSizeIterator for ObjectValueIterator<'_> {} + pub(crate) struct ObjectIterator<'a> { raw_jsonb: RawJsonb<'a>, - key_jentries: VecDeque, - jentry_offset: usize, + key_jentry_offset: usize, + val_jentry_offset: usize, key_offset: usize, val_offset: usize, length: usize, + index: usize, } impl<'a> ObjectIterator<'a> { pub(crate) fn new(raw_jsonb: RawJsonb<'a>) -> Result> { let (header_type, header_len) = raw_jsonb.read_header(0)?; if header_type == OBJECT_CONTAINER_TAG { - let mut jentry_offset = 4; - let mut key_jentries = VecDeque::with_capacity(header_len); - for _ in 0..header_len { - let key_jentry = raw_jsonb.read_jentry(jentry_offset)?; - jentry_offset += 4; - key_jentries.push_back(key_jentry); - } - let key_length: usize = key_jentries.iter().map(|j| j.length as usize).sum(); - let key_offset = 4 + 8 * header_len; - let val_offset = key_offset + key_length; - - Ok(Some(Self { - raw_jsonb, - key_jentries, - jentry_offset, - key_offset, - val_offset, - length: header_len, - })) + Ok(Some(Self::new_with_len(raw_jsonb, header_len)?)) } else { Ok(None) } } + pub(crate) fn new_with_len(raw_jsonb: RawJsonb<'a>, length: usize) -> Result { + let mut key_jentry_offset = 4; + let mut key_length = 0; + for _ in 0..length { + let key_jentry = raw_jsonb.read_jentry(key_jentry_offset)?; + key_jentry_offset += 4; + key_length += key_jentry.length as usize; + } + + let key_offset = 4 + 8 * length; + Ok(Self { + raw_jsonb, + key_jentry_offset: 4, + val_jentry_offset: 4 + 4 * length, + key_offset, + val_offset: key_offset + key_length, + length, + index: 0, + }) + } + pub(crate) fn len(&self) -> usize { self.length } + + fn remaining(&self) -> usize { + self.length - self.index + } } impl<'a> Iterator for ObjectIterator<'a> { type Item = Result<(&'a str, JsonbItem<'a>)>; fn next(&mut self) -> Option { - match self.key_jentries.pop_front() { - Some(key_jentry) => { - let val_jentry = match self.raw_jsonb.read_jentry(self.jentry_offset) { - Ok(jentry) => jentry, - Err(err) => return Some(Err(err)), - }; - let key_length = key_jentry.length as usize; - let val_length = val_jentry.length as usize; - - let key_range = Range { - start: self.key_offset, - end: self.key_offset + key_length, - }; - let key_data = match self.raw_jsonb.slice(key_range) { - Ok(data) => data, - Err(err) => return Some(Err(err)), - }; - let key = unsafe { std::str::from_utf8_unchecked(key_data) }; - - let val_range = Range { - start: self.val_offset, - end: self.val_offset + val_length, - }; - let val_data = match self.raw_jsonb.slice(val_range) { - Ok(data) => data, - Err(err) => return Some(Err(err)), - }; - let val_item = jentry_to_jsonb_item(val_jentry, val_data); - - self.jentry_offset += 4; - self.key_offset += key_length; - self.val_offset += val_length; - - Some(Ok((key, val_item))) - } - None => None, + if self.index >= self.length { + return None; } + + let key_jentry = match self.raw_jsonb.read_jentry(self.key_jentry_offset) { + Ok(jentry) => jentry, + Err(err) => return Some(Err(err)), + }; + let val_jentry = match self.raw_jsonb.read_jentry(self.val_jentry_offset) { + Ok(jentry) => jentry, + Err(err) => return Some(Err(err)), + }; + let key_length = key_jentry.length as usize; + let val_length = val_jentry.length as usize; + + let key_range = Range { + start: self.key_offset, + end: self.key_offset + key_length, + }; + let key_data = match self.raw_jsonb.slice(key_range) { + Ok(data) => data, + Err(err) => return Some(Err(err)), + }; + let key = unsafe { std::str::from_utf8_unchecked(key_data) }; + + let val_range = Range { + start: self.val_offset, + end: self.val_offset + val_length, + }; + let val_data = match self.raw_jsonb.slice(val_range) { + Ok(data) => data, + Err(err) => return Some(Err(err)), + }; + let val_item = jentry_to_jsonb_item(val_jentry, val_data); + + self.index += 1; + self.key_jentry_offset += 4; + self.val_jentry_offset += 4; + self.key_offset += key_length; + self.val_offset += val_length; + + Some(Ok((key, val_item))) + } + + fn size_hint(&self) -> (usize, Option) { + let remaining = self.remaining(); + (remaining, Some(remaining)) + } +} + +impl ExactSizeIterator for ObjectIterator<'_> {} + +#[cfg(test)] +mod tests { + use super::*; + use crate::OwnedJsonb; + + fn owned(json: &str) -> OwnedJsonb { + json.parse().unwrap() + } + + fn number_string(item: JsonbItem<'_>) -> String { + match item { + JsonbItem::Number(value) => value.as_number().unwrap().to_string(), + _ => panic!("expected number item"), + } + } + + fn string_value(item: JsonbItem<'_>) -> String { + item.as_str().unwrap().into_owned() + } + + #[test] + fn array_iterator_reports_exact_remaining_items() { + let jsonb = owned(r#"[1,"two",true,null]"#); + let mut iter = ArrayIterator::new(jsonb.as_raw()).unwrap().unwrap(); + + assert_eq!(iter.len(), 4); + assert_eq!(iter.size_hint(), (4, Some(4))); + assert_eq!(number_string(iter.next().unwrap().unwrap()), "1"); + assert_eq!(iter.size_hint(), (3, Some(3))); + assert_eq!(string_value(iter.next().unwrap().unwrap()), "two"); + assert_eq!(iter.size_hint(), (2, Some(2))); + assert_eq!(iter.next().unwrap().unwrap(), JsonbItem::Boolean(true)); + assert_eq!(iter.size_hint(), (1, Some(1))); + assert_eq!(iter.next().unwrap().unwrap(), JsonbItem::Null); + assert_eq!(iter.size_hint(), (0, Some(0))); + assert!(iter.next().is_none()); + } + + #[test] + fn empty_array_iterator_reports_zero_remaining_items() { + let jsonb = owned("[]"); + let mut iter = ArrayIterator::new(jsonb.as_raw()).unwrap().unwrap(); + + assert_eq!(iter.len(), 0); + assert_eq!(iter.size_hint(), (0, Some(0))); + assert!(iter.next().is_none()); + } + + #[test] + fn object_iterator_preserves_key_value_pairs_and_size_hint() { + let jsonb = owned(r#"{"first":1,"second":"two","third":true}"#); + let mut iter = ObjectIterator::new(jsonb.as_raw()).unwrap().unwrap(); + + assert_eq!(iter.len(), 3); + assert_eq!(iter.size_hint(), (3, Some(3))); + + let (key, value) = iter.next().unwrap().unwrap(); + assert_eq!(key, "first"); + assert_eq!(number_string(value), "1"); + assert_eq!(iter.size_hint(), (2, Some(2))); + + let (key, value) = iter.next().unwrap().unwrap(); + assert_eq!(key, "second"); + assert_eq!(string_value(value), "two"); + assert_eq!(iter.size_hint(), (1, Some(1))); + + let (key, value) = iter.next().unwrap().unwrap(); + assert_eq!(key, "third"); + assert_eq!(value, JsonbItem::Boolean(true)); + assert_eq!(iter.size_hint(), (0, Some(0))); + assert!(iter.next().is_none()); + } + + #[test] + fn empty_object_iterator_reports_zero_remaining_items() { + let jsonb = owned("{}"); + let mut iter = ObjectIterator::new(jsonb.as_raw()).unwrap().unwrap(); + + assert_eq!(iter.len(), 0); + assert_eq!(iter.size_hint(), (0, Some(0))); + assert!(iter.next().is_none()); + } + + #[test] + fn object_key_and_value_iterators_report_exact_remaining_items() { + let jsonb = owned(r#"{"first":1,"second":"two","third":true}"#); + let mut keys = ObjectKeyIterator::new(jsonb.as_raw()).unwrap().unwrap(); + let mut values = ObjectValueIterator::new(jsonb.as_raw()).unwrap().unwrap(); + + assert_eq!(keys.len(), 3); + assert_eq!(keys.size_hint(), (3, Some(3))); + assert_eq!(string_value(keys.next().unwrap().unwrap()), "first"); + assert_eq!(keys.size_hint(), (2, Some(2))); + assert_eq!(string_value(keys.next().unwrap().unwrap()), "second"); + assert_eq!(keys.size_hint(), (1, Some(1))); + assert_eq!(string_value(keys.next().unwrap().unwrap()), "third"); + assert_eq!(keys.size_hint(), (0, Some(0))); + assert!(keys.next().is_none()); + + assert_eq!(values.len(), 3); + assert_eq!(values.size_hint(), (3, Some(3))); + assert_eq!(number_string(values.next().unwrap().unwrap()), "1"); + assert_eq!(values.size_hint(), (2, Some(2))); + assert_eq!(string_value(values.next().unwrap().unwrap()), "two"); + assert_eq!(values.size_hint(), (1, Some(1))); + assert_eq!(values.next().unwrap().unwrap(), JsonbItem::Boolean(true)); + assert_eq!(values.size_hint(), (0, Some(0))); + assert!(values.next().is_none()); } } diff --git a/src/core/databend/ser.rs b/src/core/databend/ser.rs index 3321043..9fb31a4 100644 --- a/src/core/databend/ser.rs +++ b/src/core/databend/ser.rs @@ -513,7 +513,7 @@ impl ser::SerializeMap for ObjectSerializer<'_> { )); } let mut builder = ObjectBuilder::new(); - for (key_str, value) in self.keys.iter().zip(self.values.into_iter()) { + for (key_str, value) in self.keys.iter().zip(self.values) { builder.push_owned_jsonb(key_str, value)?; } let object_jsonb = builder.build()?; diff --git a/src/jaq.rs b/src/jaq.rs new file mode 100644 index 0000000..059dd29 --- /dev/null +++ b/src/jaq.rs @@ -0,0 +1,253 @@ +// Copyright 2023 Datafuse Labs. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::borrow::Cow; +use std::str; + +use jaq_json::Map; +use jaq_json::Num; +use jaq_json::Val; +use num_traits::ToPrimitive; + +use crate::constants::DECIMAL128_MAX; +use crate::constants::DECIMAL128_MIN; +use crate::constants::MAX_DECIMAL256_PRECISION; +use crate::core::ArrayBuilder; +use crate::core::ArrayIterator; +use crate::core::ExtensionItem; +use crate::core::JsonbItem; +use crate::core::JsonbItemType; +use crate::core::NumberItem; +use crate::core::ObjectBuilder; +use crate::core::ObjectIterator; +use crate::error::Result; +use crate::Decimal128; +use crate::Decimal256; +use crate::Error; +use crate::ExtensionValue; +use crate::Number; +use crate::OwnedJsonb; +use crate::RawJsonb; +use ethnum::i256; + +/// Convert raw JSONB directly into jaq-json's native value type. +impl<'a> TryFrom> for Val { + type Error = Error; + + fn try_from(raw: RawJsonb<'a>) -> Result { + let item = JsonbItem::from_raw_jsonb(raw)?; + jsonb_item_to_jaq_val(item) + } +} + +fn jsonb_item_to_jaq_val(item: JsonbItem<'_>) -> Result { + match item { + JsonbItem::Null => Ok(Val::Null), + JsonbItem::Boolean(value) => Ok(Val::Bool(value)), + JsonbItem::Number(value) => value.as_number().map(number_to_jaq_num).map(Val::Num), + JsonbItem::String(value) => Ok(Val::utf8_str(value.as_bytes().to_vec())), + JsonbItem::Extension(value) => match value.as_extension_value()? { + ExtensionValue::Binary(value) => Ok(Val::byte_str(value.to_vec())), + value => Ok(Val::utf8_str(value.to_string().into_bytes())), + }, + JsonbItem::Raw(raw) => raw_container_to_jaq_val(raw), + JsonbItem::Owned(owned) => raw_container_to_jaq_val(owned.as_raw()), + } +} + +fn raw_container_to_jaq_val(raw: RawJsonb<'_>) -> Result { + match raw.jsonb_item_type()? { + JsonbItemType::Array(len) => { + let iter = ArrayIterator::new_with_len(raw, len); + let values = iter + .map(|item| item.and_then(jsonb_item_to_jaq_val)) + .collect::>>()?; + Ok(Val::Arr(jaq_json::Rc::new(values))) + } + JsonbItemType::Object(len) => { + let iter = ObjectIterator::new_with_len(raw, len)?; + let mut values = Map::with_capacity_and_hasher(iter.len(), Default::default()); + for item in iter { + let (key, value) = item?; + values.insert( + Val::utf8_str(key.as_bytes().to_vec()), + jsonb_item_to_jaq_val(value)?, + ); + } + Ok(Val::obj(values)) + } + _ => jsonb_item_to_jaq_val(JsonbItem::from_raw_jsonb(raw)?), + } +} + +fn number_to_jaq_num(number: Number) -> Num { + match number { + Number::Int64(value) => Num::from_integral(value), + Number::UInt64(value) => Num::from_integral(value), + Number::Float64(value) => Num::Float(value), + Number::Decimal64(_) | Number::Decimal128(_) | Number::Decimal256(_) => { + Num::Dec(jaq_json::Rc::new(number.to_string())) + } + } +} + +/// Convert a jaq-json value back into JSONB. +impl TryFrom<&Val> for OwnedJsonb { + type Error = Error; + + fn try_from(value: &Val) -> Result { + jaq_val_to_owned_jsonb(value) + } +} + +fn jaq_val_to_owned_jsonb(value: &Val) -> Result { + match value { + Val::Arr(values) => { + let mut builder = ArrayBuilder::with_capacity(values.len()); + for value in values.iter() { + builder.push_jsonb_item(jaq_val_to_jsonb_item(value)?); + } + builder.build() + } + Val::Obj(values) => { + let mut entries = Vec::with_capacity(values.len()); + for (key, value) in values.iter() { + let key = jaq_val_to_object_key(key)?; + entries.push((key.into_owned(), jaq_val_to_jsonb_item(value)?)); + } + ObjectBuilder::build_from_entries(entries) + } + value => OwnedJsonb::from_item(jaq_val_to_jsonb_item(value)?), + } +} + +fn jaq_val_to_jsonb_item(value: &Val) -> Result> { + match value { + Val::Null => Ok(JsonbItem::Null), + Val::Bool(value) => Ok(JsonbItem::Boolean(*value)), + Val::Num(value) => Ok(JsonbItem::Number(NumberItem::Number(jaq_num_to_number( + value, + )?))), + Val::TStr(value) => Ok(JsonbItem::String(bytes_to_string(value.as_ref())?)), + Val::BStr(value) => Ok(JsonbItem::Extension(ExtensionItem::Extension( + ExtensionValue::Binary(value.as_ref()), + ))), + Val::Arr(_) | Val::Obj(_) => Ok(JsonbItem::Owned(jaq_val_to_owned_jsonb(value)?)), + } +} + +fn jaq_val_to_object_key(value: &Val) -> Result> { + match value { + Val::TStr(value) | Val::BStr(value) => bytes_to_string(value.as_ref()), + _ => Err(Error::InvalidObject), + } +} + +fn bytes_to_string(value: &[u8]) -> Result> { + str::from_utf8(value) + .map(Cow::Borrowed) + .map_err(|_| Error::InvalidObject) +} + +fn jaq_num_to_number(value: &Num) -> Result { + match value { + Num::Int(value) => Ok(Number::Int64(*value as i64)), + Num::BigInt(value) => { + if let Some(value) = value.to_i64() { + return Ok(Number::Int64(value)); + } + if let Some(value) = value.to_u64() { + return Ok(Number::UInt64(value)); + } + if let Some(value) = value.to_i128() { + if (DECIMAL128_MIN..=DECIMAL128_MAX).contains(&value) { + return Ok(Number::Decimal128(Decimal128 { scale: 0, value })); + } + } + let value_string = value.to_string(); + let digits = value_string.trim_start_matches('-').len(); + if digits <= MAX_DECIMAL256_PRECISION { + let value = value_string + .parse::() + .map_err(|_| Error::InvalidJsonbNumber)?; + return Ok(Number::Decimal256(Decimal256 { scale: 0, value })); + } + Err(Error::InvalidJsonbNumber) + } + Num::Float(value) => Ok(Number::Float64(*value)), + Num::Dec(value) => crate::parse_owned_jsonb(value.as_bytes()) + .and_then(|value| value.as_raw().as_number()?.ok_or(Error::InvalidJsonbNumber)), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn big_int(value: &str) -> Num { + Num::from_str_radix(value, 10).unwrap() + } + + #[test] + fn try_from_converts_between_raw_jsonb_and_jaq_val() { + let jsonb = + r#"{"name":"alice","scores":[1,2,3],"valid":true}"#.parse::().unwrap(); + + let value = Val::try_from(jsonb.as_raw()).unwrap(); + let converted = OwnedJsonb::try_from(&value).unwrap(); + + assert_eq!( + converted.to_string(), + r#"{"name":"alice","scores":[1,2,3],"valid":true}"# + ); + } + + #[test] + fn jaq_bigint_to_number_preserves_decimal128_range() { + let value = jaq_num_to_number(&big_int("18446744073709551616")).unwrap(); + + match value { + Number::Decimal128(value) => { + assert_eq!(value.scale, 0); + assert_eq!(value.value, 18_446_744_073_709_551_616_i128); + } + other => panic!("expected Decimal128, got {other:?}"), + } + } + + #[test] + fn jaq_bigint_to_number_preserves_decimal256_range() { + let value = "100000000000000000000000000000000000000"; + let number = jaq_num_to_number(&big_int(value)).unwrap(); + + match number { + Number::Decimal256(value) => { + assert_eq!(value.scale, 0); + assert_eq!( + value.value.to_string(), + "100000000000000000000000000000000000000" + ); + } + other => panic!("expected Decimal256, got {other:?}"), + } + } + + #[test] + fn jaq_bigint_to_number_rejects_values_outside_decimal256_range() { + let value = "1".repeat(MAX_DECIMAL256_PRECISION + 1); + let err = jaq_num_to_number(&big_int(&value)).unwrap_err(); + + assert_eq!(err, Error::InvalidJsonbNumber); + } +} diff --git a/src/lib.rs b/src/lib.rs index 021a236..e5224c1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,6 +69,7 @@ mod error; mod extension; mod from; mod functions; +pub mod jaq; pub mod jsonpath; pub mod keypath; mod number; diff --git a/src/raw.rs b/src/raw.rs index 06df2cb..583edbd 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -63,7 +63,7 @@ impl<'a> RawJsonb<'a> { /// /// The length of the data in bytes. pub fn len(&self) -> usize { - self.data.as_ref().len() + self.data.len() } /// Creates an `OwnedJsonb` from the `RawJsonb` by copying the underlying data. diff --git a/tests/it/functions.rs b/tests/it/functions.rs index 49cd937..a2bdc32 100644 --- a/tests/it/functions.rs +++ b/tests/it/functions.rs @@ -2074,7 +2074,7 @@ fn test_extract_scalar_key_values() { ), ]; for ((key_paths, value), (expected_key_paths, expected_value)) in - result.into_iter().zip(expected.into_iter()) + result.into_iter().zip(expected) { assert_eq!(key_paths, expected_key_paths); assert_eq!(value, expected_value); @@ -2130,7 +2130,7 @@ fn test_extract_scalar_key_values() { ), ]; for ((key_paths, value), (expected_key_paths, expected_value)) in - result.into_iter().zip(expected.into_iter()) + result.into_iter().zip(expected) { assert_eq!(key_paths, expected_key_paths); assert_eq!(value, expected_value); @@ -2167,7 +2167,7 @@ fn test_extract_scalar_key_values() { ), ]; for ((key_paths, value), (expected_key_paths, expected_value)) in - result.into_iter().zip(expected.into_iter()) + result.into_iter().zip(expected) { assert_eq!(key_paths, expected_key_paths); assert_eq!(value, expected_value); @@ -2206,7 +2206,7 @@ fn test_extract_scalar_key_values() { ), ]; for ((key_paths, value), (expected_key_paths, expected_value)) in - result.into_iter().zip(expected.into_iter()) + result.into_iter().zip(expected) { assert_eq!(key_paths, expected_key_paths); assert_eq!(value, expected_value); @@ -2244,7 +2244,7 @@ fn test_extract_scalar_key_values() { ), ]; for ((key_paths, value), (expected_key_paths, expected_value)) in - result.into_iter().zip(expected.into_iter()) + result.into_iter().zip(expected) { assert_eq!(key_paths, expected_key_paths); assert_eq!(value, expected_value);