Merge 'JSON cache' from Ihor Andrianov

SQLite uses a similar approach for operations where up to 4 JSON objects
are accessed multiple times in a single query.
`SELECT json_extact(a, 'some_path'), json_remove(a, 'some_path')
json_set(a, 'some_path', 'some_value') from t;`

Closes #1163
This commit is contained in:
Pekka Enberg
2025-03-25 18:11:33 +02:00
7 changed files with 859 additions and 132 deletions

View File

@@ -485,9 +485,167 @@ fn bench(criterion: &mut Criterion) {
}
}
fn bench_sequential_jsonb(criterion: &mut Criterion) {
// Flag to disable rusqlite benchmarks if needed
let enable_rusqlite = std::env::var("DISABLE_RUSQLITE_BENCHMARK").is_err();
#[allow(clippy::arc_with_non_send_sync)]
let io = Arc::new(PlatformIO::new().unwrap());
let db = Database::open_file(io.clone(), "../testing/testing.db", false).unwrap();
let limbo_conn = db.connect().unwrap();
// Select a subset of JSON payloads to use in the sequential test
let json_payloads = [
("Small", r#"{"id": 1, "name": "Test"}"#),
(
"Medium",
r#"{"id": 1, "name": "Test", "attributes": {"color": "blue", "size": "medium", "tags": ["tag1", "tag2", "tag3"]}}"#,
),
(
"Real world json #1",
r#"{
"user": {
"id": "usr_7f8d3a2e",
"name": "Jane Smith",
"email": "jane.smith@example.com",
"verified": true,
"created_at": "2023-05-12T15:42:31Z",
"preferences": {
"theme": "dark",
"notifications": {
"email": true,
"push": false,
"sms": true
},
"language": "en-US"
}
}
}"#,
),
(
"Real world json #2",
r#"{
"feed": {
"user_id": "u_12345",
"posts": [
{
"id": "post_001",
"author": "user_789",
"content": "Just launched our new product! Check it out at example.com/new",
"timestamp": "2024-03-13T14:27:32Z",
"likes": 24,
"comments": [
{
"id": "comment_001",
"author": "user_456",
"content": "Looks amazing! Cant wait to try it.",
"timestamp": "2024-03-13T14:35:12Z",
"likes": 3
},
{
"id": "comment_002",
"author": "user_789",
"content": "Thanks! Let me know what you think after youve tried it.",
"timestamp": "2024-03-13T14:42:45Z",
"likes": 1
}
],
"tags": ["product", "launch", "technology"]
},
{
"id": "post_002",
"author": "user_123",
"content": "Beautiful day for hiking! #nature #outdoors",
"timestamp": "2024-03-13T11:15:22Z",
"likes": 57,
"comments": [
{
"id": "comment_003",
"author": "user_345",
"content": "Where is this? So beautiful!",
"timestamp": "2024-03-13T11:22:05Z",
"likes": 2
},
{
"id": "comment_004",
"author": "user_123",
"content": "Mount Rainier National Park!",
"timestamp": "2024-03-13T11:30:16Z",
"likes": 3
}
],
"tags": ["nature", "outdoors", "hiking"],
"location": {
"name": "Mount Rainier National Park",
"latitude": 46.8800,
"longitude": -121.7269
}
}
],
"has_more": true,
"next_cursor": "cursor_xyz123"
}
}"#,
),
];
// Create a query that calls jsonb() multiple times in sequence
let query = format!(
"SELECT jsonb('{}'), jsonb('{}'), jsonb('{}'), jsonb('{}'), jsonb('{}'), jsonb('{}'), jsonb('{}'), jsonb('{}')",
json_payloads[0].1.replace("'", "\\'"),
json_payloads[1].1.replace("'", "\\'"),
json_payloads[2].1.replace("'", "\\'"),
json_payloads[3].1.replace("'", "\\'"),
json_payloads[0].1.replace("'", "\\'"),
json_payloads[1].1.replace("'", "\\'"),
json_payloads[2].1.replace("'", "\\'"),
json_payloads[3].1.replace("'", "\\'"),
);
let mut group = criterion.benchmark_group("Sequential JSONB Calls");
group.bench_function("Limbo - Sequential", |b| {
let mut stmt = limbo_conn.prepare(&query).unwrap();
let io = io.clone();
b.iter(|| {
loop {
match stmt.step().unwrap() {
limbo_core::StepResult::Row => {}
limbo_core::StepResult::IO => {
let _ = io.run_once();
}
limbo_core::StepResult::Done => {
break;
}
limbo_core::StepResult::Interrupt | limbo_core::StepResult::Busy => {
unreachable!();
}
}
}
stmt.reset();
});
});
if enable_rusqlite {
let sqlite_conn = rusqlite_open();
group.bench_function("Sqlite3 - Sequential", |b| {
let mut stmt = sqlite_conn.prepare(&query).unwrap();
b.iter(|| {
let mut rows = stmt.raw_query();
while let Some(row) = rows.next().unwrap() {
black_box(row);
}
});
});
}
group.finish();
}
criterion_group! {
name = benches;
config = Criterion::default().with_profiler(PProfProfiler::new(100, Output::Flamegraph(Some(Options::default()))));
targets = bench
targets = bench, bench_sequential_jsonb
}
criterion_main!(benches);

435
core/json/json_cache.rs Normal file
View File

@@ -0,0 +1,435 @@
use std::cell::{Cell, UnsafeCell};
use crate::OwnedValue;
use super::jsonb::Jsonb;
const JSON_CACHE_SIZE: usize = 4;
#[derive(Debug)]
pub struct JsonCache {
entries: [Option<(OwnedValue, Jsonb)>; JSON_CACHE_SIZE],
age: [usize; JSON_CACHE_SIZE],
used: usize,
counter: usize,
}
impl JsonCache {
pub fn new() -> Self {
Self {
entries: [None, None, None, None],
age: [0, 0, 0, 0],
used: 0,
counter: 0,
}
}
fn find_oldest_entry(&self) -> usize {
let mut oldest_idx = 0;
let mut oldest_age = self.age[0];
for i in 1..self.used {
if self.age[i] < oldest_age {
oldest_idx = i;
oldest_age = self.age[i];
}
}
oldest_idx
}
pub fn insert(&mut self, key: &OwnedValue, value: &Jsonb) {
if self.used < JSON_CACHE_SIZE {
self.entries[self.used] = Some((key.clone(), 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.age[id] = self.counter;
self.counter += 1;
}
}
pub fn lookup(&mut self, key: &OwnedValue) -> Option<Jsonb> {
for i in (0..self.used).rev() {
if let Some((stored_key, value)) = &self.entries[i] {
if key == stored_key {
self.age[i] = self.counter;
self.counter += 1;
let json = value.clone();
return Some(json);
}
}
}
None
}
pub fn clear(&mut self) {
self.counter = 0;
self.used = 0;
}
}
#[derive(Debug)]
pub struct JsonCacheCell {
inner: UnsafeCell<Option<JsonCache>>,
accessed: Cell<bool>,
}
impl JsonCacheCell {
pub fn new() -> Self {
Self {
inner: UnsafeCell::new(None),
accessed: Cell::new(false),
}
}
#[cfg(test)]
pub fn lookup(&self, key: &OwnedValue) -> Option<Jsonb> {
assert_eq!(self.accessed.get(), false);
self.accessed.set(true);
let result = unsafe {
let cache_ptr = self.inner.get();
if (*cache_ptr).is_none() {
*cache_ptr = Some(JsonCache::new());
}
if let Some(cache) = &mut (*cache_ptr) {
cache.lookup(key)
} else {
None
}
};
self.accessed.set(false);
result
}
pub fn get_or_insert_with(
&self,
key: &OwnedValue,
value: impl Fn(&OwnedValue) -> crate::Result<Jsonb>,
) -> crate::Result<Jsonb> {
assert_eq!(self.accessed.get(), false);
self.accessed.set(true);
let result = unsafe {
let cache_ptr = self.inner.get();
if (*cache_ptr).is_none() {
*cache_ptr = Some(JsonCache::new());
}
if let Some(cache) = &mut (*cache_ptr) {
if let Some(jsonb) = cache.lookup(key) {
Ok(jsonb)
} else {
let result = value(key);
match result {
Ok(json) => {
cache.insert(key, &json);
Ok(json)
}
Err(e) => Err(e),
}
}
} else {
let result = value(key);
result
}
};
self.accessed.set(false);
result
}
pub fn clear(&mut self) {
assert_eq!(self.accessed.get(), false);
self.accessed.set(true);
unsafe {
let cache_ptr = self.inner.get();
if (*cache_ptr).is_none() {
self.accessed.set(false);
return;
}
if let Some(cache) = &mut (*cache_ptr) {
cache.clear()
}
}
self.accessed.set(false);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
// Helper function to create test OwnedValue and Jsonb from JSON string
fn create_test_pair(json_str: &str) -> (OwnedValue, Jsonb) {
// Create OwnedValue as text representation of JSON
let key = OwnedValue::build_text(json_str);
// Create Jsonb from the same JSON string
let value = Jsonb::from_str(json_str).unwrap();
(key, value)
}
#[test]
fn test_json_cache_new() {
let cache = JsonCache::new();
assert_eq!(cache.used, 0);
assert_eq!(cache.counter, 0);
assert_eq!(cache.age, [0, 0, 0, 0]);
assert!(cache.entries.iter().all(|entry| entry.is_none()));
}
#[test]
fn test_json_cache_insert_and_lookup() {
let mut cache = JsonCache::new();
let json_str = "{\"test\": \"value\"}";
let (key, value) = create_test_pair(json_str);
// Insert a value
cache.insert(&key, &value);
// Verify it was inserted
assert_eq!(cache.used, 1);
assert_eq!(cache.counter, 1);
// Look it up
let result = cache.lookup(&key);
assert!(result.is_some());
assert_eq!(result.unwrap(), value);
// Counter should be incremented after lookup
assert_eq!(cache.counter, 2);
}
#[test]
fn test_json_cache_lookup_nonexistent() {
let mut cache = JsonCache::new();
let (key, _) = create_test_pair("{\"id\": 123}");
// Look up a non-existent key
let result = cache.lookup(&key);
assert!(result.is_none());
// Counter should remain unchanged
assert_eq!(cache.counter, 0);
}
#[test]
fn test_json_cache_multiple_entries() {
let mut cache = JsonCache::new();
// Insert multiple entries
let (key1, value1) = create_test_pair("{\"id\": 1}");
let (key2, value2) = create_test_pair("{\"id\": 2}");
let (key3, value3) = create_test_pair("{\"id\": 3}");
cache.insert(&key1, &value1);
cache.insert(&key2, &value2);
cache.insert(&key3, &value3);
// Verify they were all inserted
assert_eq!(cache.used, 3);
assert_eq!(cache.counter, 3);
// Look them up in reverse order
let result3 = cache.lookup(&key3);
let result2 = cache.lookup(&key2);
let result1 = cache.lookup(&key1);
assert_eq!(result3.unwrap(), value3);
assert_eq!(result2.unwrap(), value2);
assert_eq!(result1.unwrap(), value1);
// Counter should be incremented for each lookup
assert_eq!(cache.counter, 6);
}
#[test]
fn test_json_cache_eviction() {
let mut cache = JsonCache::new();
// Insert more than JSON_CACHE_SIZE entries
let (key1, value1) = create_test_pair("{\"id\": 1}");
let (key2, value2) = create_test_pair("{\"id\": 2}");
let (key3, value3) = create_test_pair("{\"id\": 3}");
let (key4, value4) = create_test_pair("{\"id\": 4}");
let (key5, value5) = create_test_pair("{\"id\": 5}");
cache.insert(&key1, &value1);
cache.insert(&key2, &value2);
cache.insert(&key3, &value3);
cache.insert(&key4, &value4);
// Cache is now full
assert_eq!(cache.used, 4);
// Look up key1 to make it the most recently used
let _ = cache.lookup(&key1);
// Insert one more entry - should evict the oldest (key2)
cache.insert(&key5, &value5);
// Cache size should still be JSON_CACHE_SIZE
assert_eq!(cache.used, 4);
// key2 should have been evicted
let result2 = cache.lookup(&key2);
assert!(result2.is_none());
// Other entries should still be present
assert!(cache.lookup(&key1).is_some());
assert!(cache.lookup(&key3).is_some());
assert!(cache.lookup(&key4).is_some());
assert!(cache.lookup(&key5).is_some());
}
#[test]
fn test_json_cache_find_oldest_entry() {
let mut cache = JsonCache::new();
// Insert entries
let (key1, value1) = create_test_pair("{\"id\": 1}");
let (key2, value2) = create_test_pair("{\"id\": 2}");
let (key3, value3) = create_test_pair("{\"id\": 3}");
cache.insert(&key1, &value1);
cache.insert(&key2, &value2);
cache.insert(&key3, &value3);
// key1 should be the oldest
assert_eq!(cache.find_oldest_entry(), 0);
// Access key1 to make it the newest
let _ = cache.lookup(&key1);
// Now key2 should be the oldest
assert_eq!(cache.find_oldest_entry(), 1);
}
// Tests for JsonCacheCell
#[test]
fn test_json_cache_cell_new() {
let cache_cell = JsonCacheCell::new();
// Access flag should be false initially
assert_eq!(cache_cell.accessed.get(), false);
// Inner cache should be None initially
unsafe {
let inner = &*cache_cell.inner.get();
assert!(inner.is_none());
}
}
#[test]
fn test_json_cache_cell_lookup() {
let cache_cell = JsonCacheCell::new();
let (key, value) = create_test_pair("{\"test\": \"value\"}");
// First lookup should return None since cache is empty
let result = cache_cell.lookup(&key);
assert!(result.is_none());
// Cache should be initialized after first lookup
unsafe {
let inner = &*cache_cell.inner.get();
assert!(inner.is_some());
}
// Access flag should be reset to false
assert_eq!(cache_cell.accessed.get(), false);
// 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);
Ok(value.clone())
});
assert!(insert_result.is_ok());
assert_eq!(insert_result.unwrap(), value);
// Access flag should be reset to false
assert_eq!(cache_cell.accessed.get(), false);
// Lookup should now return the value
let lookup_result = cache_cell.lookup(&key);
assert!(lookup_result.is_some());
assert_eq!(lookup_result.unwrap(), value);
}
#[test]
fn test_json_cache_cell_get_or_insert_with_existing() {
let cache_cell = JsonCacheCell::new();
let (key, value) = create_test_pair("{\"test\": \"value\"}");
// Insert a value
let _ = cache_cell.get_or_insert_with(&key, |_| Ok(value.clone()));
// Counter indicating if the closure was called
let closure_called = Cell::new(false);
// Try to insert again with the same key
let result = cache_cell.get_or_insert_with(&key, |_| {
closure_called.set(true);
Ok(Jsonb::from_str("{\"test\": \"value\"}").unwrap())
});
// The closure should not have been called
assert!(!closure_called.get());
// Should return the original value
assert_eq!(result.unwrap(), value);
}
#[test]
#[should_panic]
fn test_json_cache_cell_double_access() {
let cache_cell = JsonCacheCell::new();
let (key, _) = create_test_pair("{\"test\": \"value\"}");
// Access the cache
// Set accessed flag to true manually
cache_cell.accessed.set(true);
// This should panic due to double access
let _ = cache_cell.lookup(&key);
}
#[test]
fn test_json_cache_cell_get_or_insert_error_handling() {
let cache_cell = JsonCacheCell::new();
let (key, _) = create_test_pair("{\"test\": \"value\"}");
// Test error handling
let error_result = cache_cell.get_or_insert_with(&key, |_| {
// Return an error
Err(crate::LimboError::Constraint("Test error".to_string()))
});
// Should propagate the error
assert!(error_result.is_err());
// Access flag should be reset to false
assert_eq!(cache_cell.accessed.get(), false);
// The entry should not be cached
let lookup_result = cache_cell.lookup(&key);
assert!(lookup_result.is_none());
}
}

View File

@@ -3,10 +3,10 @@ use std::{collections::VecDeque, rc::Rc};
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,
convert_dbtype_to_jsonb, convert_json_to_db_type, curry_convert_dbtype_to_jsonb,
get_json_value, json_path_from_owned_value, json_string_to_db_type,
jsonb::{DeleteOperation, InsertOperation, ReplaceOperation},
Conv, OutputVariant, Val,
Conv, JsonCacheCell, OutputVariant, Val,
};
/// Represents a single patch operation in the merge queue.
@@ -152,12 +152,13 @@ impl JsonPatcher {
}
}
pub fn json_remove(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
pub fn json_remove(args: &[OwnedValue], json_cache: &JsonCacheCell) -> crate::Result<OwnedValue> {
if args.is_empty() {
return Ok(OwnedValue::Null);
}
let mut json = convert_dbtype_to_jsonb(&args[0], Conv::Strict)?;
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(&args[0], make_jsonb_fn)?;
for arg in &args[1..] {
if let Some(path) = json_path_from_owned_value(arg, true)? {
let mut op = DeleteOperation::new();
@@ -170,12 +171,13 @@ pub fn json_remove(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
json_string_to_db_type(json, el_type, OutputVariant::String)
}
pub fn jsonb_remove(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
pub fn jsonb_remove(args: &[OwnedValue], json_cache: &JsonCacheCell) -> crate::Result<OwnedValue> {
if args.is_empty() {
return Ok(OwnedValue::Null);
}
let mut json = convert_dbtype_to_jsonb(&args[0], Conv::Strict)?;
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(&args[0], make_jsonb_fn)?;
for arg in &args[1..] {
if let Some(path) = json_path_from_owned_value(arg, true)? {
let mut op = DeleteOperation::new();
@@ -186,12 +188,13 @@ pub fn jsonb_remove(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
Ok(OwnedValue::Blob(Rc::new(json.data())))
}
pub fn json_replace(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
pub fn json_replace(args: &[OwnedValue], json_cache: &JsonCacheCell) -> crate::Result<OwnedValue> {
if args.is_empty() {
return Ok(OwnedValue::Null);
}
let mut json = convert_dbtype_to_jsonb(&args[0], Conv::Strict)?;
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(&args[0], make_jsonb_fn)?;
let other = args[1..].chunks_exact(2);
for chunk in other {
let path = json_path_from_owned_value(&chunk[0], true)?;
@@ -209,12 +212,13 @@ pub fn json_replace(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
json_string_to_db_type(json, el_type, super::OutputVariant::String)
}
pub fn jsonb_replace(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
pub fn jsonb_replace(args: &[OwnedValue], json_cache: &JsonCacheCell) -> crate::Result<OwnedValue> {
if args.is_empty() {
return Ok(OwnedValue::Null);
}
let mut json = convert_dbtype_to_jsonb(&args[0], Conv::Strict)?;
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(&args[0], make_jsonb_fn)?;
let other = args[1..].chunks_exact(2);
for chunk in other {
let path = json_path_from_owned_value(&chunk[0], true)?;
@@ -231,12 +235,13 @@ pub fn jsonb_replace(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
json_string_to_db_type(json, el_type, OutputVariant::Binary)
}
pub fn json_insert(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
pub fn json_insert(args: &[OwnedValue], json_cache: &JsonCacheCell) -> crate::Result<OwnedValue> {
if args.is_empty() {
return Ok(OwnedValue::Null);
}
let mut json = convert_dbtype_to_jsonb(&args[0], Conv::Strict)?;
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(&args[0], make_jsonb_fn)?;
let other = args[1..].chunks_exact(2);
for chunk in other {
let path = json_path_from_owned_value(&chunk[0], true)?;
@@ -253,12 +258,13 @@ pub fn json_insert(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
json_string_to_db_type(json, el_type, OutputVariant::String)
}
pub fn jsonb_insert(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
pub fn jsonb_insert(args: &[OwnedValue], json_cache: &JsonCacheCell) -> crate::Result<OwnedValue> {
if args.is_empty() {
return Ok(OwnedValue::Null);
}
let mut json = convert_dbtype_to_jsonb(&args[0], Conv::Strict)?;
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(&args[0], make_jsonb_fn)?;
let other = args[1..].chunks_exact(2);
for chunk in other {
let path = json_path_from_owned_value(&chunk[0], true)?;
@@ -596,14 +602,16 @@ mod tests {
#[test]
fn test_json_remove_empty_args() {
let args = vec![];
assert_eq!(json_remove(&args).unwrap(), OwnedValue::Null);
let json_cache = JsonCacheCell::new();
assert_eq!(json_remove(&args, &json_cache).unwrap(), OwnedValue::Null);
}
#[test]
fn test_json_remove_array_element() {
let args = vec![create_json(r#"[1,2,3,4,5]"#), create_text("$[2]")];
let result = json_remove(&args).unwrap();
let json_cache = JsonCacheCell::new();
let result = json_remove(&args, &json_cache).unwrap();
match result {
OwnedValue::Text(t) => assert_eq!(t.as_str(), "[1,2,4,5]"),
_ => panic!("Expected Text value"),
@@ -618,7 +626,8 @@ mod tests {
create_text("$.c"),
];
let result = json_remove(&args).unwrap();
let json_cache = JsonCacheCell::new();
let result = json_remove(&args, &json_cache).unwrap();
match result {
OwnedValue::Text(t) => assert_eq!(t.as_str(), r#"{"b":2}"#),
_ => panic!("Expected Text value"),
@@ -632,7 +641,8 @@ mod tests {
create_text("$.a.b.c"),
];
let result = json_remove(&args).unwrap();
let json_cache = JsonCacheCell::new();
let result = json_remove(&args, &json_cache).unwrap();
match result {
OwnedValue::Text(t) => assert_eq!(t.as_str(), r#"{"a":{"b":{"d":2}}}"#),
_ => panic!("Expected Text value"),
@@ -646,7 +656,8 @@ mod tests {
create_text("$.a"),
];
let result = json_remove(&args).unwrap();
let json_cache = JsonCacheCell::new();
let result = json_remove(&args, &json_cache).unwrap();
match result {
OwnedValue::Text(t) => assert_eq!(t.as_str(), r#"{"a":2,"a":3}"#),
_ => panic!("Expected Text value"),
@@ -660,7 +671,8 @@ mod tests {
OwnedValue::Integer(42), // Invalid path type
];
assert!(json_remove(&args).is_err());
let json_cache = JsonCacheCell::new();
assert!(json_remove(&args, &json_cache).is_err());
}
#[test]
@@ -672,7 +684,8 @@ mod tests {
create_text("$.c[0].y"),
];
let result = json_remove(&args).unwrap();
let json_cache = JsonCacheCell::new();
let result = json_remove(&args, &json_cache).unwrap();
match result {
OwnedValue::Text(t) => {
let value = t.as_str();

View File

@@ -171,7 +171,7 @@ const fn make_character_type_ok_table() -> [u8; 256] {
static CHARACTER_TYPE_OK: [u8; 256] = make_character_type_ok_table();
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub struct Jsonb {
data: Vec<u8>,
}

View File

@@ -1,5 +1,6 @@
mod de;
mod error;
mod json_cache;
mod json_operations;
mod json_path;
mod jsonb;
@@ -15,6 +16,7 @@ use crate::json::json_path::{json_path, JsonPath, PathElement};
pub use crate::json::ser::to_string;
use crate::types::{OwnedValue, OwnedValueType, Text, TextSubtype};
use crate::{bail_parse_error, json::de::ordered_object};
pub use json_cache::JsonCacheCell;
use jsonb::{ElementType, Jsonb, JsonbHeader, PathOperationMode, SearchOperation, SetOperation};
use ser::to_string_pretty;
use serde::{Deserialize, Serialize};
@@ -36,7 +38,8 @@ pub enum Val {
Object(Vec<(String, Val)>),
}
enum Conv {
#[derive(Debug, Clone, Copy)]
pub enum Conv {
Strict,
NotStrict,
ToString,
@@ -86,8 +89,10 @@ pub fn get_json(json_value: &OwnedValue, indent: Option<&str>) -> crate::Result<
}
}
pub fn jsonb(json_value: &OwnedValue) -> crate::Result<OwnedValue> {
let jsonbin = convert_dbtype_to_jsonb(json_value, Conv::Strict);
pub fn jsonb(json_value: &OwnedValue, cache: &JsonCacheCell) -> crate::Result<OwnedValue> {
let json_conv_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let jsonbin = cache.get_or_insert_with(json_value, json_conv_fn);
match jsonbin {
Ok(jsonbin) => Ok(OwnedValue::Blob(Rc::new(jsonbin.data()))),
Err(_) => {
@@ -140,6 +145,10 @@ fn convert_dbtype_to_jsonb(val: &OwnedValue, strict: Conv) -> crate::Result<Json
}
}
pub fn curry_convert_dbtype_to_jsonb(strict: Conv) -> impl Fn(&OwnedValue) -> crate::Result<Jsonb> {
move |val| convert_dbtype_to_jsonb(val, strict)
}
fn get_json_value(json_value: &OwnedValue) -> crate::Result<Val> {
match json_value {
OwnedValue::Text(ref t) => match from_str::<Val>(t.as_str()) {
@@ -189,17 +198,19 @@ pub fn jsonb_array(values: &[OwnedValue]) -> crate::Result<OwnedValue> {
}
pub fn json_array_length(
json_value: &OwnedValue,
json_path: Option<&OwnedValue>,
value: &OwnedValue,
path: Option<&OwnedValue>,
json_cache: &JsonCacheCell,
) -> crate::Result<OwnedValue> {
let mut json = convert_dbtype_to_jsonb(json_value, Conv::Strict)?;
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(value, make_jsonb_fn)?;
if json_path.is_none() {
if path.is_none() {
let len = json.array_len()?;
return Ok(OwnedValue::Integer(len as i64));
}
let path = json_path_from_owned_value(json_path.expect("We already checked none"), true)?;
let path = json_path_from_owned_value(path.expect("We already checked none"), true)?;
if let Some(path) = path {
let mut op = SearchOperation::new(json.len() / 2);
@@ -211,12 +222,13 @@ pub fn json_array_length(
Ok(OwnedValue::Null)
}
pub fn json_set(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
pub fn json_set(args: &[OwnedValue], json_cache: &JsonCacheCell) -> crate::Result<OwnedValue> {
if args.is_empty() {
return Ok(OwnedValue::Null);
}
let mut json = convert_dbtype_to_jsonb(&args[0], Conv::Strict)?;
let make_jsonb_fn = curry_convert_dbtype_to_jsonb(Conv::Strict);
let mut json = json_cache.get_or_insert_with(&args[0], make_jsonb_fn)?;
let other = args[1..].chunks_exact(2);
for chunk in other {
@@ -236,13 +248,18 @@ pub fn json_set(args: &[OwnedValue]) -> crate::Result<OwnedValue> {
/// Implements the -> operator. Always returns a proper JSON value.
/// https://sqlite.org/json1.html#the_and_operators
pub fn json_arrow_extract(value: &OwnedValue, path: &OwnedValue) -> crate::Result<OwnedValue> {
pub fn json_arrow_extract(
value: &OwnedValue,
path: &OwnedValue,
json_cache: &JsonCacheCell,
) -> crate::Result<OwnedValue> {
if let OwnedValue::Null = value {
return Ok(OwnedValue::Null);
}
if let Some(path) = json_path_from_owned_value(path, false)? {
let mut json = convert_dbtype_to_jsonb(value, Conv::Strict)?;
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());
let res = json.operate_on_path(&path, &mut op);
let extracted = op.result();
@@ -261,12 +278,14 @@ pub fn json_arrow_extract(value: &OwnedValue, path: &OwnedValue) -> crate::Resul
pub fn json_arrow_shift_extract(
value: &OwnedValue,
path: &OwnedValue,
json_cache: &JsonCacheCell,
) -> crate::Result<OwnedValue> {
if let OwnedValue::Null = value {
return Ok(OwnedValue::Null);
}
if let Some(path) = json_path_from_owned_value(path, false)? {
let mut json = convert_dbtype_to_jsonb(value, Conv::Strict)?;
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());
let res = json.operate_on_path(&path, &mut op);
let extracted = op.result();
@@ -292,7 +311,11 @@ 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: &OwnedValue, paths: &[OwnedValue]) -> crate::Result<OwnedValue> {
pub fn json_extract(
value: &OwnedValue,
paths: &[OwnedValue],
json_cache: &JsonCacheCell,
) -> crate::Result<OwnedValue> {
if let OwnedValue::Null = value {
return Ok(OwnedValue::Null);
}
@@ -300,14 +323,20 @@ pub fn json_extract(value: &OwnedValue, paths: &[OwnedValue]) -> crate::Result<O
if paths.is_empty() {
return Ok(OwnedValue::Null);
}
let (json, element_type) = jsonb_extract_internal(value, paths)?;
let convert_to_jsonb = curry_convert_dbtype_to_jsonb(Conv::Strict);
let jsonb = json_cache.get_or_insert_with(value, convert_to_jsonb)?;
let (json, element_type) = jsonb_extract_internal(jsonb, paths)?;
let result = json_string_to_db_type(json, element_type, OutputVariant::ElementType)?;
Ok(result)
}
pub fn jsonb_extract(value: &OwnedValue, paths: &[OwnedValue]) -> crate::Result<OwnedValue> {
pub fn jsonb_extract(
value: &OwnedValue,
paths: &[OwnedValue],
json_cache: &JsonCacheCell,
) -> crate::Result<OwnedValue> {
if let OwnedValue::Null = value {
return Ok(OwnedValue::Null);
}
@@ -315,21 +344,23 @@ pub fn jsonb_extract(value: &OwnedValue, paths: &[OwnedValue]) -> crate::Result<
if paths.is_empty() {
return Ok(OwnedValue::Null);
}
let convert_to_jsonb = curry_convert_dbtype_to_jsonb(Conv::Strict);
let jsonb = json_cache.get_or_insert_with(value, convert_to_jsonb)?;
let (json, element_type) = jsonb_extract_internal(value, paths)?;
let (json, element_type) = jsonb_extract_internal(jsonb, paths)?;
let result = json_string_to_db_type(json, element_type, OutputVariant::ElementType)?;
Ok(result)
}
fn jsonb_extract_internal(
value: &OwnedValue,
value: Jsonb,
paths: &[OwnedValue],
) -> crate::Result<(Jsonb, ElementType)> {
let null = Jsonb::from_raw_data(JsonbHeader::make_null().into_bytes().as_bytes());
if paths.len() == 1 {
if let Some(path) = json_path_from_owned_value(&paths[0], true)? {
let mut json = convert_dbtype_to_jsonb(value, Conv::Strict)?;
let mut json = value;
let mut op = SearchOperation::new(json.len());
let res = json.operate_on_path(&path, &mut op);
@@ -348,7 +379,7 @@ fn jsonb_extract_internal(
}
}
let mut json = convert_dbtype_to_jsonb(value, Conv::Strict)?;
let mut json = value;
let mut result = Jsonb::make_empty_array(json.len());
// TODO: make an op to avoid creating new json for every path element
@@ -818,7 +849,8 @@ mod tests {
#[test]
fn test_json_array_length() {
let input = OwnedValue::build_text("[1,2,3,4]");
let result = json_array_length(&input, None).unwrap();
let json_cache = JsonCacheCell::new();
let result = json_array_length(&input, None, &json_cache).unwrap();
if let OwnedValue::Integer(res) = result {
assert_eq!(res, 4);
} else {
@@ -829,7 +861,8 @@ mod tests {
#[test]
fn test_json_array_length_empty() {
let input = OwnedValue::build_text("[]");
let result = json_array_length(&input, None).unwrap();
let json_cache = JsonCacheCell::new();
let result = json_array_length(&input, None, &json_cache).unwrap();
if let OwnedValue::Integer(res) = result {
assert_eq!(res, 0);
} else {
@@ -840,7 +873,9 @@ mod tests {
#[test]
fn test_json_array_length_root() {
let input = OwnedValue::build_text("[1,2,3,4]");
let result = json_array_length(&input, Some(&OwnedValue::build_text("$"))).unwrap();
let json_cache = JsonCacheCell::new();
let result =
json_array_length(&input, Some(&OwnedValue::build_text("$")), &json_cache).unwrap();
if let OwnedValue::Integer(res) = result {
assert_eq!(res, 4);
} else {
@@ -851,7 +886,8 @@ mod tests {
#[test]
fn test_json_array_length_not_array() {
let input = OwnedValue::build_text("{one: [1,2,3,4]}");
let result = json_array_length(&input, None).unwrap();
let json_cache = JsonCacheCell::new();
let result = json_array_length(&input, None, &json_cache).unwrap();
if let OwnedValue::Integer(res) = result {
assert_eq!(res, 0);
} else {
@@ -862,7 +898,9 @@ mod tests {
#[test]
fn test_json_array_length_via_prop() {
let input = OwnedValue::build_text("{one: [1,2,3,4]}");
let result = json_array_length(&input, Some(&OwnedValue::build_text("$.one"))).unwrap();
let json_cache = JsonCacheCell::new();
let result =
json_array_length(&input, Some(&OwnedValue::build_text("$.one")), &json_cache).unwrap();
if let OwnedValue::Integer(res) = result {
assert_eq!(res, 4);
} else {
@@ -873,7 +911,9 @@ mod tests {
#[test]
fn test_json_array_length_via_index() {
let input = OwnedValue::build_text("[[1,2,3,4]]");
let result = json_array_length(&input, Some(&OwnedValue::build_text("$[0]"))).unwrap();
let json_cache = JsonCacheCell::new();
let result =
json_array_length(&input, Some(&OwnedValue::build_text("$[0]")), &json_cache).unwrap();
if let OwnedValue::Integer(res) = result {
assert_eq!(res, 4);
} else {
@@ -884,7 +924,9 @@ mod tests {
#[test]
fn test_json_array_length_via_index_not_array() {
let input = OwnedValue::build_text("[1,2,3,4]");
let result = json_array_length(&input, Some(&OwnedValue::build_text("$[2]"))).unwrap();
let json_cache = JsonCacheCell::new();
let result =
json_array_length(&input, Some(&OwnedValue::build_text("$[2]")), &json_cache).unwrap();
if let OwnedValue::Integer(res) = result {
assert_eq!(res, 0);
} else {
@@ -895,15 +937,18 @@ mod tests {
#[test]
fn test_json_array_length_via_index_bad_prop() {
let input = OwnedValue::build_text("{one: [1,2,3,4]}");
let result = json_array_length(&input, Some(&OwnedValue::build_text("$.two"))).unwrap();
let json_cache = JsonCacheCell::new();
let result =
json_array_length(&input, Some(&OwnedValue::build_text("$.two")), &json_cache).unwrap();
assert_eq!(OwnedValue::Null, result);
}
#[test]
fn test_json_array_length_simple_json_subtype() {
let input = OwnedValue::build_text("[1,2,3]");
let json_cache = JsonCacheCell::new();
let wrapped = get_json(&input, None).unwrap();
let result = json_array_length(&wrapped, None).unwrap();
let result = json_array_length(&wrapped, None, &json_cache).unwrap();
if let OwnedValue::Integer(res) = result {
assert_eq!(res, 3);
@@ -914,9 +959,11 @@ mod tests {
#[test]
fn test_json_extract_missing_path() {
let json_cache = JsonCacheCell::new();
let result = json_extract(
&OwnedValue::build_text("{\"a\":2}"),
&[OwnedValue::build_text("$.x")],
&json_cache,
);
match result {
@@ -926,7 +973,12 @@ mod tests {
}
#[test]
fn test_json_extract_null_path() {
let result = json_extract(&OwnedValue::build_text("{\"a\":2}"), &[OwnedValue::Null]);
let json_cache = JsonCacheCell::new();
let result = json_extract(
&OwnedValue::build_text("{\"a\":2}"),
&[OwnedValue::Null],
&json_cache,
);
match result {
Ok(OwnedValue::Null) => (),
@@ -936,9 +988,11 @@ mod tests {
#[test]
fn test_json_path_invalid() {
let json_cache = JsonCacheCell::new();
let result = json_extract(
&OwnedValue::build_text("{\"a\":2}"),
&[OwnedValue::Float(1.1)],
&json_cache,
);
match result {
@@ -1263,11 +1317,15 @@ mod tests {
#[test]
fn test_json_set_field_empty_object() {
let result = json_set(&[
OwnedValue::build_text("{}"),
OwnedValue::build_text("$.field"),
OwnedValue::build_text("value"),
]);
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
OwnedValue::build_text("{}"),
OwnedValue::build_text("$.field"),
OwnedValue::build_text("value"),
],
&json_cache,
);
assert!(result.is_ok());
@@ -1276,11 +1334,15 @@ mod tests {
#[test]
fn test_json_set_replace_field() {
let result = json_set(&[
OwnedValue::build_text(r#"{"field":"old_value"}"#),
OwnedValue::build_text("$.field"),
OwnedValue::build_text("new_value"),
]);
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
OwnedValue::build_text(r#"{"field":"old_value"}"#),
OwnedValue::build_text("$.field"),
OwnedValue::build_text("new_value"),
],
&json_cache,
);
assert!(result.is_ok());
@@ -1292,11 +1354,15 @@ mod tests {
#[test]
fn test_json_set_set_deeply_nested_key() {
let result = json_set(&[
OwnedValue::build_text("{}"),
OwnedValue::build_text("$.object.doesnt.exist"),
OwnedValue::build_text("value"),
]);
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
OwnedValue::build_text("{}"),
OwnedValue::build_text("$.object.doesnt.exist"),
OwnedValue::build_text("value"),
],
&json_cache,
);
assert!(result.is_ok());
@@ -1308,11 +1374,15 @@ mod tests {
#[test]
fn test_json_set_add_value_to_empty_array() {
let result = json_set(&[
OwnedValue::build_text("[]"),
OwnedValue::build_text("$[0]"),
OwnedValue::build_text("value"),
]);
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
OwnedValue::build_text("[]"),
OwnedValue::build_text("$[0]"),
OwnedValue::build_text("value"),
],
&json_cache,
);
assert!(result.is_ok());
@@ -1321,11 +1391,15 @@ mod tests {
#[test]
fn test_json_set_add_value_to_nonexistent_array() {
let result = json_set(&[
OwnedValue::build_text("{}"),
OwnedValue::build_text("$.some_array[0]"),
OwnedValue::Integer(123),
]);
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
OwnedValue::build_text("{}"),
OwnedValue::build_text("$.some_array[0]"),
OwnedValue::Integer(123),
],
&json_cache,
);
assert!(result.is_ok());
@@ -1337,11 +1411,15 @@ mod tests {
#[test]
fn test_json_set_add_value_to_array() {
let result = json_set(&[
OwnedValue::build_text("[123]"),
OwnedValue::build_text("$[1]"),
OwnedValue::Integer(456),
]);
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
OwnedValue::build_text("[123]"),
OwnedValue::build_text("$[1]"),
OwnedValue::Integer(456),
],
&json_cache,
);
assert!(result.is_ok());
@@ -1350,11 +1428,15 @@ mod tests {
#[test]
fn test_json_set_add_value_to_array_out_of_bounds() {
let result = json_set(&[
OwnedValue::build_text("[123]"),
OwnedValue::build_text("$[200]"),
OwnedValue::Integer(456),
]);
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
OwnedValue::build_text("[123]"),
OwnedValue::build_text("$[200]"),
OwnedValue::Integer(456),
],
&json_cache,
);
assert!(result.is_ok());
@@ -1363,11 +1445,15 @@ mod tests {
#[test]
fn test_json_set_replace_value_in_array() {
let result = json_set(&[
OwnedValue::build_text("[123]"),
OwnedValue::build_text("$[0]"),
OwnedValue::Integer(456),
]);
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
OwnedValue::build_text("[123]"),
OwnedValue::build_text("$[0]"),
OwnedValue::Integer(456),
],
&json_cache,
);
assert!(result.is_ok());
@@ -1376,11 +1462,15 @@ mod tests {
#[test]
fn test_json_set_null_path() {
let result = json_set(&[
OwnedValue::build_text("{}"),
OwnedValue::Null,
OwnedValue::Integer(456),
]);
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
OwnedValue::build_text("{}"),
OwnedValue::Null,
OwnedValue::Integer(456),
],
&json_cache,
);
assert!(result.is_ok());
@@ -1389,13 +1479,17 @@ mod tests {
#[test]
fn test_json_set_multiple_keys() {
let result = json_set(&[
OwnedValue::build_text("[123]"),
OwnedValue::build_text("$[0]"),
OwnedValue::Integer(456),
OwnedValue::build_text("$[1]"),
OwnedValue::Integer(789),
]);
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
OwnedValue::build_text("[123]"),
OwnedValue::build_text("$[0]"),
OwnedValue::Integer(456),
OwnedValue::build_text("$[1]"),
OwnedValue::Integer(789),
],
&json_cache,
);
assert!(result.is_ok());
@@ -1404,11 +1498,15 @@ mod tests {
#[test]
fn test_json_set_add_array_in_nested_object() {
let result = json_set(&[
OwnedValue::build_text("{}"),
OwnedValue::build_text("$.object[0].field"),
OwnedValue::Integer(123),
]);
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
OwnedValue::build_text("{}"),
OwnedValue::build_text("$.object[0].field"),
OwnedValue::Integer(123),
],
&json_cache,
);
assert!(result.is_ok());
@@ -1420,11 +1518,15 @@ mod tests {
#[test]
fn test_json_set_add_array_in_array_in_nested_object() {
let result = json_set(&[
OwnedValue::build_text("{}"),
OwnedValue::build_text("$.object[0][0]"),
OwnedValue::Integer(123),
]);
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
OwnedValue::build_text("{}"),
OwnedValue::build_text("$.object[0][0]"),
OwnedValue::Integer(123),
],
&json_cache,
);
assert!(result.is_ok());
@@ -1433,13 +1535,17 @@ mod tests {
#[test]
fn test_json_set_add_array_in_array_in_nested_object_out_of_bounds() {
let result = json_set(&[
OwnedValue::build_text("{}"),
OwnedValue::build_text("$.object[123].another"),
OwnedValue::build_text("value"),
OwnedValue::build_text("$.field"),
OwnedValue::build_text("value"),
]);
let json_cache = JsonCacheCell::new();
let result = json_set(
&[
OwnedValue::build_text("{}"),
OwnedValue::build_text("$.object[123].another"),
OwnedValue::build_text("value"),
OwnedValue::build_text("$.field"),
OwnedValue::build_text("value"),
],
&json_cache,
);
assert!(result.is_ok());

View File

@@ -445,6 +445,7 @@ impl ProgramBuilder {
self.constant_insns.is_empty(),
"constant_insns is not empty when build() is called, did you forget to call emit_constant_insns()?"
);
self.parameters.list.dedup();
Program {
max_registers: self.next_free_register,

View File

@@ -31,6 +31,7 @@ use crate::functions::datetime::{
exec_date, exec_datetime_full, exec_julianday, exec_strftime, exec_time, exec_unixepoch,
};
use crate::functions::printf::exec_printf;
use crate::pseudo::PseudoCursor;
use crate::result::LimboResult;
use crate::schema::{affinity, Affinity};
@@ -56,7 +57,7 @@ use crate::{
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,
json::jsonb_object, json::jsonb_remove, json::jsonb_replace, json::JsonCacheCell,
};
use crate::{
resolve_ext_path, Connection, MvCursor, MvStore, Result, TransactionState, DATABASE_VERSION,
@@ -244,6 +245,8 @@ pub struct ProgramState {
interrupted: bool,
parameters: HashMap<NonZero<usize>, OwnedValue>,
halt_state: Option<HaltState>,
#[cfg(feature = "json")]
json_cache: JsonCacheCell,
}
impl ProgramState {
@@ -264,6 +267,8 @@ impl ProgramState {
interrupted: false,
parameters: HashMap::new(),
halt_state: None,
#[cfg(feature = "json")]
json_cache: JsonCacheCell::new(),
}
}
@@ -303,6 +308,8 @@ impl ProgramState {
self.regex_cache.like.clear();
self.interrupted = false;
self.parameters.clear();
#[cfg(feature = "json")]
self.json_cache.clear()
}
pub fn get_cursor<'a>(&'a self, cursor_id: CursorID) -> std::cell::RefMut<'a, Cursor> {
@@ -2123,6 +2130,7 @@ impl Program {
dest,
} => {
let arg_count = func.arg_count;
match &func.func {
#[cfg(feature = "json")]
crate::function::Func::Json(json_func) => match json_func {
@@ -2136,7 +2144,7 @@ impl Program {
}
JsonFunc::Jsonb => {
let json_value = &state.registers[*start_reg];
let json_blob = jsonb(json_value);
let json_blob = jsonb(json_value, &state.json_cache);
match json_blob {
Ok(json) => state.registers[*dest] = json,
Err(e) => return Err(e),
@@ -2165,13 +2173,13 @@ impl Program {
}
JsonFunc::JsonExtract => {
let result = match arg_count {
0 => json_extract(&OwnedValue::Null, &[]),
0 => Ok(OwnedValue::Null),
_ => {
let val = &state.registers[*start_reg];
let reg_values = &state.registers
[*start_reg + 1..*start_reg + arg_count];
json_extract(val, reg_values)
json_extract(val, reg_values, &state.json_cache)
}
};
@@ -2182,13 +2190,13 @@ impl Program {
}
JsonFunc::JsonbExtract => {
let result = match arg_count {
0 => jsonb_extract(&OwnedValue::Null, &[]),
0 => Ok(OwnedValue::Null),
_ => {
let val = &state.registers[*start_reg];
let reg_values = &state.registers
[*start_reg + 1..*start_reg + arg_count];
jsonb_extract(val, reg_values)
jsonb_extract(val, reg_values, &state.json_cache)
}
};
@@ -2207,7 +2215,7 @@ impl Program {
JsonFunc::JsonArrowShiftExtract => json_arrow_shift_extract,
_ => unreachable!(),
};
let json_str = json_func(json, path);
let json_str = json_func(json, path, &state.json_cache);
match json_str {
Ok(json) => state.registers[*dest] = json,
Err(e) => return Err(e),
@@ -2222,7 +2230,7 @@ impl Program {
};
let func_result = match json_func {
JsonFunc::JsonArrayLength => {
json_array_length(json_value, path_value)
json_array_length(json_value, path_value, &state.json_cache)
}
JsonFunc::JsonType => json_type(json_value, path_value),
_ => unreachable!(),
@@ -2254,6 +2262,7 @@ impl Program {
JsonFunc::JsonRemove => {
if let Ok(json) = json_remove(
&state.registers[*start_reg..*start_reg + arg_count],
&state.json_cache,
) {
state.registers[*dest] = json;
} else {
@@ -2263,6 +2272,7 @@ impl Program {
JsonFunc::JsonbRemove => {
if let Ok(json) = jsonb_remove(
&state.registers[*start_reg..*start_reg + arg_count],
&state.json_cache,
) {
state.registers[*dest] = json;
} else {
@@ -2272,6 +2282,7 @@ impl Program {
JsonFunc::JsonReplace => {
if let Ok(json) = json_replace(
&state.registers[*start_reg..*start_reg + arg_count],
&state.json_cache,
) {
state.registers[*dest] = json;
} else {
@@ -2281,6 +2292,7 @@ impl Program {
JsonFunc::JsonbReplace => {
if let Ok(json) = jsonb_replace(
&state.registers[*start_reg..*start_reg + arg_count],
&state.json_cache,
) {
state.registers[*dest] = json;
} else {
@@ -2290,6 +2302,7 @@ impl Program {
JsonFunc::JsonInsert => {
if let Ok(json) = json_insert(
&state.registers[*start_reg..*start_reg + arg_count],
&state.json_cache,
) {
state.registers[*dest] = json;
} else {
@@ -2299,6 +2312,7 @@ impl Program {
JsonFunc::JsonbInsert => {
if let Ok(json) = jsonb_insert(
&state.registers[*start_reg..*start_reg + arg_count],
&state.json_cache,
) {
state.registers[*dest] = json;
} else {
@@ -2348,7 +2362,7 @@ impl Program {
let reg_values =
&state.registers[*start_reg..*start_reg + arg_count];
let json_result = json_set(reg_values);
let json_result = json_set(reg_values, &state.json_cache);
match json_result {
Ok(json) => state.registers[*dest] = json,