feat: add Value::Json value type (#7083)

* feat: struct value

Signed-off-by: Ning Sun <sunning@greptime.com>

* feat: update for proto module

* feat: wip struct type

* feat: implement more vector operations

* feat: make datatype and api

* feat: reoslve some compilation issues

* feat: resolve all compilation issues

* chore: format update

* test: resolve tests

* test: test and refactor value-to-pb

* feat: add more tests and fix for value types

* chore: remove dbg

* feat: test and fix iterator

* fix: resolve struct_type issue

* feat: pgwire 0.33 update

* refactor: use vec for struct items

* feat: conversion from json to value

* feat: add decode function

* fix: lint issue

* feat: update how we encode raw data

* feat: add convertion to fully strcutured StructValue

* refactor: take owned value in all encode/decode functions

* feat: add pg serialization of structvalue

* chore: toml format

* refactor: adopt new and try_new from struct value

* chore: cleanup residual issues

* docs: docs up

* fix lint issue

* Apply suggestion from @MichaelScofield

Co-authored-by: LFC <990479+MichaelScofield@users.noreply.github.com>

* Apply suggestion from @MichaelScofield

Co-authored-by: LFC <990479+MichaelScofield@users.noreply.github.com>

* Apply suggestion from @MichaelScofield

Co-authored-by: LFC <990479+MichaelScofield@users.noreply.github.com>

* Apply suggestion from @MichaelScofield

Co-authored-by: LFC <990479+MichaelScofield@users.noreply.github.com>

* chore: address review comment especially collection capacity

* refactor: remove unneeded processed keys collection

* feat: Value::Json type

* chore: add some work in progress changes

* feat: adopt new json type

* refactor: limit scope json conversion functions

* fix: self review update

* test: provide tests for value::json

* test: add tests for api/helper

* switch proto to main branch

* fix: implement is_null for ValueRef::Json

---------

Signed-off-by: Ning Sun <sunning@greptime.com>
Co-authored-by: LFC <990479+MichaelScofield@users.noreply.github.com>
This commit is contained in:
Ning Sun
2025-10-16 04:13:12 +08:00
committed by GitHub
parent 8073e552df
commit 145c1024d1
16 changed files with 590 additions and 150 deletions

View File

@@ -33,8 +33,8 @@ use crate::types::{
BinaryType, BooleanType, DateType, Decimal128Type, DictionaryType, DurationMicrosecondType,
DurationMillisecondType, DurationNanosecondType, DurationSecondType, DurationType, Float32Type,
Float64Type, Int8Type, Int16Type, Int32Type, Int64Type, IntervalDayTimeType,
IntervalMonthDayNanoType, IntervalType, IntervalYearMonthType, JsonType, ListType, NullType,
StringType, StructType, TimeMillisecondType, TimeType, TimestampMicrosecondType,
IntervalMonthDayNanoType, IntervalType, IntervalYearMonthType, JsonFormat, JsonType, ListType,
NullType, StringType, StructType, TimeMillisecondType, TimeType, TimestampMicrosecondType,
TimestampMillisecondType, TimestampNanosecondType, TimestampSecondType, TimestampType,
UInt8Type, UInt16Type, UInt32Type, UInt64Type, VectorType,
};
@@ -350,7 +350,7 @@ impl ConcreteDataType {
pub fn as_json(&self) -> Option<JsonType> {
match self {
ConcreteDataType::Json(j) => Some(*j),
ConcreteDataType::Json(j) => Some(j.clone()),
_ => None,
}
}
@@ -668,6 +668,10 @@ impl ConcreteDataType {
pub fn vector_default_datatype() -> ConcreteDataType {
Self::vector_datatype(0)
}
pub fn json_native_datatype(inner_type: ConcreteDataType) -> ConcreteDataType {
ConcreteDataType::Json(JsonType::new(JsonFormat::Native(Box::new(inner_type))))
}
}
/// Data type abstraction.

View File

@@ -87,15 +87,16 @@ impl JsonStructureSettings {
decode_struct_with_settings(struct_value, &context)
}
/// Encode a serde_json::Value into a StructValue using current settings.
/// Encode a serde_json::Value into a Value::Json using current settings.
pub fn encode(&self, json: Json) -> Result<Value, Error> {
let context = JsonContext {
key_path: String::new(),
settings: self,
};
encode_json_with_context(json, None, &context)
encode_json_with_context(json, None, &context).map(|v| Value::Json(Box::new(v)))
}
/// Encode a serde_json::Value into a Value::Json with given data type.
pub fn encode_with_type(
&self,
json: Json,
@@ -105,7 +106,7 @@ impl JsonStructureSettings {
key_path: String::new(),
settings: self,
};
encode_json_with_context(json, data_type, &context)
encode_json_with_context(json, data_type, &context).map(|v| Value::Json(Box::new(v)))
}
}
@@ -394,6 +395,7 @@ pub fn decode_value_with_context<'a>(
}
match value {
Value::Json(inner) => decode_value_with_context(*inner, context),
Value::Struct(struct_value) => decode_struct_with_context(struct_value, context),
Value::List(list_value) => decode_list_with_context(list_value, context),
_ => decode_primitive_value(value),
@@ -507,7 +509,7 @@ fn decode_primitive_value(value: Value) -> Result<Json, Error> {
}
Value::Duration(v) => serde_json::to_value(v.value()).context(error::SerializeSnafu),
Value::Decimal128(v) => serde_json::to_value(v.to_string()).context(error::SerializeSnafu),
Value::Struct(_) | Value::List(_) => {
Value::Struct(_) | Value::List(_) | Value::Json(_) => {
// These should be handled by the context-aware functions
Err(error::InvalidJsonSnafu {
value: "Structured values should be handled by context-aware decoding".to_string(),
@@ -694,7 +696,11 @@ mod tests {
fn test_encode_json_null() {
let json = Json::Null;
let settings = JsonStructureSettings::Structured(None);
let result = settings.encode_with_type(json, None).unwrap();
let result = settings
.encode_with_type(json, None)
.unwrap()
.into_json_inner()
.unwrap();
assert_eq!(result, Value::Null);
}
@@ -702,7 +708,11 @@ mod tests {
fn test_encode_json_boolean() {
let json = Json::Bool(true);
let settings = JsonStructureSettings::Structured(None);
let result = settings.encode_with_type(json, None).unwrap();
let result = settings
.encode_with_type(json, None)
.unwrap()
.into_json_inner()
.unwrap();
assert_eq!(result, Value::Boolean(true));
}
@@ -710,7 +720,11 @@ mod tests {
fn test_encode_json_number_integer() {
let json = Json::from(42);
let settings = JsonStructureSettings::Structured(None);
let result = settings.encode_with_type(json, None).unwrap();
let result = settings
.encode_with_type(json, None)
.unwrap()
.into_json_inner()
.unwrap();
assert_eq!(result, Value::Int64(42));
}
@@ -718,7 +732,11 @@ mod tests {
fn test_encode_json_number_float() {
let json = Json::from(3.15);
let settings = JsonStructureSettings::Structured(None);
let result = settings.encode_with_type(json, None).unwrap();
let result = settings
.encode_with_type(json, None)
.unwrap()
.into_json_inner()
.unwrap();
match result {
Value::Float64(f) => assert_eq!(f.0, 3.15),
_ => panic!("Expected Float64"),
@@ -729,7 +747,11 @@ mod tests {
fn test_encode_json_string() {
let json = Json::String("hello".to_string());
let settings = JsonStructureSettings::Structured(None);
let result = settings.encode_with_type(json, None).unwrap();
let result = settings
.encode_with_type(json, None)
.unwrap()
.into_json_inner()
.unwrap();
assert_eq!(result, Value::String("hello".into()));
}
@@ -737,7 +759,11 @@ mod tests {
fn test_encode_json_array() {
let json = json!([1, 2, 3]);
let settings = JsonStructureSettings::Structured(None);
let result = settings.encode_with_type(json, None).unwrap();
let result = settings
.encode_with_type(json, None)
.unwrap()
.into_json_inner()
.unwrap();
if let Value::List(list_value) = result {
assert_eq!(list_value.items().len(), 3);
@@ -758,7 +784,11 @@ mod tests {
});
let settings = JsonStructureSettings::Structured(None);
let result = settings.encode_with_type(json, None).unwrap();
let result = settings
.encode_with_type(json, None)
.unwrap()
.into_json_inner()
.unwrap();
let Value::Struct(result) = result else {
panic!("Expected Struct value");
};
@@ -804,7 +834,11 @@ mod tests {
});
let settings = JsonStructureSettings::Structured(None);
let result = settings.encode_with_type(json, None).unwrap();
let result = settings
.encode_with_type(json, None)
.unwrap()
.into_json_inner()
.unwrap();
let Value::Struct(result) = result else {
panic!("Expected Struct value");
};
@@ -856,12 +890,16 @@ mod tests {
let settings = JsonStructureSettings::Structured(None);
let result = settings
.encode_with_type(json.clone(), Some(&ConcreteDataType::int8_datatype()))
.unwrap()
.into_json_inner()
.unwrap();
assert_eq!(result, Value::Int8(42));
// Test with expected string type
let result = settings
.encode_with_type(json, Some(&ConcreteDataType::string_datatype()))
.unwrap()
.into_json_inner()
.unwrap();
assert_eq!(result, Value::String("42".into()));
}
@@ -870,7 +908,11 @@ mod tests {
fn test_encode_json_array_mixed_types() {
let json = json!([1, "hello", true, 3.15]);
let settings = JsonStructureSettings::Structured(None);
let result = settings.encode_with_type(json, None).unwrap();
let result = settings
.encode_with_type(json, None)
.unwrap()
.into_json_inner()
.unwrap();
if let Value::List(list_value) = result {
assert_eq!(list_value.items().len(), 4);
@@ -889,7 +931,11 @@ mod tests {
fn test_encode_json_empty_array() {
let json = json!([]);
let settings = JsonStructureSettings::Structured(None);
let result = settings.encode_with_type(json, None).unwrap();
let result = settings
.encode_with_type(json, None)
.unwrap()
.into_json_inner()
.unwrap();
if let Value::List(list_value) = result {
assert_eq!(list_value.items().len(), 0);
@@ -911,7 +957,7 @@ mod tests {
});
let settings = JsonStructureSettings::Structured(None);
let result = settings.encode(json).unwrap();
let result = settings.encode(json).unwrap().into_json_inner().unwrap();
if let Value::Struct(struct_value) = result {
assert_eq!(struct_value.items().len(), 2);
@@ -950,6 +996,8 @@ mod tests {
let settings = JsonStructureSettings::Structured(None);
let result = settings
.encode_with_type(json, Some(&concrete_type))
.unwrap()
.into_json_inner()
.unwrap();
if let Value::Struct(struct_value) = result {
@@ -1110,7 +1158,7 @@ mod tests {
});
let settings = JsonStructureSettings::Structured(None);
let result = settings.encode(json).unwrap();
let result = settings.encode(json).unwrap().into_json_inner().unwrap();
if let Value::Struct(struct_value) = result {
assert_eq!(struct_value.items().len(), 2);
@@ -1127,6 +1175,8 @@ mod tests {
let settings = JsonStructureSettings::Structured(None);
let result = settings
.encode_with_type(json, Some(&concrete_type))
.unwrap()
.into_json_inner()
.unwrap();
if let Value::List(list_value) = result {
@@ -1151,6 +1201,8 @@ mod tests {
let settings = JsonStructureSettings::Structured(None);
let result = settings
.encode_with_type(json, Some(&concrete_type))
.unwrap()
.into_json_inner()
.unwrap();
if let Value::List(list_value) = result {
@@ -1420,6 +1472,8 @@ mod tests {
let json = Json::from(42);
let result = settings
.encode_with_type(json, Some(&ConcreteDataType::int64_datatype()))
.unwrap()
.into_json_inner()
.unwrap();
assert_eq!(result, Value::Int64(42));
@@ -1427,6 +1481,8 @@ mod tests {
let json = Json::String("hello".to_string());
let result = settings
.encode_with_type(json, Some(&ConcreteDataType::string_datatype()))
.unwrap()
.into_json_inner()
.unwrap();
assert_eq!(result, Value::String("hello".into()));
@@ -1434,6 +1490,8 @@ mod tests {
let json = Json::Bool(true);
let result = settings
.encode_with_type(json, Some(&ConcreteDataType::boolean_datatype()))
.unwrap()
.into_json_inner()
.unwrap();
assert_eq!(result, Value::Boolean(true));
}
@@ -1461,6 +1519,8 @@ mod tests {
let settings = JsonStructureSettings::Structured(None);
let result = settings
.encode_with_type(json, Some(&concrete_type))
.unwrap()
.into_json_inner()
.unwrap();
if let Value::List(list_value) = result {
@@ -1484,6 +1544,8 @@ mod tests {
let settings = JsonStructureSettings::Structured(None);
let result = settings
.encode_with_type(json.clone(), Some(&ConcreteDataType::null_datatype()))
.unwrap()
.into_json_inner()
.unwrap();
assert_eq!(result, Value::Null);
@@ -1491,6 +1553,8 @@ mod tests {
let json = Json::from(3.15);
let result = settings
.encode_with_type(json, Some(&ConcreteDataType::float64_datatype()))
.unwrap()
.into_json_inner()
.unwrap();
match result {
Value::Float64(f) => assert_eq!(f.0, 3.15),
@@ -1503,12 +1567,20 @@ mod tests {
// Test unsigned integer that fits in i64
let json = Json::from(u64::MAX / 2);
let settings = JsonStructureSettings::Structured(None);
let result = settings.encode_with_type(json, None).unwrap();
let result = settings
.encode_with_type(json, None)
.unwrap()
.into_json_inner()
.unwrap();
assert_eq!(result, Value::Int64((u64::MAX / 2) as i64));
// Test unsigned integer that exceeds i64 range
let json = Json::from(u64::MAX);
let result = settings.encode_with_type(json, None).unwrap();
let result = settings
.encode_with_type(json, None)
.unwrap()
.into_json_inner()
.unwrap();
assert_eq!(result, Value::UInt64(u64::MAX));
}
@@ -1520,7 +1592,7 @@ mod tests {
});
let settings = JsonStructureSettings::UnstructuredRaw;
let result = settings.encode(json).unwrap();
let result = settings.encode(json).unwrap().into_json_inner().unwrap();
if let Value::Struct(struct_value) = result {
assert_eq!(struct_value.struct_type().fields().len(), 1);
@@ -1553,7 +1625,11 @@ mod tests {
let settings = JsonStructureSettings::UnstructuredRaw;
// Test with encode (no type)
let result = settings.encode(json.clone()).unwrap();
let result = settings
.encode(json.clone())
.unwrap()
.into_json_inner()
.unwrap();
if let Value::Struct(s) = result {
if let Value::String(json_str) = &s.items()[0] {
let json_str = json_str.as_utf8();
@@ -1585,6 +1661,8 @@ mod tests {
let result2 = settings
.encode_with_type(json, Some(&concrete_type))
.unwrap()
.into_json_inner()
.unwrap();
if let Value::Struct(s) = result2 {
if let Value::String(json_str) = &s.items()[0] {
@@ -1609,7 +1687,11 @@ mod tests {
}
});
let result3 = settings.encode(nested_json).unwrap();
let result3 = settings
.encode(nested_json)
.unwrap()
.into_json_inner()
.unwrap();
if let Value::Struct(s) = result3 {
if let Value::String(json_str) = &s.items()[0] {
let json_str = json_str.as_utf8();
@@ -1627,7 +1709,11 @@ mod tests {
// Test with arrays
let array_json = json!([1, "hello", true, 3.15]);
let result4 = settings.encode(array_json).unwrap();
let result4 = settings
.encode(array_json)
.unwrap()
.into_json_inner()
.unwrap();
if let Value::Struct(s) = result4 {
if let Value::String(json_str) = &s.items()[0] {
let json_str = json_str.as_utf8();
@@ -1659,7 +1745,7 @@ mod tests {
fields: None,
unstructured_keys,
};
let result = settings.encode(json).unwrap();
let result = settings.encode(json).unwrap().into_json_inner().unwrap();
if let Value::Struct(struct_value) = result {
let items = struct_value.items();
@@ -2160,7 +2246,11 @@ mod tests {
});
// Encode the JSON with partial unstructured settings
let encoded_value = settings.encode(original_json).unwrap();
let encoded_value = settings
.encode(original_json)
.unwrap()
.into_json_inner()
.unwrap();
// Verify encoding worked - metadata and user.profile.settings should be unstructured
if let Value::Struct(encoded_struct) = encoded_value {

View File

@@ -44,8 +44,8 @@ pub use interval_type::{
IntervalDayTimeType, IntervalMonthDayNanoType, IntervalType, IntervalYearMonthType,
};
pub use json_type::{
JSON_TYPE_NAME, JsonType, json_type_value_to_serde_json, json_type_value_to_string,
parse_string_to_json_type_value,
JSON_TYPE_NAME, JsonFormat, JsonType, jsonb_to_serde_json, jsonb_to_string,
parse_string_to_jsonb,
};
pub use list_type::ListType;
pub use null_type::NullType;

View File

@@ -21,6 +21,7 @@ use snafu::ResultExt;
use crate::data_type::DataType;
use crate::error::{DeserializeSnafu, InvalidJsonSnafu, InvalidJsonbSnafu, Result};
use crate::prelude::ConcreteDataType;
use crate::scalars::ScalarVectorBuilder;
use crate::type_id::LogicalTypeId;
use crate::value::Value;
@@ -28,19 +29,16 @@ use crate::vectors::{BinaryVectorBuilder, MutableVector};
pub const JSON_TYPE_NAME: &str = "Json";
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, Default,
)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, Default)]
pub enum JsonFormat {
#[default]
Jsonb,
Native(Box<ConcreteDataType>),
}
/// JsonType is a data type for JSON data. It is stored as binary data of jsonb format.
/// It utilizes current binary value and vector implementation.
#[derive(
Debug, Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize,
)]
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub struct JsonType {
pub format: JsonFormat,
}
@@ -81,34 +79,26 @@ impl DataType for JsonType {
}
/// Converts a json type value to string
pub fn json_type_value_to_string(val: &[u8], format: &JsonFormat) -> Result<String> {
match format {
JsonFormat::Jsonb => match jsonb::from_slice(val) {
Ok(jsonb_value) => {
let serialized = jsonb_value.to_string();
Ok(serialized)
}
Err(e) => InvalidJsonbSnafu { error: e }.fail(),
},
pub fn jsonb_to_string(val: &[u8]) -> Result<String> {
match jsonb::from_slice(val) {
Ok(jsonb_value) => {
let serialized = jsonb_value.to_string();
Ok(serialized)
}
Err(e) => InvalidJsonbSnafu { error: e }.fail(),
}
}
/// Converts a json type value to serde_json::Value
pub fn json_type_value_to_serde_json(val: &[u8], format: &JsonFormat) -> Result<serde_json::Value> {
match format {
JsonFormat::Jsonb => {
let json_string = json_type_value_to_string(val, format)?;
serde_json::Value::from_str(json_string.as_str())
.context(DeserializeSnafu { json: json_string })
}
}
pub fn jsonb_to_serde_json(val: &[u8]) -> Result<serde_json::Value> {
let json_string = jsonb_to_string(val)?;
serde_json::Value::from_str(json_string.as_str())
.context(DeserializeSnafu { json: json_string })
}
/// Parses a string to a json type value
pub fn parse_string_to_json_type_value(s: &str, format: &JsonFormat) -> Result<Vec<u8>> {
match format {
JsonFormat::Jsonb => jsonb::parse_value(s.as_bytes())
.map_err(|_| InvalidJsonSnafu { value: s }.build())
.map(|json| json.to_vec()),
}
pub fn parse_string_to_jsonb(s: &str) -> Result<Vec<u8>> {
jsonb::parse_value(s.as_bytes())
.map_err(|_| InvalidJsonSnafu { value: s }.build())
.map(|json| json.to_vec())
}

View File

@@ -89,6 +89,8 @@ impl DataType for StringType {
Value::Duration(v) => Some(Value::String(StringBytes::from(v.to_string()))),
Value::Decimal128(v) => Some(Value::String(StringBytes::from(v.to_string()))),
Value::Json(v) => self.try_cast(*v),
// StringBytes is only support for utf-8, Value::Binary and collections are not allowed.
Value::Binary(_) | Value::List(_) | Value::Struct(_) => None,
}

View File

@@ -81,8 +81,12 @@ pub enum Value {
IntervalDayTime(IntervalDayTime),
IntervalMonthDayNano(IntervalMonthDayNano),
// Collection types:
List(ListValue),
Struct(StructValue),
// Json Logical types:
Json(Box<Value>),
}
impl Display for Value {
@@ -144,6 +148,9 @@ impl Display for Value {
.join(", ");
write!(f, "{{ {items} }}")
}
Value::Json(json_data) => {
write!(f, "Json({})", json_data)
}
}
}
}
@@ -190,6 +197,7 @@ macro_rules! define_data_type_func {
$struct::Struct(struct_value) => {
ConcreteDataType::struct_datatype(struct_value.struct_type().clone())
}
$struct::Json(v) => ConcreteDataType::json_native_datatype(v.data_type()),
}
}
};
@@ -200,7 +208,11 @@ impl Value {
/// Returns true if this is a null value.
pub fn is_null(&self) -> bool {
matches!(self, Value::Null)
match self {
Value::Null => true,
Value::Json(inner) => inner.is_null(),
_ => false,
}
}
/// Cast itself to [ListValue].
@@ -208,6 +220,7 @@ impl Value {
match self {
Value::Null => Ok(None),
Value::List(v) => Ok(Some(v)),
Value::Json(inner) => inner.as_list(),
other => error::CastTypeSnafu {
msg: format!("Failed to cast {other:?} to list value"),
}
@@ -219,6 +232,7 @@ impl Value {
match self {
Value::Null => Ok(None),
Value::Struct(v) => Ok(Some(v)),
Value::Json(inner) => inner.as_struct(),
other => error::CastTypeSnafu {
msg: format!("Failed to cast {other:?} to struct value"),
}
@@ -253,6 +267,7 @@ impl Value {
Value::Duration(v) => ValueRef::Duration(*v),
Value::Decimal128(v) => ValueRef::Decimal128(*v),
Value::Struct(v) => ValueRef::Struct(StructValueRef::Ref(v)),
Value::Json(v) => ValueRef::Json(Box::new(v.as_value_ref())),
}
}
@@ -322,6 +337,7 @@ impl Value {
Value::UInt8(v) => Some(*v as _),
Value::UInt16(v) => Some(*v as _),
Value::UInt32(v) => Some(*v as _),
Value::Json(inner) => inner.as_i64(),
_ => None,
}
}
@@ -333,6 +349,7 @@ impl Value {
Value::UInt16(v) => Some(*v as _),
Value::UInt32(v) => Some(*v as _),
Value::UInt64(v) => Some(*v),
Value::Json(inner) => inner.as_u64(),
_ => None,
}
}
@@ -349,6 +366,7 @@ impl Value {
Value::UInt16(v) => Some(*v as _),
Value::UInt32(v) => Some(*v as _),
Value::UInt64(v) => Some(*v as _),
Value::Json(inner) => inner.as_f64_lossy(),
_ => None,
}
}
@@ -365,6 +383,15 @@ impl Value {
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Boolean(b) => Some(*b),
Value::Json(inner) => inner.as_bool(),
_ => None,
}
}
/// Extract the inner JSON value from a JSON type.
pub fn into_json_inner(self) -> Option<Value> {
match self {
Value::Json(v) => Some(*v),
_ => None,
}
}
@@ -411,6 +438,7 @@ impl Value {
},
Value::Decimal128(_) => LogicalTypeId::Decimal128,
Value::Struct(_) => LogicalTypeId::Struct,
Value::Json(_) => LogicalTypeId::Json,
}
}
@@ -420,11 +448,11 @@ impl Value {
let value_type_id = self.logical_type_id();
let output_type_id = output_type.logical_type_id();
ensure!(
// Json type leverage Value(Binary) for storage.
output_type_id == value_type_id
|| self.is_null()
|| (output_type_id == LogicalTypeId::Json
&& value_type_id == LogicalTypeId::Binary),
&& (value_type_id == LogicalTypeId::Binary
|| value_type_id == LogicalTypeId::Json)),
error::ToScalarValueSnafu {
reason: format!(
"expect value to return output_type {output_type_id:?}, actual: {value_type_id:?}",
@@ -467,6 +495,7 @@ impl Value {
let struct_type = output_type.as_struct().unwrap();
struct_value.try_to_scalar_value(struct_type)?
}
Value::Json(v) => v.try_to_scalar_value(output_type)?,
};
Ok(scalar_value)
@@ -519,6 +548,8 @@ impl Value {
Value::IntervalDayTime(x) => Some(Value::IntervalDayTime(x.negative())),
Value::IntervalMonthDayNano(x) => Some(Value::IntervalMonthDayNano(x.negative())),
Value::Json(v) => v.try_negative().map(|neg| Value::Json(Box::new(neg))),
Value::Binary(_)
| Value::String(_)
| Value::Boolean(_)
@@ -877,6 +908,7 @@ impl TryFrom<Value> for serde_json::Value {
.collect::<serde_json::Result<Map<String, serde_json::Value>>>()?;
serde_json::Value::Object(map)
}
Value::Json(v) => serde_json::Value::try_from(*v)?,
};
Ok(json_value)
@@ -1205,6 +1237,7 @@ impl From<ValueRef<'_>> for Value {
ValueRef::List(v) => v.to_value(),
ValueRef::Decimal128(v) => Value::Decimal128(v),
ValueRef::Struct(v) => v.to_value(),
ValueRef::Json(v) => Value::Json(Box::new(Value::from(*v))),
}
}
}
@@ -1247,6 +1280,8 @@ pub enum ValueRef<'a> {
// Compound types:
List(ListValueRef<'a>),
Struct(StructValueRef<'a>),
Json(Box<ValueRef<'a>>),
}
macro_rules! impl_as_for_value_ref {
@@ -1271,7 +1306,11 @@ impl<'a> ValueRef<'a> {
/// Returns true if this is null.
pub fn is_null(&self) -> bool {
matches!(self, ValueRef::Null)
match self {
ValueRef::Null => true,
ValueRef::Json(v) => v.is_null(),
_ => false,
}
}
/// Cast itself to binary slice.
@@ -1648,6 +1687,7 @@ impl ValueRef<'_> {
StructValueRef::Ref(val) => val.estimated_size(),
StructValueRef::RefList { val, .. } => val.iter().map(|v| v.data_size()).sum(),
},
ValueRef::Json(v) => v.data_size(),
}
}
}
@@ -1710,6 +1750,10 @@ pub(crate) mod tests {
ScalarValue::Struct(Arc::new(struct_arrow_array))
}
pub fn build_list_type() -> ConcreteDataType {
ConcreteDataType::list_datatype(ConcreteDataType::boolean_datatype())
}
pub(crate) fn build_list_value() -> ListValue {
let items = vec![Value::Boolean(true), Value::Boolean(false)];
ListValue::new(items, ConcreteDataType::boolean_datatype())
@@ -2185,6 +2229,23 @@ pub(crate) mod tests {
&ConcreteDataType::struct_datatype(build_struct_type()),
&Value::Struct(build_struct_value()),
);
check_type_and_value(
&ConcreteDataType::json_native_datatype(ConcreteDataType::boolean_datatype()),
&Value::Json(Box::new(Value::Boolean(true))),
);
check_type_and_value(
&ConcreteDataType::json_native_datatype(build_list_type()),
&Value::Json(Box::new(Value::List(build_list_value()))),
);
check_type_and_value(
&ConcreteDataType::json_native_datatype(ConcreteDataType::struct_datatype(
build_struct_type(),
)),
&Value::Json(Box::new(Value::Struct(build_struct_value()))),
);
}
#[test]
@@ -2315,7 +2376,35 @@ pub(crate) mod tests {
)
.unwrap();
assert_eq!(
serde_json::Value::try_from(Value::Struct(struct_value)).unwrap(),
serde_json::Value::try_from(Value::Struct(struct_value.clone())).unwrap(),
serde_json::json!({
"num": 42,
"name": "tomcat",
"yes_or_no": true
})
);
// string wrapped in json
assert_eq!(
serde_json::Value::try_from(Value::Json(Box::new(Value::String("hello".into()))))
.unwrap(),
serde_json::json!("hello")
);
// list wrapped in json
assert_eq!(
serde_json::Value::try_from(Value::Json(Box::new(Value::List(ListValue::new(
vec![Value::Int32(1), Value::Int32(2), Value::Int32(3),],
ConcreteDataType::int32_datatype()
)))))
.unwrap(),
serde_json::json!([1, 2, 3])
);
// struct wrapped in json
assert_eq!(
serde_json::Value::try_from(Value::Json(Box::new(Value::Struct(struct_value))))
.unwrap(),
serde_json::json!({
"num": 42,
"name": "tomcat",
@@ -2327,6 +2416,7 @@ pub(crate) mod tests {
#[test]
fn test_null_value() {
assert!(Value::Null.is_null());
assert!(Value::Json(Box::new(Value::Null)).is_null());
assert!(!Value::Boolean(true).is_null());
assert!(Value::Null < Value::Boolean(false));
assert!(Value::Boolean(true) > Value::Null);
@@ -2405,6 +2495,13 @@ pub(crate) mod tests {
ValueRef::Struct(StructValueRef::Ref(&struct_value)),
Value::Struct(struct_value.clone()).as_value_ref()
);
assert_eq!(
ValueRef::Json(Box::new(ValueRef::Struct(StructValueRef::Ref(
&struct_value
)))),
Value::Json(Box::new(Value::Struct(struct_value.clone()))).as_value_ref()
);
}
#[test]
@@ -2525,6 +2622,11 @@ pub(crate) mod tests {
Value::Struct(build_struct_value()).to_string(),
"{ id: 1, name: tom, age: 25, address: 94038, awards: Boolean[true, false] }"
);
assert_eq!(
Value::Json(Box::new(Value::Struct(build_struct_value()))).to_string(),
"Json({ id: 1, name: tom, age: 25, address: 94038, awards: Boolean[true, false] })"
)
}
#[test]
@@ -3013,6 +3115,13 @@ pub(crate) mod tests {
&ValueRef::Struct(StructValueRef::Ref(&build_struct_value())),
15,
);
check_value_ref_size_eq(
&ValueRef::Json(Box::new(ValueRef::Struct(StructValueRef::Ref(
&build_struct_value(),
)))),
15,
);
}
#[test]