From 9268560a511f88b0fd51a5ffce2668cab89415a0 Mon Sep 17 00:00:00 2001 From: Ramkarthik Krishnamurthy Date: Sat, 13 Jul 2024 00:55:40 +0530 Subject: [PATCH 1/7] Implements group concat aggregate function --- core/translate.rs | 40 ++++++++++++++++++++++++++++++++++++++-- core/types.rs | 6 ++++++ core/vdbe.rs | 40 ++++++++++++++++++++++++++++++++++++++-- testing/all.test | 8 ++++++++ 4 files changed, 90 insertions(+), 4 deletions(-) diff --git a/core/translate.rs b/core/translate.rs index 99a3c0a50..569977e96 100644 --- a/core/translate.rs +++ b/core/translate.rs @@ -8,7 +8,7 @@ use crate::sqlite3_ondisk::{DatabaseHeader, MIN_PAGE_CACHE_SIZE}; use crate::util::normalize_ident; use crate::vdbe::{Insn, Program, ProgramBuilder}; use anyhow::Result; -use sqlite3_parser::ast::{self, Expr}; +use sqlite3_parser::ast::{self, Expr, Literal}; struct Select { columns: Vec, @@ -685,6 +685,7 @@ fn translate_aggregation( program.emit_insn(Insn::AggStep { acc_reg: target_register, col: expr_reg, + delimiter: 0, func: AggFunc::Avg, }); target_register @@ -701,11 +702,42 @@ fn translate_aggregation( program.emit_insn(Insn::AggStep { acc_reg: target_register, col: expr_reg, + delimiter: 0, func: AggFunc::Count, }); target_register } - AggFunc::GroupConcat => todo!(), + AggFunc::GroupConcat => { + if args.len() != 1 && args.len() != 2 { + anyhow::bail!("Parse error: group_concat bad number of arguments"); + } + + let expr = &args[0]; + let expr_reg = program.alloc_register(); + let delimiter_reg = program.alloc_register(); + let _ = translate_expr(program, select, expr, expr_reg); + if args.len() == 2 { + let delimiter = match &args[1] { + ast::Expr::Id(ident) => &ident.0, + ast::Expr::Literal(Literal::String(s)) => &s, + _ => anyhow::bail!("Incorrect delimiter parameter"), + }; + let delimiter = ast::Expr::Literal(Literal::String(delimiter.to_string())); + let _ = translate_expr(program, select, &delimiter, delimiter_reg); + } else { + let delimiter = ast::Expr::Literal(Literal::String(String::from("\",\""))); + let _ = translate_expr(program, select, &delimiter, delimiter_reg); + } + + program.emit_insn(Insn::AggStep { + acc_reg: target_register, + col: expr_reg, + delimiter: delimiter_reg, + func: AggFunc::GroupConcat, + }); + + target_register + } AggFunc::Max => { if args.len() != 1 { anyhow::bail!("Parse error: max bad number of arguments"); @@ -716,6 +748,7 @@ fn translate_aggregation( program.emit_insn(Insn::AggStep { acc_reg: target_register, col: expr_reg, + delimiter: 0, func: AggFunc::Max, }); target_register @@ -730,6 +763,7 @@ fn translate_aggregation( program.emit_insn(Insn::AggStep { acc_reg: target_register, col: expr_reg, + delimiter: 0, func: AggFunc::Min, }); target_register @@ -745,6 +779,7 @@ fn translate_aggregation( program.emit_insn(Insn::AggStep { acc_reg: target_register, col: expr_reg, + delimiter: 0, func: AggFunc::Sum, }); target_register @@ -759,6 +794,7 @@ fn translate_aggregation( program.emit_insn(Insn::AggStep { acc_reg: target_register, col: expr_reg, + delimiter: 0, func: AggFunc::Total, }); target_register diff --git a/core/types.rs b/core/types.rs index 5f99f1482..f6913dcd8 100644 --- a/core/types.rs +++ b/core/types.rs @@ -49,6 +49,7 @@ impl Display for OwnedValue { AggContext::Count(count) => write!(f, "{}", count), AggContext::Max(max) => write!(f, "{}", max), AggContext::Min(min) => write!(f, "{}", min), + AggContext::GroupConcat(s) => write!(f, "{}", s), }, OwnedValue::Record(r) => write!(f, "{:?}", r), } @@ -62,6 +63,7 @@ pub enum AggContext { Count(OwnedValue), Max(OwnedValue), Min(OwnedValue), + GroupConcat(OwnedValue), } impl std::ops::Add for OwnedValue { @@ -81,6 +83,9 @@ impl std::ops::Add for OwnedValue { (OwnedValue::Float(float_left), OwnedValue::Float(float_right)) => { OwnedValue::Float(float_left + float_right) } + (OwnedValue::Text(string_left), OwnedValue::Text(string_right)) => { + OwnedValue::Text(Rc::new(string_left.to_string() + &string_right.to_string())) + } (lhs, OwnedValue::Null) => lhs, (OwnedValue::Null, rhs) => rhs, _ => OwnedValue::Float(0.0), @@ -171,6 +176,7 @@ pub fn to_value(value: &OwnedValue) -> Value<'_> { AggContext::Count(count) => to_value(count), AggContext::Max(max) => to_value(max), AggContext::Min(min) => to_value(min), + AggContext::GroupConcat(s) => to_value(s), }, OwnedValue::Record(_) => todo!(), } diff --git a/core/vdbe.rs b/core/vdbe.rs index e99451d9d..9c75ef961 100644 --- a/core/vdbe.rs +++ b/core/vdbe.rs @@ -168,6 +168,7 @@ pub enum Insn { AggStep { acc_reg: usize, col: usize, + delimiter: usize, func: AggFunc, }, @@ -671,7 +672,12 @@ impl Program { } _ => unreachable!("DecrJumpZero on non-integer register"), }, - Insn::AggStep { acc_reg, col, func } => { + Insn::AggStep { + acc_reg, + col, + delimiter, + func, + } => { if let OwnedValue::Null = &state.registers[*acc_reg] { state.registers[*acc_reg] = match func { AggFunc::Avg => OwnedValue::Agg(Box::new(AggContext::Avg( @@ -718,6 +724,9 @@ impl Program { } } } + AggFunc::GroupConcat => OwnedValue::Agg(Box::new( + AggContext::GroupConcat(OwnedValue::Text(Rc::new("".to_string()))), + )), _ => { todo!(); } @@ -821,6 +830,27 @@ impl Program { } } } + AggFunc::GroupConcat => { + let col = state.registers[*col].clone(); + let delimiter = state.registers[*delimiter].clone(); + let OwnedValue::Agg(agg) = state.registers[*acc_reg].borrow_mut() + else { + unreachable!(); + }; + let AggContext::GroupConcat(acc) = agg.borrow_mut() else { + unreachable!(); + }; + // let AggContext::GroupConcat(acc, _col, delimiter) = + // state.registers.borrow_mut() + // else { + // unreachable!(); + // }; + if acc.to_string().len() == 0 { + *acc = col; + } else { + *acc += delimiter + col; + } + } _ => { todo!(); } @@ -841,6 +871,7 @@ impl Program { AggFunc::Count => {} AggFunc::Max => {} AggFunc::Min => {} + AggFunc::GroupConcat => {} _ => { todo!(); } @@ -1240,7 +1271,12 @@ fn insn_to_str(addr: BranchOffset, insn: &Insn, indent: String) -> String { 0, "".to_string(), ), - Insn::AggStep { func, acc_reg, col } => ( + Insn::AggStep { + func, + acc_reg, + delimiter: _, + col, + } => ( "AggStep", 0, *col as i32, diff --git a/testing/all.test b/testing/all.test index 252ae03e9..15856a2ef 100755 --- a/testing/all.test +++ b/testing/all.test @@ -61,6 +61,14 @@ do_execsql_test select-min { SELECT min(age) FROM users; } {1} +do_execsql_test select-group-concat { + SELECT group_concat(name) FROM products; +} {hat,cap,shirt,sweater,sweatshirt,shorts,jeans,sneakers,boots,coat,accessories} + +do_execsql_test select-group-concat-with-delimiter { + SELECT group_concat(name, ';') FROM products; +} {hat;cap;shirt;sweater;sweatshirt;shorts;jeans;sneakers;boots;coat;accessories} + do_execsql_test select-limit-0 { SELECT id FROM users LIMIT 0; } {} From a303e6ad96358d5d1155980187d7d5e372b6d6c6 Mon Sep 17 00:00:00 2001 From: Ramkarthik Krishnamurthy Date: Sat, 13 Jul 2024 02:13:28 +0530 Subject: [PATCH 2/7] Implements string_agg and extends group_concat to work with column delimiters --- core/translate.rs | 58 +++++++++++++++++++++++++++++++++++++++-------- core/types.rs | 12 ++++++++++ core/vdbe.rs | 22 ++++-------------- testing/all.test | 4 ++++ 4 files changed, 70 insertions(+), 26 deletions(-) diff --git a/core/translate.rs b/core/translate.rs index 569977e96..3adc1fdea 100644 --- a/core/translate.rs +++ b/core/translate.rs @@ -716,19 +716,25 @@ fn translate_aggregation( let expr_reg = program.alloc_register(); let delimiter_reg = program.alloc_register(); let _ = translate_expr(program, select, expr, expr_reg); + let delimiter: ast::Expr; if args.len() == 2 { - let delimiter = match &args[1] { - ast::Expr::Id(ident) => &ident.0, - ast::Expr::Literal(Literal::String(s)) => &s, + match &args[1] { + ast::Expr::Id(ident) => { + if ident.0.starts_with("\"") { + delimiter = ast::Expr::Literal(Literal::String(ident.0.to_string())); + } else { + delimiter = args[1].clone(); + } + }, + ast::Expr::Literal(Literal::String(s)) => { + delimiter = ast::Expr::Literal(Literal::String(s.to_string())); + }, _ => anyhow::bail!("Incorrect delimiter parameter"), }; - let delimiter = ast::Expr::Literal(Literal::String(delimiter.to_string())); - let _ = translate_expr(program, select, &delimiter, delimiter_reg); } else { - let delimiter = ast::Expr::Literal(Literal::String(String::from("\",\""))); - let _ = translate_expr(program, select, &delimiter, delimiter_reg); + delimiter = ast::Expr::Literal(Literal::String(String::from("\",\""))); } - + let _ = translate_expr(program, select, &delimiter, delimiter_reg); program.emit_insn(Insn::AggStep { acc_reg: target_register, col: expr_reg, @@ -768,7 +774,41 @@ fn translate_aggregation( }); target_register } - AggFunc::StringAgg => todo!(), + AggFunc::StringAgg => { + if args.len() != 2 { + anyhow::bail!("Parse error: StringAgg bad number of arguments"); + } + + let expr = &args[0]; + let expr_reg = program.alloc_register(); + let delimiter_reg = program.alloc_register(); + let _ = translate_expr(program, select, expr, expr_reg); + let delimiter: ast::Expr; + match &args[1] { + ast::Expr::Id(ident) => { + if ident.0.starts_with("\"") { + delimiter = ast::Expr::Literal(Literal::String(ident.0.to_string())); + } else { + delimiter = args[1].clone(); + } + }, + ast::Expr::Literal(Literal::String(s)) => { + delimiter = ast::Expr::Literal(Literal::String(s.to_string())); + }, + _ => anyhow::bail!("Incorrect delimiter parameter"), + }; + let _ = translate_expr(program, select, &delimiter, delimiter_reg); + let _ = translate_expr(program, select, &delimiter, delimiter_reg); + + program.emit_insn(Insn::AggStep { + acc_reg: target_register, + col: expr_reg, + delimiter: delimiter_reg, + func: AggFunc::StringAgg, + }); + + target_register + }, AggFunc::Sum => { if args.len() != 1 { anyhow::bail!("Parse error: sum bad number of arguments"); diff --git a/core/types.rs b/core/types.rs index f6913dcd8..734e3cc6a 100644 --- a/core/types.rs +++ b/core/types.rs @@ -86,6 +86,18 @@ impl std::ops::Add for OwnedValue { (OwnedValue::Text(string_left), OwnedValue::Text(string_right)) => { OwnedValue::Text(Rc::new(string_left.to_string() + &string_right.to_string())) } + (OwnedValue::Text(string_left), OwnedValue::Integer(int_right)) => { + OwnedValue::Text(Rc::new(string_left.to_string() + &int_right.to_string())) + } + (OwnedValue::Integer(int_left), OwnedValue::Text(string_right)) => { + OwnedValue::Text(Rc::new(int_left.to_string() + &string_right.to_string())) + } + (OwnedValue::Text(string_left), OwnedValue::Float(float_right)) => { + OwnedValue::Text(Rc::new(string_left.to_string() + &float_right.to_string())) + } + (OwnedValue::Float(float_left), OwnedValue::Text(string_right)) => { + OwnedValue::Text(Rc::new(float_left.to_string() + &string_right.to_string())) + } (lhs, OwnedValue::Null) => lhs, (OwnedValue::Null, rhs) => rhs, _ => OwnedValue::Float(0.0), diff --git a/core/vdbe.rs b/core/vdbe.rs index 9c75ef961..4bc4af63e 100644 --- a/core/vdbe.rs +++ b/core/vdbe.rs @@ -724,12 +724,10 @@ impl Program { } } } - AggFunc::GroupConcat => OwnedValue::Agg(Box::new( + AggFunc::GroupConcat | + AggFunc::StringAgg => OwnedValue::Agg(Box::new( AggContext::GroupConcat(OwnedValue::Text(Rc::new("".to_string()))), )), - _ => { - todo!(); - } }; } match func { @@ -830,7 +828,8 @@ impl Program { } } } - AggFunc::GroupConcat => { + AggFunc::GroupConcat | + AggFunc::StringAgg => { let col = state.registers[*col].clone(); let delimiter = state.registers[*delimiter].clone(); let OwnedValue::Agg(agg) = state.registers[*acc_reg].borrow_mut() @@ -840,20 +839,12 @@ impl Program { let AggContext::GroupConcat(acc) = agg.borrow_mut() else { unreachable!(); }; - // let AggContext::GroupConcat(acc, _col, delimiter) = - // state.registers.borrow_mut() - // else { - // unreachable!(); - // }; if acc.to_string().len() == 0 { *acc = col; } else { *acc += delimiter + col; } } - _ => { - todo!(); - } }; state.pc += 1; } @@ -871,10 +862,7 @@ impl Program { AggFunc::Count => {} AggFunc::Max => {} AggFunc::Min => {} - AggFunc::GroupConcat => {} - _ => { - todo!(); - } + AggFunc::GroupConcat | AggFunc::StringAgg => {} }; } OwnedValue::Null => { diff --git a/testing/all.test b/testing/all.test index 15856a2ef..287fce19b 100755 --- a/testing/all.test +++ b/testing/all.test @@ -69,6 +69,10 @@ do_execsql_test select-group-concat-with-delimiter { SELECT group_concat(name, ';') FROM products; } {hat;cap;shirt;sweater;sweatshirt;shorts;jeans;sneakers;boots;coat;accessories} +do_execsql_test select-group-concat-with-column-delimiter { + SELECT group_concat(name, id) FROM products; +} {hat2cap3shirt4sweater5sweatshirt6shorts7jeans8sneakers9boots10coat11accessories} + do_execsql_test select-limit-0 { SELECT id FROM users LIMIT 0; } {} From dddc0be88f792f26a20652519cb94c5e852e8c72 Mon Sep 17 00:00:00 2001 From: Ramkarthik Krishnamurthy Date: Sat, 13 Jul 2024 02:46:37 +0530 Subject: [PATCH 3/7] Some refactoring --- core/translate.rs | 41 ++++++++++++++++++++++++----------------- core/vdbe.rs | 3 ++- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/core/translate.rs b/core/translate.rs index 3adc1fdea..010d42053 100644 --- a/core/translate.rs +++ b/core/translate.rs @@ -712,29 +712,33 @@ fn translate_aggregation( anyhow::bail!("Parse error: group_concat bad number of arguments"); } - let expr = &args[0]; let expr_reg = program.alloc_register(); let delimiter_reg = program.alloc_register(); - let _ = translate_expr(program, select, expr, expr_reg); - let delimiter: ast::Expr; + + let expr = &args[0]; + let delimiter_expr: ast::Expr; + if args.len() == 2 { match &args[1] { ast::Expr::Id(ident) => { if ident.0.starts_with("\"") { - delimiter = ast::Expr::Literal(Literal::String(ident.0.to_string())); + delimiter_expr = ast::Expr::Literal(Literal::String(ident.0.to_string())); } else { - delimiter = args[1].clone(); + delimiter_expr = args[1].clone(); } }, ast::Expr::Literal(Literal::String(s)) => { - delimiter = ast::Expr::Literal(Literal::String(s.to_string())); + delimiter_expr = ast::Expr::Literal(Literal::String(s.to_string())); }, _ => anyhow::bail!("Incorrect delimiter parameter"), }; } else { - delimiter = ast::Expr::Literal(Literal::String(String::from("\",\""))); + delimiter_expr = ast::Expr::Literal(Literal::String(String::from("\",\""))); } - let _ = translate_expr(program, select, &delimiter, delimiter_reg); + + let _ = translate_expr(program, select, expr, expr_reg); + let _ = translate_expr(program, select, &delimiter_expr, delimiter_reg); + program.emit_insn(Insn::AggStep { acc_reg: target_register, col: expr_reg, @@ -776,29 +780,32 @@ fn translate_aggregation( } AggFunc::StringAgg => { if args.len() != 2 { - anyhow::bail!("Parse error: StringAgg bad number of arguments"); + anyhow::bail!("Parse error: string_agg bad number of arguments"); } - let expr = &args[0]; + let expr_reg = program.alloc_register(); let delimiter_reg = program.alloc_register(); - let _ = translate_expr(program, select, expr, expr_reg); - let delimiter: ast::Expr; + + let expr = &args[0]; + let delimiter_expr: ast::Expr; + match &args[1] { ast::Expr::Id(ident) => { if ident.0.starts_with("\"") { - delimiter = ast::Expr::Literal(Literal::String(ident.0.to_string())); + delimiter_expr = ast::Expr::Literal(Literal::String(ident.0.to_string())); } else { - delimiter = args[1].clone(); + delimiter_expr = args[1].clone(); } }, ast::Expr::Literal(Literal::String(s)) => { - delimiter = ast::Expr::Literal(Literal::String(s.to_string())); + delimiter_expr = ast::Expr::Literal(Literal::String(s.to_string())); }, _ => anyhow::bail!("Incorrect delimiter parameter"), }; - let _ = translate_expr(program, select, &delimiter, delimiter_reg); - let _ = translate_expr(program, select, &delimiter, delimiter_reg); + + let _ = translate_expr(program, select, expr, expr_reg); + let _ = translate_expr(program, select, &delimiter_expr, delimiter_reg); program.emit_insn(Insn::AggStep { acc_reg: target_register, diff --git a/core/vdbe.rs b/core/vdbe.rs index 4bc4af63e..2a4e4580d 100644 --- a/core/vdbe.rs +++ b/core/vdbe.rs @@ -842,7 +842,8 @@ impl Program { if acc.to_string().len() == 0 { *acc = col; } else { - *acc += delimiter + col; + *acc += delimiter; + *acc += col; } } }; From 59b1852d41532b7804226047d08f9a19759914ac Mon Sep 17 00:00:00 2001 From: Ramkarthik Krishnamurthy Date: Sat, 13 Jul 2024 10:59:46 +0530 Subject: [PATCH 4/7] Adds test for string_agg --- testing/all.test | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/testing/all.test b/testing/all.test index 287fce19b..215e3dc05 100755 --- a/testing/all.test +++ b/testing/all.test @@ -73,6 +73,14 @@ do_execsql_test select-group-concat-with-column-delimiter { SELECT group_concat(name, id) FROM products; } {hat2cap3shirt4sweater5sweatshirt6shorts7jeans8sneakers9boots10coat11accessories} +do_execsql_test select-string-agg-with-delimiter { + SELECT string_agg(name, ',') FROM products; +} {hat,cap,shirt,sweater,sweatshirt,shorts,jeans,sneakers,boots,coat,accessories} + +do_execsql_test select-string-agg-with-column-delimiter { + SELECT string_agg(name, id) FROM products; +} {hat2cap3shirt4sweater5sweatshirt6shorts7jeans8sneakers9boots10coat11accessories} + do_execsql_test select-limit-0 { SELECT id FROM users LIMIT 0; } {} From 59c642955536df7f4d0d22771b51bac32164ee1d Mon Sep 17 00:00:00 2001 From: Ramkarthik Krishnamurthy Date: Sat, 13 Jul 2024 11:47:59 +0530 Subject: [PATCH 5/7] Fix invalid column names in aggregate function --- core/translate.rs | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/core/translate.rs b/core/translate.rs index 010d42053..b6e339f8a 100644 --- a/core/translate.rs +++ b/core/translate.rs @@ -717,7 +717,7 @@ fn translate_aggregation( let expr = &args[0]; let delimiter_expr: ast::Expr; - + if args.len() == 2 { match &args[1] { ast::Expr::Id(ident) => { @@ -736,8 +736,12 @@ fn translate_aggregation( delimiter_expr = ast::Expr::Literal(Literal::String(String::from("\",\""))); } - let _ = translate_expr(program, select, expr, expr_reg); - let _ = translate_expr(program, select, &delimiter_expr, delimiter_reg); + if let Err(error) = translate_expr(program, select, expr, expr_reg) { + anyhow::bail!(error); + } + if let Err(error) = translate_expr(program, select, &delimiter_expr, delimiter_reg) { + anyhow::bail!(error); + } program.emit_insn(Insn::AggStep { acc_reg: target_register, @@ -793,7 +797,7 @@ fn translate_aggregation( match &args[1] { ast::Expr::Id(ident) => { if ident.0.starts_with("\"") { - delimiter_expr = ast::Expr::Literal(Literal::String(ident.0.to_string())); + anyhow::bail!("Parse error: no such column: \",\" - should this be a string literal in single-quotes?"); } else { delimiter_expr = args[1].clone(); } @@ -804,8 +808,12 @@ fn translate_aggregation( _ => anyhow::bail!("Incorrect delimiter parameter"), }; - let _ = translate_expr(program, select, expr, expr_reg); - let _ = translate_expr(program, select, &delimiter_expr, delimiter_reg); + if let Err(error) = translate_expr(program, select, expr, expr_reg) { + anyhow::bail!(error); + } + if let Err(error) = translate_expr(program, select, &delimiter_expr, delimiter_reg) { + anyhow::bail!(error); + } program.emit_insn(Insn::AggStep { acc_reg: target_register, From 0f7c88f5ad6c03e908a6e4d22e4f4cc8e0e63204 Mon Sep 17 00:00:00 2001 From: Ramkarthik Krishnamurthy Date: Sat, 13 Jul 2024 14:49:18 +0530 Subject: [PATCH 6/7] Fixes truncation of trailing zeros when converting float to string --- core/types.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/types.rs b/core/types.rs index 734e3cc6a..e737b1278 100644 --- a/core/types.rs +++ b/core/types.rs @@ -40,7 +40,7 @@ impl Display for OwnedValue { match self { OwnedValue::Null => write!(f, "NULL"), OwnedValue::Integer(i) => write!(f, "{}", i), - OwnedValue::Float(fl) => write!(f, "{}", fl), + OwnedValue::Float(fl) => write!(f, "{:?}", fl), OwnedValue::Text(s) => write!(f, "{}", s), OwnedValue::Blob(b) => write!(f, "{:?}", b), OwnedValue::Agg(a) => match a.as_ref() { @@ -93,7 +93,8 @@ impl std::ops::Add for OwnedValue { OwnedValue::Text(Rc::new(int_left.to_string() + &string_right.to_string())) } (OwnedValue::Text(string_left), OwnedValue::Float(float_right)) => { - OwnedValue::Text(Rc::new(string_left.to_string() + &float_right.to_string())) + let text_right = OwnedValue::Float(float_right).to_string(); + OwnedValue::Text(Rc::new(string_left.to_string() + &text_right)) } (OwnedValue::Float(float_left), OwnedValue::Text(string_right)) => { OwnedValue::Text(Rc::new(float_left.to_string() + &string_right.to_string())) From 5dff56d18a259c8088d1506179a7dbda89c388c1 Mon Sep 17 00:00:00 2001 From: Ramkarthik Krishnamurthy Date: Sat, 13 Jul 2024 14:53:16 +0530 Subject: [PATCH 7/7] Fixes it for both sides of the operator --- core/types.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/types.rs b/core/types.rs index e737b1278..30fdf6d85 100644 --- a/core/types.rs +++ b/core/types.rs @@ -93,11 +93,12 @@ impl std::ops::Add for OwnedValue { OwnedValue::Text(Rc::new(int_left.to_string() + &string_right.to_string())) } (OwnedValue::Text(string_left), OwnedValue::Float(float_right)) => { - let text_right = OwnedValue::Float(float_right).to_string(); - OwnedValue::Text(Rc::new(string_left.to_string() + &text_right)) + let string_right = OwnedValue::Float(float_right).to_string(); + OwnedValue::Text(Rc::new(string_left.to_string() + &string_right)) } (OwnedValue::Float(float_left), OwnedValue::Text(string_right)) => { - OwnedValue::Text(Rc::new(float_left.to_string() + &string_right.to_string())) + let string_left = OwnedValue::Float(float_left).to_string(); + OwnedValue::Text(Rc::new(string_left + &string_right.to_string())) } (lhs, OwnedValue::Null) => lhs, (OwnedValue::Null, rhs) => rhs,