convert json functions to use AsValueRef

This commit is contained in:
pedrocarlo
2025-11-10 23:06:46 -03:00
parent 98d268cdc6
commit 84268c155b
5 changed files with 420 additions and 287 deletions

View File

@@ -1,6 +1,7 @@
use std::cell::{Cell, UnsafeCell};
use crate::Value;
use crate::types::AsValueRef;
use crate::{Value, ValueRef};
use super::jsonb::Jsonb;
@@ -38,25 +39,27 @@ impl JsonCache {
oldest_idx
}
pub fn insert(&mut self, key: &Value, value: &Jsonb) {
pub fn insert(&mut self, key: impl AsValueRef, value: &Jsonb) {
let key = key.as_value_ref();
if self.used < JSON_CACHE_SIZE {
self.entries[self.used] = Some((key.clone(), value.clone()));
self.entries[self.used] = Some((key.to_owned(), value.clone()));
self.age[self.used] = self.counter;
self.counter += 1;
self.used += 1
} else {
let id = self.find_oldest_entry();
self.entries[id] = Some((key.clone(), value.clone()));
self.entries[id] = Some((key.to_owned(), value.clone()));
self.age[id] = self.counter;
self.counter += 1;
}
}
pub fn lookup(&mut self, key: &Value) -> Option<Jsonb> {
pub fn lookup(&mut self, key: impl AsValueRef) -> Option<Jsonb> {
let key = key.as_value_ref();
for i in (0..self.used).rev() {
if let Some((stored_key, value)) = &self.entries[i] {
if key == stored_key {
if key == *stored_key {
self.age[i] = self.counter;
self.counter += 1;
let json = value.clone();
@@ -89,7 +92,7 @@ impl JsonCacheCell {
}
#[cfg(test)]
pub fn lookup(&self, key: &Value) -> Option<Jsonb> {
pub fn lookup(&self, key: impl AsValueRef) -> Option<Jsonb> {
assert!(!self.accessed.get());
self.accessed.set(true);
@@ -113,11 +116,12 @@ impl JsonCacheCell {
pub fn get_or_insert_with(
&self,
key: &Value,
value: impl Fn(&Value) -> crate::Result<Jsonb>,
key: impl AsValueRef,
value: impl FnOnce(ValueRef) -> crate::Result<Jsonb>,
) -> crate::Result<Jsonb> {
assert!(!self.accessed.get());
let key = key.as_value_ref();
self.accessed.set(true);
let result = unsafe {
let cache_ptr = self.inner.get();
@@ -354,7 +358,7 @@ mod tests {
// Insert the value using get_or_insert_with
let insert_result = cache_cell.get_or_insert_with(&key, |k| {
// Verify that k is the same as our key
assert_eq!(k, &key);
assert_eq!(k, key);
Ok(value.clone())
});

View File

@@ -11,8 +11,7 @@ pub use crate::json::ops::{
jsonb_replace,
};
use crate::json::path::{json_path, JsonPath, PathElement};
use crate::types::{Text, TextRef, TextSubtype, Value, ValueType};
use crate::vdbe::Register;
use crate::types::{AsValueRef, Text, TextSubtype, Value, ValueType};
use crate::{bail_constraint_error, bail_parse_error, LimboError, ValueRef};
pub use cache::JsonCacheCell;
use jsonb::{ElementType, Jsonb, JsonbHeader, PathOperationMode, SearchOperation, SetOperation};
@@ -100,17 +99,9 @@ pub fn json_from_raw_bytes_agg(data: &[u8], raw: bool) -> crate::Result<Value> {
}
}
pub fn convert_dbtype_to_jsonb(val: &Value, strict: Conv) -> crate::Result<Jsonb> {
convert_ref_dbtype_to_jsonb(
match val {
Value::Null => ValueRef::Null,
Value::Integer(x) => ValueRef::Integer(*x),
Value::Float(x) => ValueRef::Float(*x),
Value::Text(text) => ValueRef::Text(TextRef::new(text.as_str(), text.subtype)),
Value::Blob(items) => ValueRef::Blob(items.as_slice()),
},
strict,
)
pub fn convert_dbtype_to_jsonb(val: impl AsValueRef, strict: Conv) -> crate::Result<Jsonb> {
let val = val.as_value_ref();
convert_ref_dbtype_to_jsonb(val, strict)
}
fn parse_as_json_text(slice: &[u8], mode: Conv) -> crate::Result<Jsonb> {
@@ -187,18 +178,27 @@ pub fn convert_ref_dbtype_to_jsonb(val: ValueRef<'_>, strict: Conv) -> crate::Re
}
}
pub fn curry_convert_dbtype_to_jsonb(strict: Conv) -> impl Fn(&Value) -> crate::Result<Jsonb> {
pub fn curry_convert_dbtype_to_jsonb(
strict: Conv,
) -> impl FnOnce(ValueRef) -> crate::Result<Jsonb> {
move |val| convert_dbtype_to_jsonb(val, strict)
}
pub fn json_array(values: &[Register]) -> crate::Result<Value> {
pub fn json_array<I, E, V>(values: I) -> crate::Result<Value>
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let values = values.into_iter();
let mut json = Jsonb::make_empty_array(values.len());
for value in values.iter() {
if matches!(value.get_value(), Value::Blob(_)) {
for value in values {
let value = value.as_value_ref();
if matches!(value, ValueRef::Blob(_)) {
crate::bail_constraint_error!("JSON cannot hold BLOB values")
}
let value = convert_dbtype_to_jsonb(value.get_value(), Conv::NotStrict)?;
let value = convert_dbtype_to_jsonb(value, Conv::NotStrict)?;
json.append_jsonb_to_end(value.data());
}
json.finalize_unsafe(ElementType::ARRAY)?;
@@ -206,14 +206,21 @@ pub fn json_array(values: &[Register]) -> crate::Result<Value> {
json_string_to_db_type(json, ElementType::ARRAY, OutputVariant::ElementType)
}
pub fn jsonb_array(values: &[Register]) -> crate::Result<Value> {
pub fn jsonb_array<I, E, V>(values: I) -> crate::Result<Value>
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let values = values.into_iter();
let mut json = Jsonb::make_empty_array(values.len());
for value in values.iter() {
if matches!(value.get_value(), Value::Blob(_)) {
for value in values {
let value = value.as_value_ref();
if matches!(value, ValueRef::Blob(_)) {
crate::bail_constraint_error!("JSON cannot hold BLOB values")
}
let value = convert_dbtype_to_jsonb(value.get_value(), Conv::NotStrict)?;
let value = convert_dbtype_to_jsonb(value, Conv::NotStrict)?;
json.append_jsonb_to_end(value.data());
}
json.finalize_unsafe(ElementType::ARRAY)?;
@@ -246,19 +253,27 @@ pub fn json_array_length(
Ok(Value::Null)
}
pub fn json_set(args: &[Register], json_cache: &JsonCacheCell) -> crate::Result<Value> {
if args.is_empty() {
pub fn json_set<I, E, V>(args: I, json_cache: &JsonCacheCell) -> crate::Result<Value>
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let mut args = args.into_iter();
if args.len() == 0 {
return Ok(Value::Null);
}
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(args[0].get_value(), make_jsonb_fn)?;
let other = args[1..].chunks_exact(2);
let mut json = json_cache.get_or_insert_with(args.next().unwrap(), make_jsonb_fn)?;
for chunk in other {
let path = json_path_from_db_value(chunk[0].get_value(), true)?;
// TODO: when `array_chunks` is stabilized we can chunk by 2 here
while args.len() > 1 {
let first = args.next().unwrap();
let value = convert_dbtype_to_jsonb(chunk[1].get_value(), Conv::NotStrict)?;
let path = json_path_from_db_value(&first, true)?;
let second = args.next().unwrap();
let value = convert_dbtype_to_jsonb(second, Conv::NotStrict)?;
let mut op = SetOperation::new(value);
if let Some(path) = path {
let _ = json.operate_on_path(&path, &mut op);
@@ -270,19 +285,27 @@ pub fn json_set(args: &[Register], json_cache: &JsonCacheCell) -> crate::Result<
json_string_to_db_type(json, el_type, OutputVariant::String)
}
pub fn jsonb_set(args: &[Register], json_cache: &JsonCacheCell) -> crate::Result<Value> {
if args.is_empty() {
pub fn jsonb_set<I, E, V>(args: I, json_cache: &JsonCacheCell) -> crate::Result<Value>
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let mut args = args.into_iter();
if args.len() == 0 {
return Ok(Value::Null);
}
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(args[0].get_value(), make_jsonb_fn)?;
let other = args[1..].chunks_exact(2);
let mut json = json_cache.get_or_insert_with(args.next().unwrap(), make_jsonb_fn)?;
for chunk in other {
let path = json_path_from_db_value(chunk[0].get_value(), true)?;
// TODO: when `array_chunks` is stabilized we can chunk by 2 here
while args.len() > 1 {
let first = args.next().unwrap();
let path = json_path_from_db_value(&first, true)?;
let value = convert_dbtype_to_jsonb(chunk[1].get_value(), Conv::NotStrict)?;
let second = args.next().unwrap();
let value = convert_dbtype_to_jsonb(second, Conv::NotStrict)?;
let mut op = SetOperation::new(value);
if let Some(path) = path {
let _ = json.operate_on_path(&path, &mut op);
@@ -297,15 +320,16 @@ pub fn jsonb_set(args: &[Register], json_cache: &JsonCacheCell) -> crate::Result
/// Implements the -> operator. Always returns a proper JSON value.
/// https://sqlite.org/json1.html#the_and_operators
pub fn json_arrow_extract(
value: &Value,
path: &Value,
value: impl AsValueRef,
path: impl AsValueRef,
json_cache: &JsonCacheCell,
) -> crate::Result<Value> {
if let Value::Null = value {
let value = value.as_value_ref();
if let ValueRef::Null = value {
return Ok(Value::Null);
}
if let Some(path) = json_path_from_db_value(path, false)? {
if let Some(path) = json_path_from_db_value(&path, false)? {
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(value, make_jsonb_fn)?;
let mut op = SearchOperation::new(json.len());
@@ -324,14 +348,15 @@ pub fn json_arrow_extract(
/// Implements the ->> operator. Always returns a SQL representation of the JSON subcomponent.
/// https://sqlite.org/json1.html#the_and_operators
pub fn json_arrow_shift_extract(
value: &Value,
path: &Value,
value: impl AsValueRef,
path: impl AsValueRef,
json_cache: &JsonCacheCell,
) -> crate::Result<Value> {
if let Value::Null = value {
let value = value.as_value_ref();
if let ValueRef::Null = value {
return Ok(Value::Null);
}
if let Some(path) = json_path_from_db_value(path, false)? {
if let Some(path) = json_path_from_db_value(&path, false)? {
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(value, make_jsonb_fn)?;
let mut op = SearchOperation::new(json.len());
@@ -359,16 +384,23 @@ pub fn json_arrow_shift_extract(
/// Extracts a JSON value from a JSON object or array.
/// If there's only a single path, the return value might be either a TEXT or a database type.
/// https://sqlite.org/json1.html#the_json_extract_function
pub fn json_extract(
value: &Value,
paths: &[Register],
pub fn json_extract<I, E, V>(
value: impl AsValueRef,
paths: I,
json_cache: &JsonCacheCell,
) -> crate::Result<Value> {
if let Value::Null = value {
) -> crate::Result<Value>
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let value = value.as_value_ref();
if let ValueRef::Null = value {
return Ok(Value::Null);
}
if paths.is_empty() {
let paths = paths.into_iter();
if paths.len() == 0 {
return Ok(Value::Null);
}
let convert_to_jsonb = curry_convert_dbtype_to_jsonb(Conv::Strict);
@@ -380,16 +412,22 @@ pub fn json_extract(
Ok(result)
}
pub fn jsonb_extract(
pub fn jsonb_extract<I, E, V>(
value: &Value,
paths: &[Register],
paths: I,
json_cache: &JsonCacheCell,
) -> crate::Result<Value> {
) -> crate::Result<Value>
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
if let Value::Null = value {
return Ok(Value::Null);
}
if paths.is_empty() {
let paths = paths.into_iter();
if paths.len() == 0 {
return Ok(Value::Null);
}
let convert_to_jsonb = curry_convert_dbtype_to_jsonb(Conv::Strict);
@@ -401,10 +439,14 @@ pub fn jsonb_extract(
Ok(result)
}
fn jsonb_extract_internal(value: Jsonb, paths: &[Register]) -> crate::Result<(Jsonb, ElementType)> {
fn jsonb_extract_internal<E, V>(value: Jsonb, mut paths: E) -> crate::Result<(Jsonb, ElementType)>
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
{
let null = Jsonb::from_raw_data(JsonbHeader::make_null().into_bytes().as_bytes());
if paths.len() == 1 {
if let Some(path) = json_path_from_db_value(paths[0].get_value(), true)? {
if let Some(path) = json_path_from_db_value(&paths.next().unwrap(), true)? {
let mut json = value;
let mut op = SearchOperation::new(json.len());
@@ -428,10 +470,8 @@ fn jsonb_extract_internal(value: Jsonb, paths: &[Register]) -> crate::Result<(Js
let mut result = Jsonb::make_empty_array(json.len());
// TODO: make an op to avoid creating new json for every path element
let paths = paths
.iter()
.map(|p| json_path_from_db_value(p.get_value(), true));
for path in paths {
let path = json_path_from_db_value(&path, true);
if let Some(path) = path? {
let mut op = SearchOperation::new(json.len());
let res = json.operate_on_path(&path, &mut op);
@@ -502,8 +542,9 @@ pub fn json_string_to_db_type(
}
}
pub fn json_type(value: &Value, path: Option<&Value>) -> crate::Result<Value> {
if let Value::Null = value {
pub fn json_type(value: impl AsValueRef, path: Option<impl AsValueRef>) -> crate::Result<Value> {
let value = value.as_value_ref();
if let ValueRef::Null = value {
return Ok(Value::Null);
}
if path.is_none() {
@@ -512,7 +553,7 @@ pub fn json_type(value: &Value, path: Option<&Value>) -> crate::Result<Value> {
return Ok(Value::Text(Text::json(element_type.into())));
}
if let Some(path) = json_path_from_db_value(path.unwrap(), true)? {
if let Some(path) = json_path_from_db_value(&path.unwrap(), true)? {
let mut json = convert_dbtype_to_jsonb(value, Conv::Strict)?;
if let Ok(mut path) = json.navigate_path(&path, PathOperationMode::ReplaceExisting) {
@@ -531,16 +572,20 @@ pub fn json_type(value: &Value, path: Option<&Value>) -> crate::Result<Value> {
}
}
fn json_path_from_db_value(path: &Value, strict: bool) -> crate::Result<Option<JsonPath<'_>>> {
fn json_path_from_db_value<'a>(
path: &'a (impl AsValueRef + 'a),
strict: bool,
) -> crate::Result<Option<JsonPath<'a>>> {
let path = path.as_value_ref();
let json_path = if strict {
match path {
Value::Text(t) => json_path(t.as_str())?,
Value::Null => return Ok(None),
ValueRef::Text(t) => json_path(t.as_str())?,
ValueRef::Null => return Ok(None),
_ => crate::bail_constraint_error!("JSON path error near: {:?}", path.to_string()),
}
} else {
match path {
Value::Text(t) => {
ValueRef::Text(t) => {
if t.as_str().starts_with("$") {
json_path(t.as_str())?
} else {
@@ -552,14 +597,14 @@ fn json_path_from_db_value(path: &Value, strict: bool) -> crate::Result<Option<J
}
}
}
Value::Null => return Ok(None),
Value::Integer(i) => JsonPath {
ValueRef::Null => return Ok(None),
ValueRef::Integer(i) => JsonPath {
elements: vec![
PathElement::Root(),
PathElement::ArrayLocator(Some(*i as i32)),
PathElement::ArrayLocator(Some(i as i32)),
],
},
Value::Float(f) => JsonPath {
ValueRef::Float(f) => JsonPath {
elements: vec![
PathElement::Root(),
PathElement::Key(Cow::Owned(f.to_string()), false),
@@ -572,9 +617,9 @@ fn json_path_from_db_value(path: &Value, strict: bool) -> crate::Result<Option<J
Ok(Some(json_path))
}
pub fn json_error_position(json: &Value) -> crate::Result<Value> {
match json {
Value::Text(t) => match Jsonb::from_str(t.as_str()) {
pub fn json_error_position(json: impl AsValueRef) -> crate::Result<Value> {
match json.as_value_ref() {
ValueRef::Text(t) => match Jsonb::from_str(t.as_str()) {
Ok(_) => Ok(Value::Integer(0)),
Err(JsonError::Message { location, .. }) => {
if let Some(loc) = location {
@@ -587,10 +632,10 @@ pub fn json_error_position(json: &Value) -> crate::Result<Value> {
}
}
},
Value::Blob(_) => {
ValueRef::Blob(_) => {
bail_parse_error!("Unsupported")
}
Value::Null => Ok(Value::Null),
ValueRef::Null => Ok(Value::Null),
_ => Ok(Value::Integer(0)),
}
}
@@ -598,19 +643,30 @@ pub fn json_error_position(json: &Value) -> crate::Result<Value> {
/// Constructs a JSON object from a list of values that represent key-value pairs.
/// The number of values must be even, and the first value of each pair (which represents the map key)
/// must be a TEXT value. The second value of each pair can be any JSON value (which represents the map value)
pub fn json_object(values: &[Register]) -> crate::Result<Value> {
pub fn json_object<I, E, V>(values: I) -> crate::Result<Value>
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let mut values = values.into_iter();
if values.len() % 2 != 0 {
bail_constraint_error!("json_object() requires an even number of arguments")
}
let mut json = Jsonb::make_empty_obj(values.len() * 50);
for chunk in values.chunks_exact(2) {
if chunk[0].get_value().value_type() != ValueType::Text {
// TODO: when `array_chunks` is stabilized we can chunk by 2 here
while values.len() > 1 {
let first = values.next().unwrap();
let first = first.as_value_ref();
if first.value_type() != ValueType::Text {
bail_constraint_error!("json_object() labels must be TEXT")
}
let key = convert_dbtype_to_jsonb(chunk[0].get_value(), Conv::ToString)?;
let key = convert_dbtype_to_jsonb(first, Conv::ToString)?;
json.append_jsonb_to_end(key.data());
let value = convert_dbtype_to_jsonb(chunk[1].get_value(), Conv::NotStrict)?;
let second = values.next().unwrap();
let value = convert_dbtype_to_jsonb(second, Conv::NotStrict)?;
json.append_jsonb_to_end(value.data());
}
@@ -619,19 +675,30 @@ pub fn json_object(values: &[Register]) -> crate::Result<Value> {
json_string_to_db_type(json, ElementType::OBJECT, OutputVariant::String)
}
pub fn jsonb_object(values: &[Register]) -> crate::Result<Value> {
pub fn jsonb_object<I, E, V>(values: I) -> crate::Result<Value>
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let mut values = values.into_iter();
if values.len() % 2 != 0 {
bail_constraint_error!("json_object() requires an even number of arguments")
}
let mut json = Jsonb::make_empty_obj(values.len() * 50);
for chunk in values.chunks_exact(2) {
if chunk[0].get_value().value_type() != ValueType::Text {
// TODO: when `array_chunks` is stabilized we can chunk by 2 here
while values.len() > 1 {
let first = values.next().unwrap();
let first = first.as_value_ref();
if first.value_type() != ValueType::Text {
bail_constraint_error!("json_object() labels must be TEXT")
}
let key = convert_dbtype_to_jsonb(chunk[0].get_value(), Conv::ToString)?;
let key = convert_dbtype_to_jsonb(first, Conv::ToString)?;
json.append_jsonb_to_end(key.data());
let value = convert_dbtype_to_jsonb(chunk[1].get_value(), Conv::NotStrict)?;
let second = values.next().unwrap();
let value = convert_dbtype_to_jsonb(second, Conv::NotStrict)?;
json.append_jsonb_to_end(value.data());
}
@@ -642,8 +709,9 @@ pub fn jsonb_object(values: &[Register]) -> crate::Result<Value> {
/// Tries to convert the value to jsonb. Returns Value::Integer(1) if it the conversion
/// succeeded, and Value::Integer(0) if it didn't.
pub fn is_json_valid(json_value: &Value) -> Value {
if matches!(json_value, Value::Null) {
pub fn is_json_valid(json_value: impl AsValueRef) -> Value {
let json_value = json_value.as_value_ref();
if matches!(json_value, ValueRef::Null) {
return Value::Null;
}
convert_dbtype_to_jsonb(json_value, Conv::Strict)
@@ -651,9 +719,10 @@ pub fn is_json_valid(json_value: &Value) -> Value {
.unwrap_or(Value::Integer(0))
}
pub fn json_quote(value: &Value) -> crate::Result<Value> {
pub fn json_quote(value: impl AsValueRef) -> crate::Result<Value> {
let value = value.as_value_ref();
match value {
Value::Text(ref t) => {
ValueRef::Text(ref t) => {
// If X is a JSON value returned by another JSON function,
// then this function is a no-op
if t.subtype == TextSubtype::Json {
@@ -678,10 +747,10 @@ pub fn json_quote(value: &Value) -> crate::Result<Value> {
Ok(Value::build_text(escaped_value))
}
// Numbers are unquoted in json
Value::Integer(ref int) => Ok(Value::Integer(int.to_owned())),
Value::Float(ref float) => Ok(Value::Float(float.to_owned())),
Value::Blob(_) => crate::bail_constraint_error!("JSON cannot hold BLOB values"),
Value::Null => Ok(Value::build_text("null")),
ValueRef::Integer(int) => Ok(Value::Integer(int)),
ValueRef::Float(float) => Ok(Value::Float(float)),
ValueRef::Blob(_) => crate::bail_constraint_error!("JSON cannot hold BLOB values"),
ValueRef::Null => Ok(Value::build_text("null")),
}
}
@@ -808,14 +877,9 @@ mod tests {
#[test]
fn test_json_array_simple() {
let text = Register::Value(Value::build_text("value1"));
let json = Register::Value(Value::Text(Text::json("\"value2\"".to_string())));
let input = vec![
text,
json,
Register::Value(Value::Integer(1)),
Register::Value(Value::Float(1.1)),
];
let text = Value::build_text("value1");
let json = Value::Text(Text::json("\"value2\"".to_string()));
let input = [text, json, Value::Integer(1), Value::Float(1.1)];
let result = json_array(&input).unwrap();
if let Value::Text(res) = result {
@@ -828,9 +892,9 @@ mod tests {
#[test]
fn test_json_array_empty() {
let input = vec![];
let input: [Value; 0] = [];
let result = json_array(&input).unwrap();
let result = json_array(input).unwrap();
if let Value::Text(res) = result {
assert_eq!(res.as_str(), "[]");
assert_eq!(res.subtype, TextSubtype::Json);
@@ -841,9 +905,9 @@ mod tests {
#[test]
fn test_json_array_blob_invalid() {
let blob = Register::Value(Value::Blob("1".as_bytes().to_vec()));
let blob = Value::Blob("1".as_bytes().to_vec());
let input = vec![blob];
let input = [blob];
let result = json_array(&input);
@@ -968,7 +1032,7 @@ mod tests {
let json_cache = JsonCacheCell::new();
let result = json_extract(
&Value::build_text("{\"a\":2}"),
&[Register::Value(Value::build_text("$.x"))],
&[Value::build_text("$.x")],
&json_cache,
);
@@ -980,11 +1044,7 @@ mod tests {
#[test]
fn test_json_extract_null_path() {
let json_cache = JsonCacheCell::new();
let result = json_extract(
&Value::build_text("{\"a\":2}"),
&[Register::Value(Value::Null)],
&json_cache,
);
let result = json_extract(&Value::build_text("{\"a\":2}"), &[Value::Null], &json_cache);
match result {
Ok(Value::Null) => (),
@@ -997,7 +1057,7 @@ mod tests {
let json_cache = JsonCacheCell::new();
let result = json_extract(
&Value::build_text("{\"a\":2}"),
&[Register::Value(Value::Float(1.1))],
&[Value::Float(1.1)],
&json_cache,
);
@@ -1058,9 +1118,9 @@ mod tests {
#[test]
fn test_json_object_simple() {
let key = Register::Value(Value::build_text("key"));
let value = Register::Value(Value::build_text("value"));
let input = vec![key, value];
let key = Value::build_text("key");
let value = Value::build_text("value");
let input = [key, value];
let result = json_object(&input).unwrap();
let Value::Text(json_text) = result else {
@@ -1082,17 +1142,17 @@ mod tests {
let null_key = Value::build_text("null_key");
let null_value = Value::Null;
let input = vec![
Register::Value(text_key),
Register::Value(text_value),
Register::Value(json_key),
Register::Value(json_value),
Register::Value(integer_key),
Register::Value(integer_value),
Register::Value(float_key),
Register::Value(float_value),
Register::Value(null_key),
Register::Value(null_value),
let input = [
text_key,
text_value,
json_key,
json_value,
integer_key,
integer_value,
float_key,
float_value,
null_key,
null_value,
];
let result = json_object(&input).unwrap();
@@ -1107,9 +1167,9 @@ mod tests {
#[test]
fn test_json_object_json_value_is_rendered_as_json() {
let key = Register::Value(Value::build_text("key"));
let value = Register::Value(Value::Text(Text::json(r#"{"json":"value"}"#.to_string())));
let input = vec![key, value];
let key = Value::build_text("key");
let value = Value::Text(Text::json(r#"{"json":"value"}"#.to_string()));
let input = [key, value];
let result = json_object(&input).unwrap();
let Value::Text(json_text) = result else {
@@ -1120,9 +1180,9 @@ mod tests {
#[test]
fn test_json_object_json_text_value_is_rendered_as_regular_text() {
let key = Register::Value(Value::build_text("key"));
let value = Register::Value(Value::Text(Text::new(r#"{"json":"value"}"#)));
let input = vec![key, value];
let key = Value::build_text("key");
let value = Value::Text(Text::new(r#"{"json":"value"}"#));
let input = [key, value];
let result = json_object(&input).unwrap();
let Value::Text(json_text) = result else {
@@ -1133,13 +1193,13 @@ mod tests {
#[test]
fn test_json_object_nested() {
let key = Register::Value(Value::build_text("key"));
let value = Register::Value(Value::build_text("value"));
let input = vec![key, value];
let key = Value::build_text("key");
let value = Value::build_text("value");
let input = [key, value];
let parent_key = Register::Value(Value::build_text("parent_key"));
let parent_value = Register::Value(json_object(&input).unwrap());
let parent_input = vec![parent_key, parent_value];
let parent_key = Value::build_text("parent_key");
let parent_value = json_object(&input).unwrap();
let parent_input = [parent_key, parent_value];
let result = json_object(&parent_input).unwrap();
@@ -1151,9 +1211,9 @@ mod tests {
#[test]
fn test_json_object_duplicated_keys() {
let key = Register::Value(Value::build_text("key"));
let value = Register::Value(Value::build_text("value"));
let input = vec![key.clone(), value.clone(), key, value];
let key = Value::build_text("key");
let value = Value::build_text("value");
let input = [key.clone(), value.clone(), key, value];
let result = json_object(&input).unwrap();
let Value::Text(json_text) = result else {
@@ -1164,7 +1224,7 @@ mod tests {
#[test]
fn test_json_object_empty() {
let input = vec![];
let input: [Value; 0] = [];
let result = json_object(&input).unwrap();
let Value::Text(json_text) = result else {
@@ -1175,9 +1235,9 @@ mod tests {
#[test]
fn test_json_object_non_text_key() {
let key = Register::Value(Value::Integer(1));
let value = Register::Value(Value::build_text("value"));
let input = vec![key, value];
let key = Value::Integer(1);
let value = Value::build_text("value");
let input = [key, value];
match json_object(&input) {
Ok(_) => panic!("Expected error for non-TEXT key"),
@@ -1187,9 +1247,9 @@ mod tests {
#[test]
fn test_json_odd_number_of_values() {
let key = Register::Value(Value::build_text("key"));
let value = Register::Value(Value::build_text("value"));
let input = vec![key.clone(), value, key];
let key = Value::build_text("key");
let value = Value::build_text("value");
let input = [key.clone(), value, key];
assert!(json_object(&input).is_err());
}
@@ -1326,9 +1386,9 @@ mod tests {
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
Register::Value(Value::build_text("{}")),
Register::Value(Value::build_text("$.field")),
Register::Value(Value::build_text("value")),
Value::build_text("{}"),
Value::build_text("$.field"),
Value::build_text("value"),
],
&json_cache,
);
@@ -1343,9 +1403,9 @@ mod tests {
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
Register::Value(Value::build_text(r#"{"field":"old_value"}"#)),
Register::Value(Value::build_text("$.field")),
Register::Value(Value::build_text("new_value")),
Value::build_text(r#"{"field":"old_value"}"#),
Value::build_text("$.field"),
Value::build_text("new_value"),
],
&json_cache,
);
@@ -1363,9 +1423,9 @@ mod tests {
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
Register::Value(Value::build_text("{}")),
Register::Value(Value::build_text("$.object.doesnt.exist")),
Register::Value(Value::build_text("value")),
Value::build_text("{}"),
Value::build_text("$.object.doesnt.exist"),
Value::build_text("value"),
],
&json_cache,
);
@@ -1383,9 +1443,9 @@ mod tests {
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
Register::Value(Value::build_text("[]")),
Register::Value(Value::build_text("$[0]")),
Register::Value(Value::build_text("value")),
Value::build_text("[]"),
Value::build_text("$[0]"),
Value::build_text("value"),
],
&json_cache,
);
@@ -1400,9 +1460,9 @@ mod tests {
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
Register::Value(Value::build_text("{}")),
Register::Value(Value::build_text("$.some_array[0]")),
Register::Value(Value::Integer(123)),
Value::build_text("{}"),
Value::build_text("$.some_array[0]"),
Value::Integer(123),
],
&json_cache,
);
@@ -1420,9 +1480,9 @@ mod tests {
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
Register::Value(Value::build_text("[123]")),
Register::Value(Value::build_text("$[1]")),
Register::Value(Value::Integer(456)),
Value::build_text("[123]"),
Value::build_text("$[1]"),
Value::Integer(456),
],
&json_cache,
);
@@ -1437,9 +1497,9 @@ mod tests {
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
Register::Value(Value::build_text("[123]")),
Register::Value(Value::build_text("$[200]")),
Register::Value(Value::Integer(456)),
Value::build_text("[123]"),
Value::build_text("$[200]"),
Value::Integer(456),
],
&json_cache,
);
@@ -1454,9 +1514,9 @@ mod tests {
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
Register::Value(Value::build_text("[123]")),
Register::Value(Value::build_text("$[0]")),
Register::Value(Value::Integer(456)),
Value::build_text("[123]"),
Value::build_text("$[0]"),
Value::Integer(456),
],
&json_cache,
);
@@ -1470,11 +1530,7 @@ mod tests {
fn test_json_set_null_path() {
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
Register::Value(Value::build_text("{}")),
Register::Value(Value::Null),
Register::Value(Value::Integer(456)),
],
&[Value::build_text("{}"), Value::Null, Value::Integer(456)],
&json_cache,
);
@@ -1488,11 +1544,11 @@ mod tests {
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
Register::Value(Value::build_text("[123]")),
Register::Value(Value::build_text("$[0]")),
Register::Value(Value::Integer(456)),
Register::Value(Value::build_text("$[1]")),
Register::Value(Value::Integer(789)),
Value::build_text("[123]"),
Value::build_text("$[0]"),
Value::Integer(456),
Value::build_text("$[1]"),
Value::Integer(789),
],
&json_cache,
);
@@ -1507,9 +1563,9 @@ mod tests {
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
Register::Value(Value::build_text("{}")),
Register::Value(Value::build_text("$.object[0].field")),
Register::Value(Value::Integer(123)),
Value::build_text("{}"),
Value::build_text("$.object[0].field"),
Value::Integer(123),
],
&json_cache,
);
@@ -1527,9 +1583,9 @@ mod tests {
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
Register::Value(Value::build_text("{}")),
Register::Value(Value::build_text("$.object[0][0]")),
Register::Value(Value::Integer(123)),
Value::build_text("{}"),
Value::build_text("$.object[0][0]"),
Value::Integer(123),
],
&json_cache,
);
@@ -1544,11 +1600,11 @@ mod tests {
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
Register::Value(Value::build_text("{}")),
Register::Value(Value::build_text("$.object[123].another")),
Register::Value(Value::build_text("value")),
Register::Value(Value::build_text("$.field")),
Register::Value(Value::build_text("value")),
Value::build_text("{}"),
Value::build_text("$.object[123].another"),
Value::build_text("value"),
Value::build_text("$.field"),
Value::build_text("value"),
],
&json_cache,
);

View File

@@ -1,4 +1,7 @@
use crate::{types::Value, vdbe::Register};
use crate::{
types::{AsValueRef, Value},
ValueRef,
};
use super::{
convert_dbtype_to_jsonb, curry_convert_dbtype_to_jsonb, json_path_from_db_value,
@@ -12,16 +15,22 @@ use super::{
/// * If the patch contains a scalar value, the target is replaced with that value
/// * If both target and patch are objects, the patch is recursively applied
/// * null values in the patch result in property removal from the target
pub fn json_patch(target: &Value, patch: &Value, cache: &JsonCacheCell) -> crate::Result<Value> {
pub fn json_patch(
target: impl AsValueRef,
patch: impl AsValueRef,
cache: &JsonCacheCell,
) -> crate::Result<Value> {
let (target, patch) = (target.as_value_ref(), patch.as_value_ref());
match (target, patch) {
(Value::Blob(_), _) | (_, Value::Blob(_)) => {
(ValueRef::Blob(_), _) | (_, ValueRef::Blob(_)) => {
crate::bail_constraint_error!("blob is not supported!");
}
_ => (),
}
let make_jsonb = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut target = cache.get_or_insert_with(target, &make_jsonb)?;
let patch = cache.get_or_insert_with(patch, &make_jsonb)?;
let mut target = cache.get_or_insert_with(target, make_jsonb)?;
let make_jsonb = curry_convert_dbtype_to_jsonb(Conv::Strict);
let patch = cache.get_or_insert_with(patch, make_jsonb)?;
target.patch(&patch)?;
@@ -30,16 +39,22 @@ pub fn json_patch(target: &Value, patch: &Value, cache: &JsonCacheCell) -> crate
json_string_to_db_type(target, element_type, OutputVariant::ElementType)
}
pub fn jsonb_patch(target: &Value, patch: &Value, cache: &JsonCacheCell) -> crate::Result<Value> {
pub fn jsonb_patch(
target: impl AsValueRef,
patch: impl AsValueRef,
cache: &JsonCacheCell,
) -> crate::Result<Value> {
let (target, patch) = (target.as_value_ref(), patch.as_value_ref());
match (target, patch) {
(Value::Blob(_), _) | (_, Value::Blob(_)) => {
(ValueRef::Blob(_), _) | (_, ValueRef::Blob(_)) => {
crate::bail_constraint_error!("blob is not supported!");
}
_ => (),
}
let make_jsonb = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut target = cache.get_or_insert_with(target, &make_jsonb)?;
let patch = cache.get_or_insert_with(patch, &make_jsonb)?;
let mut target = cache.get_or_insert_with(target, make_jsonb)?;
let make_jsonb = curry_convert_dbtype_to_jsonb(Conv::Strict);
let patch = cache.get_or_insert_with(patch, make_jsonb)?;
target.patch(&patch)?;
@@ -48,15 +63,21 @@ pub fn jsonb_patch(target: &Value, patch: &Value, cache: &JsonCacheCell) -> crat
json_string_to_db_type(target, element_type, OutputVariant::Binary)
}
pub fn json_remove(args: &[Register], json_cache: &JsonCacheCell) -> crate::Result<Value> {
if args.is_empty() {
pub fn json_remove<I, E, V>(args: I, json_cache: &JsonCacheCell) -> crate::Result<Value>
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let mut args = args.into_iter();
if args.len() == 0 {
return Ok(Value::Null);
}
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(args[0].get_value(), make_jsonb_fn)?;
for arg in &args[1..] {
if let Some(path) = json_path_from_db_value(arg.get_value(), true)? {
let mut json = json_cache.get_or_insert_with(args.next().unwrap(), make_jsonb_fn)?;
for arg in args {
if let Some(path) = json_path_from_db_value(&arg, true)? {
let mut op = DeleteOperation::new();
let _ = json.operate_on_path(&path, &mut op);
}
@@ -67,15 +88,21 @@ pub fn json_remove(args: &[Register], json_cache: &JsonCacheCell) -> crate::Resu
json_string_to_db_type(json, el_type, OutputVariant::String)
}
pub fn jsonb_remove(args: &[Register], json_cache: &JsonCacheCell) -> crate::Result<Value> {
if args.is_empty() {
pub fn jsonb_remove<I, E, V>(args: I, json_cache: &JsonCacheCell) -> crate::Result<Value>
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let mut args = args.into_iter();
if args.len() == 0 {
return Ok(Value::Null);
}
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(args[0].get_value(), make_jsonb_fn)?;
for arg in &args[1..] {
if let Some(path) = json_path_from_db_value(arg.get_value(), true)? {
let mut json = json_cache.get_or_insert_with(args.next().unwrap(), make_jsonb_fn)?;
for arg in args {
if let Some(path) = json_path_from_db_value(&arg, true)? {
let mut op = DeleteOperation::new();
let _ = json.operate_on_path(&path, &mut op);
}
@@ -84,18 +111,26 @@ pub fn jsonb_remove(args: &[Register], json_cache: &JsonCacheCell) -> crate::Res
Ok(Value::Blob(json.data()))
}
pub fn json_replace(args: &[Register], json_cache: &JsonCacheCell) -> crate::Result<Value> {
if args.is_empty() {
pub fn json_replace<I, E, V>(args: I, json_cache: &JsonCacheCell) -> crate::Result<Value>
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let mut args = args.into_iter();
if args.len() == 0 {
return Ok(Value::Null);
}
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(args[0].get_value(), make_jsonb_fn)?;
let other = args[1..].chunks_exact(2);
for chunk in other {
let path = json_path_from_db_value(chunk[0].get_value(), true)?;
let mut json = json_cache.get_or_insert_with(args.next().unwrap(), make_jsonb_fn)?;
// TODO: when `array_chunks` is stabilized we can chunk by 2 here
while args.len() > 1 {
let first = args.next().unwrap();
let path = json_path_from_db_value(&first, true)?;
let value = convert_dbtype_to_jsonb(chunk[1].get_value(), Conv::NotStrict)?;
let second = args.next().unwrap();
let value = convert_dbtype_to_jsonb(&second, Conv::NotStrict)?;
if let Some(path) = path {
let mut op = ReplaceOperation::new(value);
@@ -108,17 +143,26 @@ pub fn json_replace(args: &[Register], json_cache: &JsonCacheCell) -> crate::Res
json_string_to_db_type(json, el_type, super::OutputVariant::String)
}
pub fn jsonb_replace(args: &[Register], json_cache: &JsonCacheCell) -> crate::Result<Value> {
if args.is_empty() {
pub fn jsonb_replace<I, E, V>(args: I, json_cache: &JsonCacheCell) -> crate::Result<Value>
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let mut args = args.into_iter();
if args.len() == 0 {
return Ok(Value::Null);
}
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(args[0].get_value(), make_jsonb_fn)?;
let other = args[1..].chunks_exact(2);
for chunk in other {
let path = json_path_from_db_value(chunk[0].get_value(), true)?;
let value = convert_dbtype_to_jsonb(chunk[1].get_value(), Conv::NotStrict)?;
let mut json = json_cache.get_or_insert_with(args.next().unwrap(), make_jsonb_fn)?;
// TODO: when `array_chunks` is stabilized we can chunk by 2 here
while args.len() > 1 {
let first = args.next().unwrap();
let path = json_path_from_db_value(&first, true)?;
let second = args.next().unwrap();
let value = convert_dbtype_to_jsonb(&second, Conv::NotStrict)?;
if let Some(path) = path {
let mut op = ReplaceOperation::new(value);
@@ -131,17 +175,27 @@ pub fn jsonb_replace(args: &[Register], json_cache: &JsonCacheCell) -> crate::Re
json_string_to_db_type(json, el_type, OutputVariant::Binary)
}
pub fn json_insert(args: &[Register], json_cache: &JsonCacheCell) -> crate::Result<Value> {
if args.is_empty() {
pub fn json_insert<I, E, V>(args: I, json_cache: &JsonCacheCell) -> crate::Result<Value>
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let mut args = args.into_iter();
if args.len() == 0 {
return Ok(Value::Null);
}
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(args[0].get_value(), make_jsonb_fn)?;
let other = args[1..].chunks_exact(2);
for chunk in other {
let path = json_path_from_db_value(chunk[0].get_value(), true)?;
let value = convert_dbtype_to_jsonb(chunk[1].get_value(), Conv::NotStrict)?;
let mut json = json_cache.get_or_insert_with(args.next().unwrap(), make_jsonb_fn)?;
// TODO: when `array_chunks` is stabilized we can chunk by 2 here
while args.len() > 1 {
let first = args.next().unwrap();
let path = json_path_from_db_value(&first, true)?;
let second = args.next().unwrap();
let value = convert_dbtype_to_jsonb(&second, Conv::NotStrict)?;
if let Some(path) = path {
let mut op = InsertOperation::new(value);
@@ -154,17 +208,27 @@ pub fn json_insert(args: &[Register], json_cache: &JsonCacheCell) -> crate::Resu
json_string_to_db_type(json, el_type, OutputVariant::String)
}
pub fn jsonb_insert(args: &[Register], json_cache: &JsonCacheCell) -> crate::Result<Value> {
if args.is_empty() {
pub fn jsonb_insert<I, E, V>(args: I, json_cache: &JsonCacheCell) -> crate::Result<Value>
where
V: AsValueRef,
E: ExactSizeIterator<Item = V>,
I: IntoIterator<IntoIter = E, Item = V>,
{
let mut args = args.into_iter();
if args.len() == 0 {
return Ok(Value::Null);
}
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(args[0].get_value(), make_jsonb_fn)?;
let other = args[1..].chunks_exact(2);
for chunk in other {
let path = json_path_from_db_value(chunk[0].get_value(), true)?;
let value = convert_dbtype_to_jsonb(chunk[1].get_value(), Conv::NotStrict)?;
let mut json = json_cache.get_or_insert_with(args.next().unwrap(), make_jsonb_fn)?;
// TODO: when `array_chunks` is stabilized we can chunk by 2 here
while args.len() > 1 {
let first = args.next().unwrap();
let path = json_path_from_db_value(&first, true)?;
let second = args.next().unwrap();
let value = convert_dbtype_to_jsonb(&second, Conv::NotStrict)?;
if let Some(path) = path {
let mut op = InsertOperation::new(value);
@@ -282,17 +346,14 @@ mod tests {
#[test]
fn test_json_remove_empty_args() {
let args = vec![];
let args: [Value; 0] = [];
let json_cache = JsonCacheCell::new();
assert_eq!(json_remove(&args, &json_cache).unwrap(), Value::Null);
}
#[test]
fn test_json_remove_array_element() {
let args = vec![
Register::Value(create_json(r#"[1,2,3,4,5]"#)),
Register::Value(create_text("$[2]")),
];
let args = [create_json(r#"[1,2,3,4,5]"#), create_text("$[2]")];
let json_cache = JsonCacheCell::new();
let result = json_remove(&args, &json_cache).unwrap();
@@ -304,10 +365,10 @@ mod tests {
#[test]
fn test_json_remove_multiple_paths() {
let args = vec![
Register::Value(create_json(r#"{"a": 1, "b": 2, "c": 3}"#)),
Register::Value(create_text("$.a")),
Register::Value(create_text("$.c")),
let args = [
create_json(r#"{"a": 1, "b": 2, "c": 3}"#),
create_text("$.a"),
create_text("$.c"),
];
let json_cache = JsonCacheCell::new();
@@ -320,9 +381,9 @@ mod tests {
#[test]
fn test_json_remove_nested_paths() {
let args = vec![
Register::Value(create_json(r#"{"a": {"b": {"c": 1, "d": 2}}}"#)),
Register::Value(create_text("$.a.b.c")),
let args = [
create_json(r#"{"a": {"b": {"c": 1, "d": 2}}}"#),
create_text("$.a.b.c"),
];
let json_cache = JsonCacheCell::new();
@@ -335,9 +396,9 @@ mod tests {
#[test]
fn test_json_remove_duplicate_keys() {
let args = vec![
Register::Value(create_json(r#"{"a": 1, "a": 2, "a": 3}"#)),
Register::Value(create_text("$.a")),
let args = [
create_json(r#"{"a": 1, "a": 2, "a": 3}"#),
create_text("$.a"),
];
let json_cache = JsonCacheCell::new();
@@ -350,9 +411,9 @@ mod tests {
#[test]
fn test_json_remove_invalid_path() {
let args = vec![
Register::Value(create_json(r#"{"a": 1}"#)),
Register::Value(Value::Integer(42)), // Invalid path type
let args = [
create_json(r#"{"a": 1}"#),
Value::Integer(42), // Invalid path type
];
let json_cache = JsonCacheCell::new();
@@ -361,13 +422,11 @@ mod tests {
#[test]
fn test_json_remove_complex_case() {
let args = vec![
Register::Value(create_json(
r#"{"a":[1,2,3],"b":{"x":1,"x":2},"c":[{"y":1},{"y":2}]}"#,
)),
Register::Value(create_text("$.a[1]")),
Register::Value(create_text("$.b.x")),
Register::Value(create_text("$.c[0].y")),
let args = [
create_json(r#"{"a":[1,2,3],"b":{"x":1,"x":2},"c":[{"y":1},{"y":2}]}"#),
create_text("$.a[1]"),
create_text("$.b.x"),
create_text("$.c[0].y"),
];
let json_cache = JsonCacheCell::new();

View File

@@ -1566,6 +1566,13 @@ impl<'a> PartialEq<ValueRef<'a>> for ValueRef<'a> {
}
}
impl<'a> PartialEq<Value> for ValueRef<'a> {
fn eq(&self, other: &Value) -> bool {
let other = other.as_value_ref();
self.eq(&other)
}
}
impl<'a> Eq for ValueRef<'a> {}
#[expect(clippy::non_canonical_partial_ord_impl)]

View File

@@ -4442,7 +4442,8 @@ pub fn op_function(
| JsonFunc::JsonObject
| JsonFunc::JsonbArray
| JsonFunc::JsonbObject => {
let reg_values = &state.registers[*start_reg..*start_reg + arg_count];
let reg_values =
registers_to_ref_values(&state.registers[*start_reg..*start_reg + arg_count]);
let json_func = match json_func {
JsonFunc::JsonArray => json_array,
@@ -4463,7 +4464,9 @@ pub fn op_function(
0 => Ok(Value::Null),
_ => {
let val = &state.registers[*start_reg];
let reg_values = &state.registers[*start_reg + 1..*start_reg + arg_count];
let reg_values = registers_to_ref_values(
&state.registers[*start_reg + 1..*start_reg + arg_count],
);
json_extract(val.get_value(), reg_values, &state.json_cache)
}
@@ -4479,7 +4482,9 @@ pub fn op_function(
0 => Ok(Value::Null),
_ => {
let val = &state.registers[*start_reg];
let reg_values = &state.registers[*start_reg + 1..*start_reg + arg_count];
let reg_values = registers_to_ref_values(
&state.registers[*start_reg + 1..*start_reg + arg_count],
);
jsonb_extract(val.get_value(), reg_values, &state.json_cache)
}
@@ -4565,7 +4570,7 @@ pub fn op_function(
}
JsonFunc::JsonRemove => {
if let Ok(json) = json_remove(
&state.registers[*start_reg..*start_reg + arg_count],
registers_to_ref_values(&state.registers[*start_reg..*start_reg + arg_count]),
&state.json_cache,
) {
state.registers[*dest] = Register::Value(json);
@@ -4575,7 +4580,7 @@ pub fn op_function(
}
JsonFunc::JsonbRemove => {
if let Ok(json) = jsonb_remove(
&state.registers[*start_reg..*start_reg + arg_count],
registers_to_ref_values(&state.registers[*start_reg..*start_reg + arg_count]),
&state.json_cache,
) {
state.registers[*dest] = Register::Value(json);
@@ -4585,7 +4590,7 @@ pub fn op_function(
}
JsonFunc::JsonReplace => {
if let Ok(json) = json_replace(
&state.registers[*start_reg..*start_reg + arg_count],
registers_to_ref_values(&state.registers[*start_reg..*start_reg + arg_count]),
&state.json_cache,
) {
state.registers[*dest] = Register::Value(json);
@@ -4595,7 +4600,7 @@ pub fn op_function(
}
JsonFunc::JsonbReplace => {
if let Ok(json) = jsonb_replace(
&state.registers[*start_reg..*start_reg + arg_count],
registers_to_ref_values(&state.registers[*start_reg..*start_reg + arg_count]),
&state.json_cache,
) {
state.registers[*dest] = Register::Value(json);
@@ -4605,7 +4610,7 @@ pub fn op_function(
}
JsonFunc::JsonInsert => {
if let Ok(json) = json_insert(
&state.registers[*start_reg..*start_reg + arg_count],
registers_to_ref_values(&state.registers[*start_reg..*start_reg + arg_count]),
&state.json_cache,
) {
state.registers[*dest] = Register::Value(json);
@@ -4615,7 +4620,7 @@ pub fn op_function(
}
JsonFunc::JsonbInsert => {
if let Ok(json) = jsonb_insert(
&state.registers[*start_reg..*start_reg + arg_count],
registers_to_ref_values(&state.registers[*start_reg..*start_reg + arg_count]),
&state.json_cache,
) {
state.registers[*dest] = Register::Value(json);
@@ -4654,7 +4659,8 @@ pub fn op_function(
if arg_count % 2 == 0 {
bail_constraint_error!("json_set() needs an odd number of arguments")
}
let reg_values = &state.registers[*start_reg..*start_reg + arg_count];
let reg_values =
registers_to_ref_values(&state.registers[*start_reg..*start_reg + arg_count]);
let json_result = json_set(reg_values, &state.json_cache);
@@ -4667,7 +4673,8 @@ pub fn op_function(
if arg_count % 2 == 0 {
bail_constraint_error!("json_set() needs an odd number of arguments")
}
let reg_values = &state.registers[*start_reg..*start_reg + arg_count];
let reg_values =
registers_to_ref_values(&state.registers[*start_reg..*start_reg + arg_count]);
let json_result = jsonb_set(reg_values, &state.json_cache);