make column reuse blob/text fields

This commit is contained in:
Pere Diaz Bou
2025-03-27 14:33:50 +01:00
parent 78e9f1c09a
commit 5b7fcd27bd
10 changed files with 93 additions and 74 deletions

View File

@@ -1,5 +1,4 @@
use std::ffi::{c_char, c_void};
use std::rc::Rc;
#[allow(dead_code)]
#[repr(C)]
@@ -196,7 +195,7 @@ impl LimboValue {
return limbo_core::OwnedValue::Null;
}
let bytes = self.value.to_bytes();
limbo_core::OwnedValue::Blob(Rc::new(bytes.to_vec()))
limbo_core::OwnedValue::Blob(bytes.to_vec())
}
ValueType::Null => limbo_core::OwnedValue::Null,
}

View File

@@ -7,7 +7,6 @@ use jni::sys::{jdouble, jint, jlong};
use jni::JNIEnv;
use limbo_core::{OwnedValue, Statement, StepResult};
use std::num::NonZero;
use std::rc::Rc;
pub const STEP_RESULT_ID_ROW: i32 = 10;
#[allow(dead_code)]
@@ -264,7 +263,7 @@ pub extern "system" fn Java_tech_turso_core_LimboStatement_bindBlob<'local>(
stmt.stmt.bind_at(
NonZero::new(position as usize).unwrap(),
OwnedValue::Blob(Rc::new(blob)),
OwnedValue::Blob(blob),
);
SQLITE_OK
}

View File

@@ -350,7 +350,7 @@ fn py_to_owned_value(obj: &Bound<PyAny>) -> Result<limbo_core::OwnedValue> {
} else if let Ok(string) = obj.extract::<String>() {
return Ok(OwnedValue::Text(Text::from_str(string)));
} else if let Ok(bytes) = obj.downcast::<PyBytes>() {
return Ok(OwnedValue::Blob(Rc::new(bytes.as_bytes().to_vec())));
return Ok(OwnedValue::Blob(bytes.as_bytes().to_vec()));
} else {
return Err(PyErr::new::<ProgrammingError, _>(format!(
"Unsupported Python type: {}",

View File

@@ -5,7 +5,7 @@ use crate::{
opcodes_dictionary::OPCODE_DESCRIPTIONS,
};
use comfy_table::{Attribute, Cell, CellAlignment, Color, ContentArrangement, Row, Table};
use limbo_core::{Database, LimboError, OwnedValue, RefValue, Statement, StepResult};
use limbo_core::{Database, LimboError, RefValue, Statement, StepResult};
use clap::{Parser, ValueEnum};
use rustyline::{history::DefaultHistory, Editor};

View File

@@ -185,7 +185,7 @@ pub fn jsonb_remove(args: &[Register], json_cache: &JsonCacheCell) -> crate::Res
}
}
Ok(OwnedValue::Blob(Rc::new(json.data())))
Ok(OwnedValue::Blob(json.data()))
}
pub fn json_replace(args: &[Register], json_cache: &JsonCacheCell) -> crate::Result<OwnedValue> {
@@ -554,7 +554,7 @@ mod tests {
#[test]
#[should_panic(expected = "blob is not supported!")]
fn test_blob_not_supported() {
let target = OwnedValue::Blob(Rc::new(vec![1, 2, 3]));
let target = OwnedValue::Blob(vec![1, 2, 3]);
let patch = create_text("{}");
json_patch(&target, &patch).unwrap();
}

View File

@@ -73,7 +73,7 @@ pub fn get_json(json_value: &OwnedValue, indent: Option<&str>) -> crate::Result<
let jsonbin = Jsonb::new(b.len(), Some(b));
jsonbin.is_valid()?;
Ok(OwnedValue::Text(Text {
value: Rc::new(jsonbin.to_string()?.into_bytes()),
value: jsonbin.to_string()?.into_bytes(),
subtype: TextSubtype::Json,
}))
}
@@ -95,7 +95,7 @@ pub fn jsonb(json_value: &OwnedValue, cache: &JsonCacheCell) -> crate::Result<Ow
let jsonbin = cache.get_or_insert_with(json_value, json_conv_fn);
match jsonbin {
Ok(jsonbin) => Ok(OwnedValue::Blob(Rc::new(jsonbin.data()))),
Ok(jsonbin) => Ok(OwnedValue::Blob(jsonbin.data())),
Err(_) => {
bail_parse_error!("malformed JSON")
}
@@ -405,7 +405,7 @@ fn json_string_to_db_type(
) -> crate::Result<OwnedValue> {
let mut json_string = json.to_string()?;
if matches!(flag, OutputVariant::Binary) {
return Ok(OwnedValue::Blob(Rc::new(json.data())));
return Ok(OwnedValue::Blob(json.data()));
}
match element_type {
ElementType::ARRAY | ElementType::OBJECT => Ok(OwnedValue::Text(Text::json(json_string))),
@@ -414,12 +414,12 @@ fn json_string_to_db_type(
json_string.remove(json_string.len() - 1);
json_string.remove(0);
Ok(OwnedValue::Text(Text {
value: Rc::new(json_string.into_bytes()),
value: json_string.into_bytes(),
subtype: TextSubtype::Json,
}))
} else {
Ok(OwnedValue::Text(Text {
value: Rc::new(json_string.into_bytes()),
value: json_string.into_bytes(),
subtype: TextSubtype::Text,
}))
}
@@ -764,7 +764,7 @@ mod tests {
#[test]
fn test_get_json_blob_valid_jsonb() {
let binary_json = vec![124, 55, 104, 101, 121, 39, 121, 111];
let input = OwnedValue::Blob(Rc::new(binary_json));
let input = OwnedValue::Blob(binary_json);
let result = get_json(&input, None).unwrap();
if let OwnedValue::Text(result_str) = result {
assert!(result_str.as_str().contains(r#"{"hey":"yo"}"#));
@@ -777,7 +777,7 @@ mod tests {
#[test]
fn test_get_json_blob_invalid_jsonb() {
let binary_json: Vec<u8> = vec![0xA2, 0x62, 0x6B, 0x31, 0x62, 0x76]; // Incomplete binary JSON
let input = OwnedValue::Blob(Rc::new(binary_json));
let input = OwnedValue::Blob(binary_json);
let result = get_json(&input, None);
println!("{:?}", result);
match result {
@@ -832,7 +832,7 @@ mod tests {
#[test]
fn test_json_array_blob_invalid() {
let blob = Register::OwnedValue(OwnedValue::Blob(Rc::new("1".as_bytes().to_vec())));
let blob = Register::OwnedValue(OwnedValue::Blob("1".as_bytes().to_vec()));
let input = vec![blob];

View File

@@ -3894,7 +3894,7 @@ mod tests {
.unwrap();
let key = OwnedValue::Integer(*key);
let value = ImmutableRecord::from_registers(&[Register::OwnedValue(
OwnedValue::Blob(Rc::new(vec![0; *size])),
OwnedValue::Blob(vec![0; *size]),
)]);
tracing::info!("insert key:{}", key);
run_until_done(|| cursor.insert(&key, &value, true), pager.deref()).unwrap();
@@ -3961,7 +3961,7 @@ mod tests {
let key = OwnedValue::Integer(key);
let value = ImmutableRecord::from_registers(&[Register::OwnedValue(
OwnedValue::Blob(Rc::new(vec![0; size])),
OwnedValue::Blob(vec![0; size]),
)]);
run_until_done(|| cursor.insert(&key, &value, true), pager.deref()).unwrap();
}
@@ -4874,9 +4874,11 @@ mod tests {
let page = get_page(2);
let usable_space = 4096;
let record = ImmutableRecord::from_registers(&[Register::OwnedValue(OwnedValue::Blob(
Rc::new(vec![0; 3600]),
))]);
let record =
ImmutableRecord::from_registers(&[Register::OwnedValue(OwnedValue::Blob(vec![
0;
3600
]))]);
let mut payload: Vec<u8> = Vec::new();
fill_cell_payload(
page.get_contents().page_type(),

View File

@@ -34,7 +34,7 @@ pub enum TextSubtype {
#[derive(Debug, Clone, PartialEq)]
pub struct Text {
pub value: Rc<Vec<u8>>,
pub value: Vec<u8>,
pub subtype: TextSubtype,
}
@@ -51,14 +51,14 @@ impl Text {
pub fn new(value: &str) -> Self {
Self {
value: Rc::new(value.as_bytes().to_vec()),
value: value.as_bytes().to_vec(),
subtype: TextSubtype::Text,
}
}
pub fn json(value: String) -> Self {
Self {
value: Rc::new(value.into_bytes()),
value: value.into_bytes(),
subtype: TextSubtype::Json,
}
}
@@ -88,7 +88,7 @@ pub enum OwnedValue {
Integer(i64),
Float(f64),
Text(Text),
Blob(Rc<Vec<u8>>),
Blob(Vec<u8>),
}
#[derive(Debug, Clone, PartialEq)]
@@ -97,7 +97,7 @@ pub struct RawSlice {
len: usize,
}
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
pub enum RefValue {
Null,
Integer(i64),
@@ -120,7 +120,7 @@ impl OwnedValue {
}
pub fn from_blob(data: Vec<u8>) -> Self {
OwnedValue::Blob(std::rc::Rc::new(data))
OwnedValue::Blob(data)
}
pub fn to_text(&self) -> Option<&str> {
@@ -341,7 +341,7 @@ impl OwnedValue {
let Some(blob) = v.to_blob() else {
return Ok(OwnedValue::Null);
};
Ok(OwnedValue::Blob(Rc::new(blob)))
Ok(OwnedValue::Blob(blob))
}
ExtValueType::Error => {
let Some(err) = v.to_error_details() else {
@@ -742,7 +742,6 @@ impl ImmutableRecord {
size_values += value_size;
}
let mut header_size = size_header;
let mut header_bytes_buf: Vec<u8> = Vec::new();
if header_size <= 126 {
// common case
header_size += 1;
@@ -879,10 +878,10 @@ impl RefValue {
RefValue::Integer(i) => OwnedValue::Integer(*i),
RefValue::Float(f) => OwnedValue::Float(*f),
RefValue::Text(text_ref) => OwnedValue::Text(Text {
value: Rc::new(text_ref.value.to_slice().to_vec()),
value: text_ref.value.to_slice().to_vec(),
subtype: text_ref.subtype.clone(),
}),
RefValue::Blob(b) => OwnedValue::Blob(Rc::new(b.to_slice().to_vec())),
RefValue::Blob(b) => OwnedValue::Blob(b.to_slice().to_vec()),
}
}
pub fn to_blob(&self) -> Option<&[u8]> {
@@ -1457,7 +1456,7 @@ mod tests {
#[test]
fn test_serialize_blob() {
let blob = Rc::new(vec![1, 2, 3, 4, 5]);
let blob = vec![1, 2, 3, 4, 5];
let record = Record::new(vec![OwnedValue::Blob(blob.clone())]);
let mut buf = Vec::new();
record.serialize(&mut buf);

View File

@@ -672,7 +672,7 @@ pub fn insn_to_str(
0,
*dest as i32,
0,
OwnedValue::Blob(Rc::new(value.clone())),
OwnedValue::Blob(value.clone()),
0,
format!(
"r[{}]={} (len={})",

View File

@@ -50,7 +50,7 @@ use crate::util::{
use crate::vdbe::builder::CursorType;
use crate::vdbe::insn::Insn;
use crate::vector::{vector32, vector64, vector_distance_cos, vector_extract};
use crate::{bail_constraint_error, info, CheckpointStatus};
use crate::{bail_constraint_error, info, CheckpointStatus, RefValue};
#[cfg(feature = "json")]
use crate::{
function::JsonFunc, json::get_json, json::is_json_valid, json::json_array,
@@ -1180,17 +1180,38 @@ impl Program {
);
let cursor = cursor.as_btree_mut();
let record = cursor.record();
if let Some(record) = record.as_ref() {
let value = if let Some(record) = record.as_ref() {
if cursor.get_null_flag() {
OwnedValue::Null
RefValue::Null
} else {
record.get_value(*column).to_owned()
record.get_value(*column).clone()
}
} else {
OwnedValue::Null
}
RefValue::Null
};
value
};
state.registers[*dest] = Register::OwnedValue(value);
// If we are copying a text/blob, let's try to simply update size of text if we need to allocate more and reuse.
match (&value, &mut state.registers[*dest]) {
(
RefValue::Text(text_ref),
Register::OwnedValue(OwnedValue::Text(text_reg)),
) => {
text_reg.value.clear();
text_reg.value.extend_from_slice(text_ref.value.to_slice());
}
(
RefValue::Blob(raw_slice),
Register::OwnedValue(OwnedValue::Blob(blob_reg)),
) => {
blob_reg.clear();
blob_reg.extend_from_slice(raw_slice.to_slice());
}
_ => {
let reg = &mut state.registers[*dest];
*reg = Register::OwnedValue(value.to_owned());
}
}
}
CursorType::Sorter => {
let record = {
@@ -1436,8 +1457,7 @@ impl Program {
state.pc += 1;
}
Insn::Blob { value, dest } => {
state.registers[*dest] =
Register::OwnedValue(OwnedValue::Blob(Rc::new(value.clone())));
state.registers[*dest] = Register::OwnedValue(OwnedValue::Blob(value.clone()));
state.pc += 1;
}
Insn::RowId { cursor_id, dest } => {
@@ -3908,7 +3928,7 @@ fn exec_randomblob(reg: &OwnedValue) -> OwnedValue {
let mut blob: Vec<u8> = vec![0; length];
getrandom::getrandom(&mut blob).expect("Failed to generate random blob");
OwnedValue::Blob(Rc::new(blob))
OwnedValue::Blob(blob)
}
fn exec_quote(value: &OwnedValue) -> OwnedValue {
@@ -4060,7 +4080,7 @@ fn exec_instr(reg: &OwnedValue, pattern: &OwnedValue) -> OwnedValue {
if let (OwnedValue::Blob(reg), OwnedValue::Blob(pattern)) = (reg, pattern) {
let result = reg
.windows(pattern.len())
.position(|window| window == **pattern)
.position(|window| window == *pattern)
.map_or(0, |i| i + 1);
return OwnedValue::Integer(result as i64);
}
@@ -4117,7 +4137,7 @@ fn exec_unhex(reg: &OwnedValue, ignored_chars: Option<&OwnedValue>) -> OwnedValu
OwnedValue::Null => OwnedValue::Null,
_ => match ignored_chars {
None => match hex::decode(reg.to_string()) {
Ok(bytes) => OwnedValue::Blob(Rc::new(bytes)),
Ok(bytes) => OwnedValue::Blob(bytes),
Err(_) => OwnedValue::Null,
},
Some(ignore) => match ignore {
@@ -4129,7 +4149,7 @@ fn exec_unhex(reg: &OwnedValue, ignored_chars: Option<&OwnedValue>) -> OwnedValu
.trim_end_matches(|x| pat.contains(x))
.to_string();
match hex::decode(trimmed) {
Ok(bytes) => OwnedValue::Blob(Rc::new(bytes)),
Ok(bytes) => OwnedValue::Blob(bytes),
Err(_) => OwnedValue::Null,
}
}
@@ -4240,7 +4260,7 @@ fn exec_zeroblob(req: &OwnedValue) -> OwnedValue {
OwnedValue::Text(s) => s.as_str().parse().unwrap_or(0),
_ => 0,
};
OwnedValue::Blob(Rc::new(vec![0; length.max(0) as usize]))
OwnedValue::Blob(vec![0; length.max(0) as usize])
}
// exec_if returns whether you should jump
@@ -4264,7 +4284,7 @@ fn exec_cast(value: &OwnedValue, datatype: &str) -> OwnedValue {
// Convert to TEXT first, then interpret as BLOB
// TODO: handle encoding
let text = value.to_string();
OwnedValue::Blob(Rc::new(text.into_bytes()))
OwnedValue::Blob(text.into_bytes())
}
// TEXT To cast a BLOB value to TEXT, the sequence of bytes that make up the BLOB is interpreted as text encoded using the database encoding.
// Casting an INTEGER or REAL value into TEXT renders the value as if via sqlite3_snprintf() except that the resulting TEXT uses the encoding of the database connection.
@@ -4493,7 +4513,7 @@ mod tests {
let expected_len = OwnedValue::Integer(7);
assert_eq!(exec_length(&input_float), expected_len);
let expected_blob = OwnedValue::Blob(Rc::new("example".as_bytes().to_vec()));
let expected_blob = OwnedValue::Blob("example".as_bytes().to_vec());
let expected_len = OwnedValue::Integer(7);
assert_eq!(exec_length(&expected_blob), expected_len);
}
@@ -4531,7 +4551,7 @@ mod tests {
let expected: OwnedValue = OwnedValue::build_text("text");
assert_eq!(exec_typeof(&input), expected);
let input = OwnedValue::Blob(Rc::new("limbo".as_bytes().to_vec()));
let input = OwnedValue::Blob("limbo".as_bytes().to_vec());
let expected: OwnedValue = OwnedValue::build_text("blob");
assert_eq!(exec_typeof(&input), expected);
}
@@ -4565,7 +4585,7 @@ mod tests {
);
assert_eq!(exec_unicode(&OwnedValue::Null), OwnedValue::Null);
assert_eq!(
exec_unicode(&OwnedValue::Blob(Rc::new("example".as_bytes().to_vec()))),
exec_unicode(&OwnedValue::Blob("example".as_bytes().to_vec())),
OwnedValue::Integer(101)
);
}
@@ -4725,11 +4745,11 @@ mod tests {
#[test]
fn test_unhex() {
let input = OwnedValue::build_text("6f");
let expected = OwnedValue::Blob(Rc::new(vec![0x6f]));
let expected = OwnedValue::Blob(vec![0x6f]);
assert_eq!(exec_unhex(&input, None), expected);
let input = OwnedValue::build_text("6f");
let expected = OwnedValue::Blob(Rc::new(vec![0x6f]));
let expected = OwnedValue::Blob(vec![0x6f]);
assert_eq!(exec_unhex(&input, None), expected);
let input = OwnedValue::build_text("611");
@@ -4737,7 +4757,7 @@ mod tests {
assert_eq!(exec_unhex(&input, None), expected);
let input = OwnedValue::build_text("");
let expected = OwnedValue::Blob(Rc::new(vec![]));
let expected = OwnedValue::Blob(vec![]);
assert_eq!(exec_unhex(&input, None), expected);
let input = OwnedValue::build_text("61x");
@@ -5124,23 +5144,23 @@ mod tests {
let expected = OwnedValue::Integer(3);
assert_eq!(exec_instr(&input, &pattern), expected);
let input = OwnedValue::Blob(Rc::new(vec![1, 2, 3, 4, 5]));
let pattern = OwnedValue::Blob(Rc::new(vec![3, 4]));
let input = OwnedValue::Blob(vec![1, 2, 3, 4, 5]);
let pattern = OwnedValue::Blob(vec![3, 4]);
let expected = OwnedValue::Integer(3);
assert_eq!(exec_instr(&input, &pattern), expected);
let input = OwnedValue::Blob(Rc::new(vec![1, 2, 3, 4, 5]));
let pattern = OwnedValue::Blob(Rc::new(vec![3, 2]));
let input = OwnedValue::Blob(vec![1, 2, 3, 4, 5]);
let pattern = OwnedValue::Blob(vec![3, 2]);
let expected = OwnedValue::Integer(0);
assert_eq!(exec_instr(&input, &pattern), expected);
let input = OwnedValue::Blob(Rc::new(vec![0x61, 0x62, 0x63, 0x64, 0x65]));
let input = OwnedValue::Blob(vec![0x61, 0x62, 0x63, 0x64, 0x65]);
let pattern = OwnedValue::build_text("cd");
let expected = OwnedValue::Integer(3);
assert_eq!(exec_instr(&input, &pattern), expected);
let input = OwnedValue::build_text("abcde");
let pattern = OwnedValue::Blob(Rc::new(vec![0x63, 0x64]));
let pattern = OwnedValue::Blob(vec![0x63, 0x64]);
let expected = OwnedValue::Integer(3);
assert_eq!(exec_instr(&input, &pattern), expected);
}
@@ -5191,19 +5211,19 @@ mod tests {
let expected = Some(OwnedValue::Integer(0));
assert_eq!(exec_sign(&input), expected);
let input = OwnedValue::Blob(Rc::new(b"abc".to_vec()));
let input = OwnedValue::Blob(b"abc".to_vec());
let expected = Some(OwnedValue::Null);
assert_eq!(exec_sign(&input), expected);
let input = OwnedValue::Blob(Rc::new(b"42".to_vec()));
let input = OwnedValue::Blob(b"42".to_vec());
let expected = Some(OwnedValue::Integer(1));
assert_eq!(exec_sign(&input), expected);
let input = OwnedValue::Blob(Rc::new(b"-42".to_vec()));
let input = OwnedValue::Blob(b"-42".to_vec());
let expected = Some(OwnedValue::Integer(-1));
assert_eq!(exec_sign(&input), expected);
let input = OwnedValue::Blob(Rc::new(b"0".to_vec()));
let input = OwnedValue::Blob(b"0".to_vec());
let expected = Some(OwnedValue::Integer(0));
assert_eq!(exec_sign(&input), expected);
@@ -5215,39 +5235,39 @@ mod tests {
#[test]
fn test_exec_zeroblob() {
let input = OwnedValue::Integer(0);
let expected = OwnedValue::Blob(Rc::new(vec![]));
let expected = OwnedValue::Blob(vec![]);
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::Null;
let expected = OwnedValue::Blob(Rc::new(vec![]));
let expected = OwnedValue::Blob(vec![]);
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::Integer(4);
let expected = OwnedValue::Blob(Rc::new(vec![0; 4]));
let expected = OwnedValue::Blob(vec![0; 4]);
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::Integer(-1);
let expected = OwnedValue::Blob(Rc::new(vec![]));
let expected = OwnedValue::Blob(vec![]);
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::build_text("5");
let expected = OwnedValue::Blob(Rc::new(vec![0; 5]));
let expected = OwnedValue::Blob(vec![0; 5]);
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::build_text("-5");
let expected = OwnedValue::Blob(Rc::new(vec![]));
let expected = OwnedValue::Blob(vec![]);
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::build_text("text");
let expected = OwnedValue::Blob(Rc::new(vec![]));
let expected = OwnedValue::Blob(vec![]);
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::Float(2.6);
let expected = OwnedValue::Blob(Rc::new(vec![0; 2]));
let expected = OwnedValue::Blob(vec![0; 2]);
assert_eq!(exec_zeroblob(&input), expected);
let input = OwnedValue::Blob(Rc::new(vec![1]));
let expected = OwnedValue::Blob(Rc::new(vec![]));
let input = OwnedValue::Blob(vec![1]);
let expected = OwnedValue::Blob(vec![]);
assert_eq!(exec_zeroblob(&input), expected);
}