mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-30 05:14:23 +01:00
add json_replace, jsonb_replace
This commit is contained in:
@@ -89,6 +89,8 @@ pub enum JsonFunc {
|
||||
JsonbRemove,
|
||||
JsonReplace,
|
||||
JsonbReplace,
|
||||
JsonInsert,
|
||||
JsonbInsert,
|
||||
JsonPretty,
|
||||
JsonSet,
|
||||
JsonQuote,
|
||||
@@ -120,6 +122,8 @@ impl Display for JsonFunc {
|
||||
Self::JsonbRemove => "jsonb_remove".to_string(),
|
||||
Self::JsonReplace => "json_replace".to_string(),
|
||||
Self::JsonbReplace => "jsonb_replace".to_string(),
|
||||
Self::JsonInsert => "json_insert".to_string(),
|
||||
Self::JsonbInsert => "jsonb_insert".to_string(),
|
||||
Self::JsonPretty => "json_pretty".to_string(),
|
||||
Self::JsonSet => "json_set".to_string(),
|
||||
Self::JsonQuote => "json_quote".to_string(),
|
||||
@@ -593,6 +597,10 @@ impl Func {
|
||||
#[cfg(feature = "json")]
|
||||
"json_replace" => Ok(Self::Json(JsonFunc::JsonReplace)),
|
||||
#[cfg(feature = "json")]
|
||||
"json_insert" => Ok(Self::Json(JsonFunc::JsonInsert)),
|
||||
#[cfg(feature = "json")]
|
||||
"jsonb_insert" => Ok(Self::Json(JsonFunc::JsonbInsert)),
|
||||
#[cfg(feature = "json")]
|
||||
"jsonb_replace" => Ok(Self::Json(JsonFunc::JsonReplace)),
|
||||
#[cfg(feature = "json")]
|
||||
"json_pretty" => Ok(Self::Json(JsonFunc::JsonPretty)),
|
||||
|
||||
@@ -5,7 +5,7 @@ use crate::types::OwnedValue;
|
||||
use super::{
|
||||
convert_dbtype_to_jsonb, convert_json_to_db_type, get_json_value, json_path_from_owned_value,
|
||||
json_string_to_db_type,
|
||||
jsonb::{DeleteOperation, ReplaceOperation},
|
||||
jsonb::{DeleteOperation, InsertOperation, ReplaceOperation},
|
||||
Conv, OutputVariant, Val,
|
||||
};
|
||||
|
||||
@@ -228,9 +228,53 @@ pub fn jsonb_replace(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
|
||||
|
||||
let el_type = json.is_valid()?;
|
||||
|
||||
json_string_to_db_type(json, el_type, OutputVariant::AsBinary)
|
||||
}
|
||||
|
||||
pub fn json_insert(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
|
||||
if args.is_empty() {
|
||||
return Ok(OwnedValue::Null);
|
||||
}
|
||||
|
||||
let mut json = convert_dbtype_to_jsonb(&args[0], Conv::Strict)?;
|
||||
let other = args[1..].chunks_exact(2);
|
||||
for chunk in other {
|
||||
let path = json_path_from_owned_value(&chunk[0], true)?;
|
||||
let value = convert_dbtype_to_jsonb(&chunk[1], Conv::NotStrict)?;
|
||||
if let Some(path) = path {
|
||||
let mut op = InsertOperation::new(value);
|
||||
|
||||
let _ = json.operate_on_path(&path, &mut op);
|
||||
}
|
||||
}
|
||||
|
||||
let el_type = json.is_valid()?;
|
||||
|
||||
json_string_to_db_type(json, el_type, OutputVariant::AsString)
|
||||
}
|
||||
|
||||
pub fn jsonb_insert(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
|
||||
if args.is_empty() {
|
||||
return Ok(OwnedValue::Null);
|
||||
}
|
||||
|
||||
let mut json = convert_dbtype_to_jsonb(&args[0], Conv::Strict)?;
|
||||
let other = args[1..].chunks_exact(2);
|
||||
for chunk in other {
|
||||
let path = json_path_from_owned_value(&chunk[0], true)?;
|
||||
let value = convert_dbtype_to_jsonb(&chunk[1], Conv::NotStrict)?;
|
||||
if let Some(path) = path {
|
||||
let mut op = InsertOperation::new(value);
|
||||
|
||||
let _ = json.operate_on_path(&path, &mut op);
|
||||
}
|
||||
}
|
||||
|
||||
let el_type = json.is_valid()?;
|
||||
|
||||
json_string_to_db_type(json, el_type, OutputVariant::AsBinary)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::rc::Rc;
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::bail_constraint_error;
|
||||
pub use crate::json::de::from_str;
|
||||
use crate::json::error::Error as JsonError;
|
||||
pub use crate::json::json_operations::{
|
||||
json_patch, json_remove, json_replace, jsonb_remove, jsonb_replace,
|
||||
json_insert, json_patch, json_remove, json_replace, jsonb_insert, jsonb_remove, jsonb_replace,
|
||||
};
|
||||
use crate::json::json_path::{json_path, JsonPath, PathElement};
|
||||
pub use crate::json::ser::to_string;
|
||||
@@ -525,98 +525,6 @@ fn json_path_from_owned_value(path: &OwnedValue, strict: bool) -> crate::Result<
|
||||
Ok(Some(json_path))
|
||||
}
|
||||
|
||||
enum Target<'a> {
|
||||
Array(&'a mut Vec<Val>, usize),
|
||||
Value(&'a mut Val),
|
||||
}
|
||||
|
||||
fn create_and_mutate_json_by_path<F, R>(json: &mut Val, path: JsonPath, closure: F) -> Option<R>
|
||||
where
|
||||
F: FnOnce(Target) -> R,
|
||||
{
|
||||
find_or_create_target(json, &path).map(closure)
|
||||
}
|
||||
|
||||
fn find_or_create_target<'a>(json: &'a mut Val, path: &JsonPath) -> Option<Target<'a>> {
|
||||
let mut current = json;
|
||||
for (i, key) in path.elements.iter().enumerate() {
|
||||
let is_last = i == path.elements.len() - 1;
|
||||
match key {
|
||||
PathElement::Root() => continue,
|
||||
PathElement::ArrayLocator(index) => match current {
|
||||
Val::Array(arr) => {
|
||||
if let Some(index) = match index {
|
||||
Some(i) if *i < 0 => arr.len().checked_sub(i.unsigned_abs() as usize),
|
||||
Some(i) => Some(*i as usize),
|
||||
None => Some(arr.len()),
|
||||
} {
|
||||
if is_last {
|
||||
if index == arr.len() {
|
||||
arr.push(Val::Null);
|
||||
}
|
||||
|
||||
if index >= arr.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
return Some(Target::Array(arr, index));
|
||||
} else {
|
||||
if index == arr.len() {
|
||||
arr.push(
|
||||
if matches!(path.elements[i + 1], PathElement::ArrayLocator(_))
|
||||
{
|
||||
Val::Array(vec![])
|
||||
} else {
|
||||
Val::Object(vec![])
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
if index >= arr.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
current = &mut arr[index];
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
*current = Val::Array(vec![]);
|
||||
}
|
||||
},
|
||||
PathElement::Key(key, _) => match current {
|
||||
Val::Object(obj) => {
|
||||
if let Some(pos) = &obj
|
||||
.iter()
|
||||
.position(|(k, v)| k == key && !matches!(v, Val::Removed))
|
||||
{
|
||||
let val = &mut obj[*pos].1;
|
||||
current = val;
|
||||
} else {
|
||||
let element = if !is_last
|
||||
&& matches!(path.elements[i + 1], PathElement::ArrayLocator(_))
|
||||
{
|
||||
Val::Array(vec![])
|
||||
} else {
|
||||
Val::Object(vec![])
|
||||
};
|
||||
|
||||
obj.push((key.to_string(), element));
|
||||
let index = obj.len() - 1;
|
||||
current = &mut obj[index].1;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Some(Target::Value(current))
|
||||
}
|
||||
|
||||
pub fn json_error_position(json: &OwnedValue) -> crate::Result<OwnedValue> {
|
||||
match json {
|
||||
OwnedValue::Text(t) => match from_str::<Val>(t.as_str()) {
|
||||
|
||||
@@ -901,7 +901,9 @@ pub fn translate_expr(
|
||||
| JsonFunc::JsonbExtract
|
||||
| JsonFunc::JsonReplace
|
||||
| JsonFunc::JsonbReplace
|
||||
| JsonFunc::JsonbRemove => translate_function(
|
||||
| JsonFunc::JsonbRemove
|
||||
| JsonFunc::JsonInsert
|
||||
| JsonFunc::JsonbInsert => translate_function(
|
||||
program,
|
||||
args.as_deref().unwrap_or_default(),
|
||||
referenced_tables,
|
||||
|
||||
@@ -52,10 +52,10 @@ use crate::{bail_constraint_error, info, CheckpointStatus};
|
||||
use crate::{
|
||||
function::JsonFunc, json::get_json, json::is_json_valid, json::json_array,
|
||||
json::json_array_length, json::json_arrow_extract, json::json_arrow_shift_extract,
|
||||
json::json_error_position, json::json_extract, json::json_object, json::json_patch,
|
||||
json::json_quote, json::json_remove, json::json_replace, json::json_set, json::json_type,
|
||||
json::jsonb, json::jsonb_array, json::jsonb_extract, json::jsonb_object, json::jsonb_remove,
|
||||
json::jsonb_replace,
|
||||
json::json_error_position, json::json_extract, json::json_insert, json::json_object,
|
||||
json::json_patch, json::json_quote, json::json_remove, json::json_replace, json::json_set,
|
||||
json::json_type, json::jsonb, json::jsonb_array, json::jsonb_extract, json::jsonb_insert,
|
||||
json::jsonb_object, json::jsonb_remove, json::jsonb_replace,
|
||||
};
|
||||
use crate::{
|
||||
resolve_ext_path, Connection, MvCursor, MvStore, Result, TransactionState, DATABASE_VERSION,
|
||||
@@ -2288,6 +2288,24 @@ impl Program {
|
||||
state.registers[*dest] = OwnedValue::Null;
|
||||
}
|
||||
}
|
||||
JsonFunc::JsonInsert => {
|
||||
if let Ok(json) = json_insert(
|
||||
&state.registers[*start_reg..*start_reg + arg_count],
|
||||
) {
|
||||
state.registers[*dest] = json;
|
||||
} else {
|
||||
state.registers[*dest] = OwnedValue::Null;
|
||||
}
|
||||
}
|
||||
JsonFunc::JsonbInsert => {
|
||||
if let Ok(json) = jsonb_insert(
|
||||
&state.registers[*start_reg..*start_reg + arg_count],
|
||||
) {
|
||||
state.registers[*dest] = json;
|
||||
} else {
|
||||
state.registers[*dest] = OwnedValue::Null;
|
||||
}
|
||||
}
|
||||
JsonFunc::JsonPretty => {
|
||||
let json_value = &state.registers[*start_reg];
|
||||
let indent = if arg_count > 1 {
|
||||
|
||||
Reference in New Issue
Block a user