From 0ca6bbab055deded5d32e667659507cdd0222533 Mon Sep 17 00:00:00 2001 From: pedrocarlo Date: Tue, 18 Feb 2025 16:28:44 -0300 Subject: [PATCH 1/6] modified cast_text_to_number for more compatability with sqlite --- core/vdbe/insn.rs | 90 +++++++++++++++++++++------------------------- core/vdbe/mod.rs | 92 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 116 insertions(+), 66 deletions(-) diff --git a/core/vdbe/insn.rs b/core/vdbe/insn.rs index 6b04ef462..7b037ffb6 100644 --- a/core/vdbe/insn.rs +++ b/core/vdbe/insn.rs @@ -1,6 +1,6 @@ use std::num::NonZero; -use super::{AggFunc, BranchOffset, CursorID, FuncCtx, PageIdx}; +use super::{cast_text_to_numeric, AggFunc, BranchOffset, CursorID, FuncCtx, PageIdx}; use crate::storage::wal::CheckpointMode; use crate::types::{OwnedValue, Record}; use limbo_macros::Description; @@ -688,15 +688,9 @@ pub enum Cookie { UserVersion = 6, } -fn cast_text_to_numerical(value: &str) -> OwnedValue { - if let Ok(x) = value.parse::() { - OwnedValue::Integer(x) - } else if let Ok(x) = value.parse::() { - OwnedValue::Float(x) - } else { - OwnedValue::Integer(0) - } -} +// fn cast_text_to_numeric(value: &str) -> OwnedValue { +// cast_text_to_numeric(value) +// } pub fn exec_add(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue { if let OwnedValue::Agg(agg) = lhs { @@ -719,11 +713,11 @@ pub fn exec_add(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue { | (OwnedValue::Integer(i), OwnedValue::Float(f)) => OwnedValue::Float(*f + *i as f64), (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_add( - &cast_text_to_numerical(lhs.as_str()), - &cast_text_to_numerical(rhs.as_str()), + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), ), (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { - exec_add(&cast_text_to_numerical(text.as_str()), other) + exec_add(&cast_text_to_numeric(text.as_str()), other) } _ => todo!(), } @@ -750,14 +744,14 @@ pub fn exec_subtract(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue { (OwnedValue::Integer(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(*lhs as f64 - rhs), (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_subtract( - &cast_text_to_numerical(lhs.as_str()), - &cast_text_to_numerical(rhs.as_str()), + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), ), (OwnedValue::Text(text), other) => { - exec_subtract(&cast_text_to_numerical(text.as_str()), other) + exec_subtract(&cast_text_to_numeric(text.as_str()), other) } (other, OwnedValue::Text(text)) => { - exec_subtract(other, &cast_text_to_numerical(text.as_str())) + exec_subtract(other, &cast_text_to_numeric(text.as_str())) } _ => todo!(), } @@ -783,11 +777,11 @@ pub fn exec_multiply(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue { | (OwnedValue::Float(f), OwnedValue::Integer(i)) => OwnedValue::Float(*i as f64 * { *f }), (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_multiply( - &cast_text_to_numerical(lhs.as_str()), - &cast_text_to_numerical(rhs.as_str()), + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), ), (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { - exec_multiply(&cast_text_to_numerical(text.as_str()), other) + exec_multiply(&cast_text_to_numeric(text.as_str()), other) } _ => todo!(), @@ -816,15 +810,11 @@ pub fn exec_divide(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue { (OwnedValue::Integer(lhs), OwnedValue::Float(rhs)) => OwnedValue::Float(*lhs as f64 / rhs), (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_divide( - &cast_text_to_numerical(lhs.as_str()), - &cast_text_to_numerical(rhs.as_str()), + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), ), - (OwnedValue::Text(text), other) => { - exec_divide(&cast_text_to_numerical(text.as_str()), other) - } - (other, OwnedValue::Text(text)) => { - exec_divide(other, &cast_text_to_numerical(text.as_str())) - } + (OwnedValue::Text(text), other) => exec_divide(&cast_text_to_numeric(text.as_str()), other), + (other, OwnedValue::Text(text)) => exec_divide(other, &cast_text_to_numeric(text.as_str())), _ => todo!(), } } @@ -849,11 +839,11 @@ pub fn exec_bit_and(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue { (OwnedValue::Float(lh), OwnedValue::Integer(rh)) => OwnedValue::Integer(*lh as i64 & rh), (OwnedValue::Integer(lh), OwnedValue::Float(rh)) => OwnedValue::Integer(lh & *rh as i64), (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_bit_and( - &cast_text_to_numerical(lhs.as_str()), - &cast_text_to_numerical(rhs.as_str()), + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), ), (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { - exec_bit_and(&cast_text_to_numerical(text.as_str()), other) + exec_bit_and(&cast_text_to_numeric(text.as_str()), other) } _ => todo!(), } @@ -875,11 +865,11 @@ pub fn exec_bit_or(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue { OwnedValue::Integer(*lh as i64 | *rh as i64) } (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_bit_or( - &cast_text_to_numerical(lhs.as_str()), - &cast_text_to_numerical(rhs.as_str()), + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), ), (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { - exec_bit_or(&cast_text_to_numerical(text.as_str()), other) + exec_bit_or(&cast_text_to_numeric(text.as_str()), other) } _ => todo!(), } @@ -939,7 +929,7 @@ pub fn exec_bit_not(mut reg: &OwnedValue) -> OwnedValue { OwnedValue::Null => OwnedValue::Null, OwnedValue::Integer(i) => OwnedValue::Integer(!i), OwnedValue::Float(f) => OwnedValue::Integer(!(*f as i64)), - OwnedValue::Text(text) => exec_bit_not(&cast_text_to_numerical(text.as_str())), + OwnedValue::Text(text) => exec_bit_not(&cast_text_to_numeric(text.as_str())), _ => todo!(), } } @@ -966,14 +956,14 @@ pub fn exec_shift_left(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue OwnedValue::Integer(compute_shl(*lh as i64, *rh as i64)) } (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_shift_left( - &cast_text_to_numerical(lhs.as_str()), - &cast_text_to_numerical(rhs.as_str()), + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), ), (OwnedValue::Text(text), other) => { - exec_shift_left(&cast_text_to_numerical(text.as_str()), other) + exec_shift_left(&cast_text_to_numeric(text.as_str()), other) } (other, OwnedValue::Text(text)) => { - exec_shift_left(other, &cast_text_to_numerical(text.as_str())) + exec_shift_left(other, &cast_text_to_numeric(text.as_str())) } _ => todo!(), } @@ -1005,14 +995,14 @@ pub fn exec_shift_right(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValu OwnedValue::Integer(compute_shr(*lh as i64, *rh as i64)) } (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_shift_right( - &cast_text_to_numerical(lhs.as_str()), - &cast_text_to_numerical(rhs.as_str()), + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), ), (OwnedValue::Text(text), other) => { - exec_shift_right(&cast_text_to_numerical(text.as_str()), other) + exec_shift_right(&cast_text_to_numeric(text.as_str()), other) } (other, OwnedValue::Text(text)) => { - exec_shift_right(other, &cast_text_to_numerical(text.as_str())) + exec_shift_right(other, &cast_text_to_numeric(text.as_str())) } _ => todo!(), } @@ -1043,7 +1033,7 @@ pub fn exec_boolean_not(mut reg: &OwnedValue) -> OwnedValue { OwnedValue::Null => OwnedValue::Null, OwnedValue::Integer(i) => OwnedValue::Integer((*i == 0) as i64), OwnedValue::Float(f) => OwnedValue::Integer((*f == 0.0) as i64), - OwnedValue::Text(text) => exec_boolean_not(&cast_text_to_numerical(text.as_str())), + OwnedValue::Text(text) => exec_boolean_not(&cast_text_to_numeric(text.as_str())), _ => todo!(), } } @@ -1125,11 +1115,11 @@ pub fn exec_and(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue { | (OwnedValue::Float(0.0), _) => OwnedValue::Integer(0), (OwnedValue::Null, _) | (_, OwnedValue::Null) => OwnedValue::Null, (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_and( - &cast_text_to_numerical(lhs.as_str()), - &cast_text_to_numerical(rhs.as_str()), + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), ), (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { - exec_and(&cast_text_to_numerical(text.as_str()), other) + exec_and(&cast_text_to_numeric(text.as_str()), other) } _ => OwnedValue::Integer(1), } @@ -1154,11 +1144,11 @@ pub fn exec_or(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue { | (OwnedValue::Float(0.0), OwnedValue::Float(0.0)) | (OwnedValue::Integer(0), OwnedValue::Integer(0)) => OwnedValue::Integer(0), (OwnedValue::Text(lhs), OwnedValue::Text(rhs)) => exec_or( - &cast_text_to_numerical(lhs.as_str()), - &cast_text_to_numerical(rhs.as_str()), + &cast_text_to_numeric(lhs.as_str()), + &cast_text_to_numeric(rhs.as_str()), ), (OwnedValue::Text(text), other) | (other, OwnedValue::Text(text)) => { - exec_or(&cast_text_to_numerical(text.as_str()), other) + exec_or(&cast_text_to_numeric(text.as_str()), other) } _ => OwnedValue::Integer(1), } diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index a07145dc9..5c16c7f09 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -403,6 +403,28 @@ macro_rules! must_be_btree_cursor { }}; } +/// Reference: +/// https://github.com/sqlite/sqlite/blob/master/src/util.c#L798 +enum CastTextToIntResultCode { + NotInt = -1, + Success = 0, + ExcessSpace = 1, + #[allow(dead_code)] + TooLargeOrMalformed = 2, + #[allow(dead_code)] + SpecialCase = 3, +} + +/// Reference +/// https://github.com/sqlite/sqlite/blob/master/src/util.c#L529 +enum CastTextToRealResultCode { + PureInt = 1, + HasDecimal = 2, + NotValid = 0, + #[allow(dead_code)] + NotValidButPrefix = -1, +} + #[derive(Debug)] pub struct Program { pub max_registers: usize, @@ -3544,9 +3566,9 @@ fn exec_cast(value: &OwnedValue, datatype: &str) -> OwnedValue { OwnedValue::Blob(b) => { // Convert BLOB to TEXT first let text = String::from_utf8_lossy(b); - cast_text_to_real(&text) + cast_text_to_real(&text).0 } - OwnedValue::Text(t) => cast_text_to_real(t.as_str()), + OwnedValue::Text(t) => cast_text_to_real(t.as_str()).0, OwnedValue::Integer(i) => OwnedValue::Float(*i as f64), OwnedValue::Float(f) => OwnedValue::Float(*f), _ => OwnedValue::Float(0.0), @@ -3555,9 +3577,9 @@ fn exec_cast(value: &OwnedValue, datatype: &str) -> OwnedValue { OwnedValue::Blob(b) => { // Convert BLOB to TEXT first let text = String::from_utf8_lossy(b); - cast_text_to_integer(&text) + cast_text_to_integer(&text).0 } - OwnedValue::Text(t) => cast_text_to_integer(t.as_str()), + OwnedValue::Text(t) => cast_text_to_integer(t.as_str()).0, OwnedValue::Integer(i) => OwnedValue::Integer(*i), // A cast of a REAL value into an INTEGER results in the integer between the REAL value and zero // that is closest to the REAL value. If a REAL is greater than the greatest possible signed integer (+9223372036854775807) @@ -3629,48 +3651,68 @@ fn exec_replace(source: &OwnedValue, pattern: &OwnedValue, replacement: &OwnedVa /// When casting to INTEGER, if the text looks like a floating point value with an exponent, the exponent will be ignored /// because it is no part of the integer prefix. For example, "CAST('123e+5' AS INTEGER)" results in 123, not in 12300000. /// The CAST operator understands decimal integers only — conversion of hexadecimal integers stops at the "x" in the "0x" prefix of the hexadecimal integer string and thus result of the CAST is always zero. -fn cast_text_to_integer(text: &str) -> OwnedValue { +fn cast_text_to_integer(text: &str) -> (OwnedValue, CastTextToIntResultCode) { let text = text.trim(); if text.is_empty() { - return OwnedValue::Integer(0); + return (OwnedValue::Integer(0), CastTextToIntResultCode::NotInt); } if let Ok(i) = text.parse::() { - return OwnedValue::Integer(i); + // Compare if the text value has more characters that the number of digits + the sign in the parsed int + if i.to_string().len() < text.len() { + // Means it was probably casted from a real or some malformed number. + return (OwnedValue::Integer(i), CastTextToIntResultCode::ExcessSpace); + } + + return (OwnedValue::Integer(i), CastTextToIntResultCode::Success); } // Try to find longest valid prefix that parses as an integer // TODO: inefficient let mut end_index = text.len().saturating_sub(1) as isize; while end_index >= 0 { if let Ok(i) = text[..=end_index as usize].parse::() { - return OwnedValue::Integer(i); + // Compare if the text value has more characters that the number of digits + the sign in the parsed int + if i.to_string().len() < text.len() { + // Means it was probably casted from a real or some malformed number. + return (OwnedValue::Integer(i), CastTextToIntResultCode::ExcessSpace); + } + + return (OwnedValue::Integer(i), CastTextToIntResultCode::Success); } end_index -= 1; } - OwnedValue::Integer(0) + return (OwnedValue::Integer(0), CastTextToIntResultCode::NotInt); } /// When casting a TEXT value to REAL, the longest possible prefix of the value that can be interpreted /// as a real number is extracted from the TEXT value and the remainder ignored. Any leading spaces in /// the TEXT value are ignored when converging from TEXT to REAL. /// If there is no prefix that can be interpreted as a real number, the result of the conversion is 0.0. -fn cast_text_to_real(text: &str) -> OwnedValue { +fn cast_text_to_real(text: &str) -> (OwnedValue, CastTextToRealResultCode) { let trimmed = text.trim_start(); if trimmed.is_empty() { - return OwnedValue::Float(0.0); + return (OwnedValue::Float(0.0), CastTextToRealResultCode::NotValid); } if let Ok(num) = trimmed.parse::() { - return OwnedValue::Float(num); + if num.fract() == 0.0 { + return (OwnedValue::Float(num), CastTextToRealResultCode::PureInt); + } + + return (OwnedValue::Float(num), CastTextToRealResultCode::HasDecimal); } // Try to find longest valid prefix that parses as a float // TODO: inefficient let mut end_index = trimmed.len().saturating_sub(1) as isize; while end_index >= 0 { if let Ok(num) = trimmed[..=end_index as usize].parse::() { - return OwnedValue::Float(num); + if num.fract() == 0.0 { + return (OwnedValue::Float(num), CastTextToRealResultCode::PureInt); + } + + return (OwnedValue::Float(num), CastTextToRealResultCode::HasDecimal); } end_index -= 1; } - OwnedValue::Float(0.0) + return (OwnedValue::Float(0.0), CastTextToRealResultCode::NotValid); } /// NUMERIC Casting a TEXT or BLOB value into NUMERIC yields either an INTEGER or a REAL result. @@ -3700,9 +3742,27 @@ fn checked_cast_text_to_numeric(text: &str) -> std::result::Result OwnedValue { - checked_cast_text_to_numeric(text).unwrap_or(OwnedValue::Integer(0)) + let (real_cast, rc_real) = cast_text_to_real(text); + let (int_cast, rc_int) = cast_text_to_integer(text); + match (rc_real, rc_int) { + ( + CastTextToRealResultCode::NotValid, + CastTextToIntResultCode::ExcessSpace + | CastTextToIntResultCode::Success + | CastTextToIntResultCode::NotInt, + ) => int_cast, + (CastTextToRealResultCode::NotValidButPrefix, _) => real_cast, + ( + CastTextToRealResultCode::NotValid, + CastTextToIntResultCode::TooLargeOrMalformed | CastTextToIntResultCode::SpecialCase, + ) => real_cast, + (CastTextToRealResultCode::PureInt, CastTextToIntResultCode::Success) => int_cast, + // CastTextToRealResultCode::NotValid => (), + _ => real_cast, + } } // Check if float can be losslessly converted to 51-bit integer From 0c5696c26ae0a84aaff85821316d61b845ab8410 Mon Sep 17 00:00:00 2001 From: pedrocarlo Date: Tue, 18 Feb 2025 21:43:57 -0300 Subject: [PATCH 2/6] remove comment --- core/vdbe/insn.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/vdbe/insn.rs b/core/vdbe/insn.rs index 7b037ffb6..01b6b5469 100644 --- a/core/vdbe/insn.rs +++ b/core/vdbe/insn.rs @@ -688,10 +688,6 @@ pub enum Cookie { UserVersion = 6, } -// fn cast_text_to_numeric(value: &str) -> OwnedValue { -// cast_text_to_numeric(value) -// } - pub fn exec_add(mut lhs: &OwnedValue, mut rhs: &OwnedValue) -> OwnedValue { if let OwnedValue::Agg(agg) = lhs { lhs = agg.final_value(); From 409297cfddfbda07eaa3e914bbb8f8836ff9f639 Mon Sep 17 00:00:00 2001 From: pedrocarlo Date: Wed, 19 Feb 2025 00:05:56 -0300 Subject: [PATCH 3/6] remove comment --- core/vdbe/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 5c16c7f09..14214b0f0 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -3760,7 +3760,6 @@ fn cast_text_to_numeric(text: &str) -> OwnedValue { CastTextToIntResultCode::TooLargeOrMalformed | CastTextToIntResultCode::SpecialCase, ) => real_cast, (CastTextToRealResultCode::PureInt, CastTextToIntResultCode::Success) => int_cast, - // CastTextToRealResultCode::NotValid => (), _ => real_cast, } } From 033d0116d6ac251c0357e8edc3e9a0abc01dceaf Mon Sep 17 00:00:00 2001 From: pedrocarlo Date: Thu, 20 Feb 2025 02:16:30 -0300 Subject: [PATCH 4/6] rewrote parsing from text to integer and real --- core/util.rs | 335 +++++++++++++++++++++++++++++++++++++++++++++++ core/vdbe/mod.rs | 84 +----------- 2 files changed, 342 insertions(+), 77 deletions(-) diff --git a/core/util.rs b/core/util.rs index 2ee09189d..7a258c6e3 100644 --- a/core/util.rs +++ b/core/util.rs @@ -1,8 +1,10 @@ +use core::num::IntErrorKind; use limbo_sqlite3_parser::ast::{self, CreateTableBody, Expr, FunctionTail, Literal}; use std::{rc::Rc, sync::Arc}; use crate::{ schema::{self, Column, Schema, Type}, + types::OwnedValue, Result, Statement, StepResult, IO, }; @@ -380,6 +382,147 @@ pub fn columns_from_create_table_body(body: ast::CreateTableBody) -> Result>()) } +#[derive(Debug, PartialEq)] +/// Reference: +/// https://github.com/sqlite/sqlite/blob/master/src/util.c#L798 +pub enum CastTextToIntResultCode { + NotInt = -1, + Success = 0, + ExcessSpace = 1, + TooLargeOrMalformed = 2, + #[allow(dead_code)] + SpecialCase = 3, +} + +pub fn text_to_integer(text: &str) -> (OwnedValue, CastTextToIntResultCode) { + let text = text.trim(); + if text.is_empty() { + return (OwnedValue::Integer(0), CastTextToIntResultCode::NotInt); + } + let mut accum = String::new(); + let mut sign = false; + let mut has_digit = false; + let mut excess_space = false; + + let chars = text.chars(); + + for c in chars { + match c { + '0'..='9' => { + has_digit = true; + accum.push(c); + } + '+' | '-' if !has_digit && !sign => { + sign = true; + accum.push(c); + } + _ => { + excess_space = true; + break; + } + } + } + + match accum.parse::() { + Ok(num) => { + if excess_space { + return ( + OwnedValue::Integer(num), + CastTextToIntResultCode::ExcessSpace, + ); + } + + return (OwnedValue::Integer(num), CastTextToIntResultCode::Success); + } + Err(e) => match e.kind() { + IntErrorKind::NegOverflow | IntErrorKind::PosOverflow => ( + OwnedValue::Integer(0), + CastTextToIntResultCode::TooLargeOrMalformed, + ), + _ => (OwnedValue::Integer(0), CastTextToIntResultCode::NotInt), + }, + } +} + +#[derive(Debug, PartialEq)] +/// Reference +/// https://github.com/sqlite/sqlite/blob/master/src/util.c#L529 +pub enum CastTextToRealResultCode { + PureInt = 1, + HasDecimal = 2, + NotValid = 0, + NotValidButPrefix = -1, +} + +pub fn text_to_real(text: &str) -> (OwnedValue, CastTextToRealResultCode) { + let text = text.trim(); + if text.is_empty() { + return (OwnedValue::Float(0.0), CastTextToRealResultCode::NotValid); + } + let mut accum = String::new(); + let mut has_decimal_separator = false; + let mut sign = false; + let mut exp_sign = false; + let mut has_exponent = false; + let mut has_digit = false; + let mut has_decimal_digit = false; + let mut excess_space = false; + + let chars = text.chars(); + + for c in chars { + match c { + '0'..='9' if !has_decimal_separator => { + has_digit = true; + accum.push(c); + } + '0'..='9' => { + has_decimal_digit = true; + accum.push(c); + } + '+' | '-' if !has_digit && !sign => { + sign = true; + accum.push(c); + } + '+' | '-' if has_exponent && !exp_sign => { + exp_sign = true; + accum.push(c); + } + '.' if !has_decimal_separator => { + has_decimal_separator = true; + accum.push(c); + } + 'E' | 'e' if !has_decimal_separator || has_decimal_digit => { + has_exponent = true; + accum.push(c); + } + _ => { + excess_space = true; + break; + } + } + } + + if let Ok(num) = accum.parse::() { + if !has_decimal_separator && !exp_sign && !has_exponent { + return (OwnedValue::Float(num), CastTextToRealResultCode::PureInt); + } + + if excess_space { + // TODO see if this branch satisfies: not a valid number, but has a valid prefix which + // includes a decimal point and/or an eNNN clause + return ( + OwnedValue::Float(num), + CastTextToRealResultCode::NotValidButPrefix, + ); + } + + return (OwnedValue::Float(num), CastTextToRealResultCode::HasDecimal); + } + + return (OwnedValue::Float(0.0), CastTextToRealResultCode::NotValid); +} + #[cfg(test)] pub mod tests { use super::*; @@ -635,4 +778,196 @@ pub mod tests { assert!(!check_ident_equivalency("\"foo\"", "[bar]")); assert!(!check_ident_equivalency("foo", "\"bar\"")); } + + #[test] + fn test_text_to_integer() { + let pairs = vec![ + ( + text_to_integer("1"), + (OwnedValue::Integer(1), CastTextToIntResultCode::Success), + ), + ( + text_to_integer("-1"), + (OwnedValue::Integer(-1), CastTextToIntResultCode::Success), + ), + ( + text_to_integer("10000000"), + ( + OwnedValue::Integer(10000000), + CastTextToIntResultCode::Success, + ), + ), + ( + text_to_integer("-10000000"), + ( + OwnedValue::Integer(-10000000), + CastTextToIntResultCode::Success, + ), + ), + ( + text_to_integer("xxx"), + (OwnedValue::Integer(0), CastTextToIntResultCode::NotInt), + ), + ( + text_to_integer("123xxx"), + ( + OwnedValue::Integer(123), + CastTextToIntResultCode::ExcessSpace, + ), + ), + ( + text_to_integer("9223372036854775807"), + ( + OwnedValue::Integer(i64::MAX), + CastTextToIntResultCode::Success, + ), + ), + ( + text_to_integer("9223372036854775808"), + ( + OwnedValue::Integer(0), + CastTextToIntResultCode::TooLargeOrMalformed, + ), + ), + ( + text_to_integer("-9223372036854775808"), + ( + OwnedValue::Integer(i64::MIN), + CastTextToIntResultCode::Success, + ), + ), + ( + text_to_integer("-9223372036854775809"), + ( + OwnedValue::Integer(0), + CastTextToIntResultCode::TooLargeOrMalformed, + ), + ), + ]; + + for (left, right) in pairs { + assert_eq!(left, right); + } + } + + #[test] + fn test_text_to_real() { + let pairs = vec![ + ( + text_to_real("1"), + (OwnedValue::Float(1.0), CastTextToRealResultCode::PureInt), + ), + ( + text_to_real("-1"), + (OwnedValue::Float(-1.0), CastTextToRealResultCode::PureInt), + ), + ( + text_to_real("1.0"), + (OwnedValue::Float(1.0), CastTextToRealResultCode::HasDecimal), + ), + ( + text_to_real("-1.0"), + ( + OwnedValue::Float(-1.0), + CastTextToRealResultCode::HasDecimal, + ), + ), + ( + text_to_real("1e10"), + ( + OwnedValue::Float(1e10), + CastTextToRealResultCode::HasDecimal, + ), + ), + ( + text_to_real("-1e10"), + ( + OwnedValue::Float(-1e10), + CastTextToRealResultCode::HasDecimal, + ), + ), + ( + text_to_real("1e-10"), + ( + OwnedValue::Float(1e-10), + CastTextToRealResultCode::HasDecimal, + ), + ), + ( + text_to_real("-1e-10"), + ( + OwnedValue::Float(-1e-10), + CastTextToRealResultCode::HasDecimal, + ), + ), + ( + text_to_real("1.123e10"), + ( + OwnedValue::Float(1.123e10), + CastTextToRealResultCode::HasDecimal, + ), + ), + ( + text_to_real("-1.123e10"), + ( + OwnedValue::Float(-1.123e10), + CastTextToRealResultCode::HasDecimal, + ), + ), + ( + text_to_real("1.123e-10"), + ( + OwnedValue::Float(1.123e-10), + CastTextToRealResultCode::HasDecimal, + ), + ), + ( + text_to_real("-1.123e-10"), + ( + OwnedValue::Float(-1.123e-10), + CastTextToRealResultCode::HasDecimal, + ), + ), + ( + text_to_real("1-282584294928"), + (OwnedValue::Float(1.0), CastTextToRealResultCode::PureInt), + ), + ( + text_to_real("xxx"), + (OwnedValue::Float(0.0), CastTextToRealResultCode::NotValid), + ), + ( + text_to_real("1.7976931348623157e308"), + ( + OwnedValue::Float(f64::MAX), + CastTextToRealResultCode::HasDecimal, + ), + ), + ( + text_to_real("1.7976931348623157e309"), + ( + OwnedValue::Float(f64::INFINITY), + CastTextToRealResultCode::HasDecimal, + ), + ), + ( + text_to_real("-1.7976931348623157e308"), + ( + OwnedValue::Float(f64::MIN), + CastTextToRealResultCode::HasDecimal, + ), + ), + ( + text_to_real("-1.7976931348623157e309"), + ( + OwnedValue::Float(f64::NEG_INFINITY), + CastTextToRealResultCode::HasDecimal, + ), + ), + ]; + + for (left, right) in pairs { + assert_eq!(left, right); + } + } } diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 14214b0f0..25f7cb6ac 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -41,7 +41,10 @@ use crate::translate::plan::{ResultSetColumn, TableReference}; use crate::types::{ AggContext, Cursor, CursorResult, ExternalAggState, OwnedValue, Record, SeekKey, SeekOp, }; -use crate::util::parse_schema_rows; +use crate::util::{ + parse_schema_rows, text_to_integer, text_to_real, CastTextToIntResultCode, + CastTextToRealResultCode, +}; use crate::vdbe::builder::CursorType; use crate::vdbe::insn::Insn; use crate::vector::{vector32, vector64, vector_distance_cos, vector_extract}; @@ -403,28 +406,6 @@ macro_rules! must_be_btree_cursor { }}; } -/// Reference: -/// https://github.com/sqlite/sqlite/blob/master/src/util.c#L798 -enum CastTextToIntResultCode { - NotInt = -1, - Success = 0, - ExcessSpace = 1, - #[allow(dead_code)] - TooLargeOrMalformed = 2, - #[allow(dead_code)] - SpecialCase = 3, -} - -/// Reference -/// https://github.com/sqlite/sqlite/blob/master/src/util.c#L529 -enum CastTextToRealResultCode { - PureInt = 1, - HasDecimal = 2, - NotValid = 0, - #[allow(dead_code)] - NotValidButPrefix = -1, -} - #[derive(Debug)] pub struct Program { pub max_registers: usize, @@ -3652,35 +3633,7 @@ fn exec_replace(source: &OwnedValue, pattern: &OwnedValue, replacement: &OwnedVa /// because it is no part of the integer prefix. For example, "CAST('123e+5' AS INTEGER)" results in 123, not in 12300000. /// The CAST operator understands decimal integers only — conversion of hexadecimal integers stops at the "x" in the "0x" prefix of the hexadecimal integer string and thus result of the CAST is always zero. fn cast_text_to_integer(text: &str) -> (OwnedValue, CastTextToIntResultCode) { - let text = text.trim(); - if text.is_empty() { - return (OwnedValue::Integer(0), CastTextToIntResultCode::NotInt); - } - if let Ok(i) = text.parse::() { - // Compare if the text value has more characters that the number of digits + the sign in the parsed int - if i.to_string().len() < text.len() { - // Means it was probably casted from a real or some malformed number. - return (OwnedValue::Integer(i), CastTextToIntResultCode::ExcessSpace); - } - - return (OwnedValue::Integer(i), CastTextToIntResultCode::Success); - } - // Try to find longest valid prefix that parses as an integer - // TODO: inefficient - let mut end_index = text.len().saturating_sub(1) as isize; - while end_index >= 0 { - if let Ok(i) = text[..=end_index as usize].parse::() { - // Compare if the text value has more characters that the number of digits + the sign in the parsed int - if i.to_string().len() < text.len() { - // Means it was probably casted from a real or some malformed number. - return (OwnedValue::Integer(i), CastTextToIntResultCode::ExcessSpace); - } - - return (OwnedValue::Integer(i), CastTextToIntResultCode::Success); - } - end_index -= 1; - } - return (OwnedValue::Integer(0), CastTextToIntResultCode::NotInt); + text_to_integer(text) } /// When casting a TEXT value to REAL, the longest possible prefix of the value that can be interpreted @@ -3688,31 +3641,7 @@ fn cast_text_to_integer(text: &str) -> (OwnedValue, CastTextToIntResultCode) { /// the TEXT value are ignored when converging from TEXT to REAL. /// If there is no prefix that can be interpreted as a real number, the result of the conversion is 0.0. fn cast_text_to_real(text: &str) -> (OwnedValue, CastTextToRealResultCode) { - let trimmed = text.trim_start(); - if trimmed.is_empty() { - return (OwnedValue::Float(0.0), CastTextToRealResultCode::NotValid); - } - if let Ok(num) = trimmed.parse::() { - if num.fract() == 0.0 { - return (OwnedValue::Float(num), CastTextToRealResultCode::PureInt); - } - - return (OwnedValue::Float(num), CastTextToRealResultCode::HasDecimal); - } - // Try to find longest valid prefix that parses as a float - // TODO: inefficient - let mut end_index = trimmed.len().saturating_sub(1) as isize; - while end_index >= 0 { - if let Ok(num) = trimmed[..=end_index as usize].parse::() { - if num.fract() == 0.0 { - return (OwnedValue::Float(num), CastTextToRealResultCode::PureInt); - } - - return (OwnedValue::Float(num), CastTextToRealResultCode::HasDecimal); - } - end_index -= 1; - } - return (OwnedValue::Float(0.0), CastTextToRealResultCode::NotValid); + text_to_real(text) } /// NUMERIC Casting a TEXT or BLOB value into NUMERIC yields either an INTEGER or a REAL result. @@ -3747,6 +3676,7 @@ fn checked_cast_text_to_numeric(text: &str) -> std::result::Result OwnedValue { let (real_cast, rc_real) = cast_text_to_real(text); let (int_cast, rc_int) = cast_text_to_integer(text); + match (rc_real, rc_int) { ( CastTextToRealResultCode::NotValid, From 13639899a52a0d9de20343238462d645efa2df7a Mon Sep 17 00:00:00 2001 From: pedrocarlo Date: Thu, 20 Feb 2025 16:05:50 -0300 Subject: [PATCH 5/6] more adjustments to parser to handle edge cases --- core/util.rs | 407 ++++++++++++++++++++++++++++++----------------- core/vdbe/mod.rs | 5 +- 2 files changed, 264 insertions(+), 148 deletions(-) diff --git a/core/util.rs b/core/util.rs index 7a258c6e3..64c01e5e9 100644 --- a/core/util.rs +++ b/core/util.rs @@ -468,15 +468,16 @@ pub fn text_to_real(text: &str) -> (OwnedValue, CastTextToRealResultCode) { let mut has_decimal_digit = false; let mut excess_space = false; - let chars = text.chars(); + let mut chars = text.chars(); - for c in chars { + 'outer: while let Some(c) = chars.next() { match c { '0'..='9' if !has_decimal_separator => { has_digit = true; accum.push(c); } '0'..='9' => { + // This pattern is used for both decimal and exponent digits has_decimal_digit = true; accum.push(c); } @@ -484,17 +485,53 @@ pub fn text_to_real(text: &str) -> (OwnedValue, CastTextToRealResultCode) { sign = true; accum.push(c); } - '+' | '-' if has_exponent && !exp_sign => { - exp_sign = true; - accum.push(c); - } '.' if !has_decimal_separator => { - has_decimal_separator = true; - accum.push(c); + // Check if next char is a number + if let Some(ch) = chars.next() { + match ch { + '0'..='9' => { + has_decimal_separator = true; + accum.push(c); + accum.push(ch); + } + _ => { + excess_space = true; + break; + } + } + } else { + excess_space = true; + } } - 'E' | 'e' if !has_decimal_separator || has_decimal_digit => { - has_exponent = true; - accum.push(c); + 'E' | 'e' if !has_exponent && (!has_decimal_separator || has_decimal_digit) => { + // Lookahead if next char is a number or sign + let mut curr_sign = None; + loop { + if let Some(ch) = chars.next() { + match ch { + '0'..='9' => { + has_exponent = true; + accum.push(c); + if let Some(sign) = curr_sign { + exp_sign = true; + accum.push(sign); + } + accum.push(ch); + break; + } + '+' | '-' => { + curr_sign = Some(ch); + } + _ => { + excess_space = true; + break 'outer; + } + } + } else { + excess_space = true; + break 'outer; + } + } } _ => { excess_space = true; @@ -503,8 +540,18 @@ pub fn text_to_real(text: &str) -> (OwnedValue, CastTextToRealResultCode) { } } + dbg!( + &has_decimal_separator, + &sign, + &exp_sign, + &has_exponent, + &has_digit, + &has_decimal_digit, + &excess_space + ); + if let Ok(num) = accum.parse::() { - if !has_decimal_separator && !exp_sign && !has_exponent { + if !has_decimal_separator && !exp_sign && !has_exponent && !excess_space { return (OwnedValue::Float(num), CastTextToRealResultCode::PureInt); } @@ -781,193 +828,261 @@ pub mod tests { #[test] fn test_text_to_integer() { - let pairs = vec![ + assert_eq!( + text_to_integer("1"), + (OwnedValue::Integer(1), CastTextToIntResultCode::Success), + ); + assert_eq!( + text_to_integer("-1"), + (OwnedValue::Integer(-1), CastTextToIntResultCode::Success), + ); + assert_eq!( + text_to_integer("10000000"), ( - text_to_integer("1"), - (OwnedValue::Integer(1), CastTextToIntResultCode::Success), + OwnedValue::Integer(10000000), + CastTextToIntResultCode::Success, ), + ); + assert_eq!( + text_to_integer("-10000000"), ( - text_to_integer("-1"), - (OwnedValue::Integer(-1), CastTextToIntResultCode::Success), + OwnedValue::Integer(-10000000), + CastTextToIntResultCode::Success, ), + ); + assert_eq!( + text_to_integer("xxx"), + (OwnedValue::Integer(0), CastTextToIntResultCode::NotInt), + ); + assert_eq!( + text_to_integer("123xxx"), ( - text_to_integer("10000000"), - ( - OwnedValue::Integer(10000000), - CastTextToIntResultCode::Success, - ), + OwnedValue::Integer(123), + CastTextToIntResultCode::ExcessSpace, ), + ); + assert_eq!( + text_to_integer("9223372036854775807"), ( - text_to_integer("-10000000"), - ( - OwnedValue::Integer(-10000000), - CastTextToIntResultCode::Success, - ), + OwnedValue::Integer(i64::MAX), + CastTextToIntResultCode::Success, ), + ); + assert_eq!( + text_to_integer("9223372036854775808"), ( - text_to_integer("xxx"), - (OwnedValue::Integer(0), CastTextToIntResultCode::NotInt), + OwnedValue::Integer(0), + CastTextToIntResultCode::TooLargeOrMalformed, ), + ); + assert_eq!( + text_to_integer("-9223372036854775808"), ( - text_to_integer("123xxx"), - ( - OwnedValue::Integer(123), - CastTextToIntResultCode::ExcessSpace, - ), + OwnedValue::Integer(i64::MIN), + CastTextToIntResultCode::Success, ), + ); + assert_eq!( + text_to_integer("-9223372036854775809"), ( - text_to_integer("9223372036854775807"), - ( - OwnedValue::Integer(i64::MAX), - CastTextToIntResultCode::Success, - ), + OwnedValue::Integer(0), + CastTextToIntResultCode::TooLargeOrMalformed, ), - ( - text_to_integer("9223372036854775808"), - ( - OwnedValue::Integer(0), - CastTextToIntResultCode::TooLargeOrMalformed, - ), - ), - ( - text_to_integer("-9223372036854775808"), - ( - OwnedValue::Integer(i64::MIN), - CastTextToIntResultCode::Success, - ), - ), - ( - text_to_integer("-9223372036854775809"), - ( - OwnedValue::Integer(0), - CastTextToIntResultCode::TooLargeOrMalformed, - ), - ), - ]; - - for (left, right) in pairs { - assert_eq!(left, right); - } + ); + assert_eq!( + text_to_integer("-"), + (OwnedValue::Integer(0), CastTextToIntResultCode::NotInt,), + ); } #[test] fn test_text_to_real() { - let pairs = vec![ + assert_eq!( + text_to_real("1"), + (OwnedValue::Float(1.0), CastTextToRealResultCode::PureInt), + ); + assert_eq!( + text_to_real("-1"), + (OwnedValue::Float(-1.0), CastTextToRealResultCode::PureInt), + ); + assert_eq!( + text_to_real("1.0"), + (OwnedValue::Float(1.0), CastTextToRealResultCode::HasDecimal), + ); + assert_eq!( + text_to_real("-1.0"), ( - text_to_real("1"), - (OwnedValue::Float(1.0), CastTextToRealResultCode::PureInt), + OwnedValue::Float(-1.0), + CastTextToRealResultCode::HasDecimal, ), + ); + assert_eq!( + text_to_real("1e10"), ( - text_to_real("-1"), - (OwnedValue::Float(-1.0), CastTextToRealResultCode::PureInt), + OwnedValue::Float(1e10), + CastTextToRealResultCode::HasDecimal, ), + ); + assert_eq!( + text_to_real("-1e10"), ( - text_to_real("1.0"), - (OwnedValue::Float(1.0), CastTextToRealResultCode::HasDecimal), + OwnedValue::Float(-1e10), + CastTextToRealResultCode::HasDecimal, ), + ); + assert_eq!( + text_to_real("1e-10"), ( - text_to_real("-1.0"), - ( - OwnedValue::Float(-1.0), - CastTextToRealResultCode::HasDecimal, - ), + OwnedValue::Float(1e-10), + CastTextToRealResultCode::HasDecimal, ), + ); + assert_eq!( + text_to_real("-1e-10"), ( - text_to_real("1e10"), - ( - OwnedValue::Float(1e10), - CastTextToRealResultCode::HasDecimal, - ), + OwnedValue::Float(-1e-10), + CastTextToRealResultCode::HasDecimal, ), + ); + assert_eq!( + text_to_real("1.123e10"), ( - text_to_real("-1e10"), - ( - OwnedValue::Float(-1e10), - CastTextToRealResultCode::HasDecimal, - ), + OwnedValue::Float(1.123e10), + CastTextToRealResultCode::HasDecimal, ), + ); + assert_eq!( + text_to_real("-1.123e10"), ( - text_to_real("1e-10"), - ( - OwnedValue::Float(1e-10), - CastTextToRealResultCode::HasDecimal, - ), + OwnedValue::Float(-1.123e10), + CastTextToRealResultCode::HasDecimal, ), + ); + assert_eq!( + text_to_real("1.123e-10"), ( - text_to_real("-1e-10"), - ( - OwnedValue::Float(-1e-10), - CastTextToRealResultCode::HasDecimal, - ), + OwnedValue::Float(1.123e-10), + CastTextToRealResultCode::HasDecimal, ), + ); + assert_eq!( + text_to_real("-1.123e-10"), ( - text_to_real("1.123e10"), - ( - OwnedValue::Float(1.123e10), - CastTextToRealResultCode::HasDecimal, - ), + OwnedValue::Float(-1.123e-10), + CastTextToRealResultCode::HasDecimal, ), + ); + assert_eq!( + text_to_real("1-282584294928"), ( - text_to_real("-1.123e10"), - ( - OwnedValue::Float(-1.123e10), - CastTextToRealResultCode::HasDecimal, - ), + OwnedValue::Float(1.0), + CastTextToRealResultCode::NotValidButPrefix ), + ); + assert_eq!( + text_to_real("xxx"), + (OwnedValue::Float(0.0), CastTextToRealResultCode::NotValid), + ); + assert_eq!( + text_to_real("1.7976931348623157e308"), ( - text_to_real("1.123e-10"), - ( - OwnedValue::Float(1.123e-10), - CastTextToRealResultCode::HasDecimal, - ), + OwnedValue::Float(f64::MAX), + CastTextToRealResultCode::HasDecimal, ), + ); + assert_eq!( + text_to_real("1.7976931348623157e309"), ( - text_to_real("-1.123e-10"), - ( - OwnedValue::Float(-1.123e-10), - CastTextToRealResultCode::HasDecimal, - ), + OwnedValue::Float(f64::INFINITY), + CastTextToRealResultCode::HasDecimal, ), + ); + assert_eq!( + text_to_real("-1.7976931348623157e308"), ( - text_to_real("1-282584294928"), - (OwnedValue::Float(1.0), CastTextToRealResultCode::PureInt), + OwnedValue::Float(f64::MIN), + CastTextToRealResultCode::HasDecimal, ), + ); + assert_eq!( + text_to_real("-1.7976931348623157e309"), ( - text_to_real("xxx"), - (OwnedValue::Float(0.0), CastTextToRealResultCode::NotValid), + OwnedValue::Float(f64::NEG_INFINITY), + CastTextToRealResultCode::HasDecimal, ), + ); + assert_eq!( + text_to_real("1E"), ( - text_to_real("1.7976931348623157e308"), - ( - OwnedValue::Float(f64::MAX), - CastTextToRealResultCode::HasDecimal, - ), + OwnedValue::Float(1.0), + CastTextToRealResultCode::NotValidButPrefix, ), + ); + assert_eq!( + text_to_real("1EE"), ( - text_to_real("1.7976931348623157e309"), - ( - OwnedValue::Float(f64::INFINITY), - CastTextToRealResultCode::HasDecimal, - ), + OwnedValue::Float(1.0), + CastTextToRealResultCode::NotValidButPrefix, ), + ); + assert_eq!( + text_to_real("-1E"), ( - text_to_real("-1.7976931348623157e308"), - ( - OwnedValue::Float(f64::MIN), - CastTextToRealResultCode::HasDecimal, - ), + OwnedValue::Float(-1.0), + CastTextToRealResultCode::NotValidButPrefix, ), + ); + assert_eq!( + text_to_real("1."), ( - text_to_real("-1.7976931348623157e309"), - ( - OwnedValue::Float(f64::NEG_INFINITY), - CastTextToRealResultCode::HasDecimal, - ), + OwnedValue::Float(1.0), + CastTextToRealResultCode::NotValidButPrefix, ), - ]; - - for (left, right) in pairs { - assert_eq!(left, right); - } + ); + assert_eq!( + text_to_real("-1."), + ( + OwnedValue::Float(-1.0), + CastTextToRealResultCode::NotValidButPrefix, + ), + ); + assert_eq!( + text_to_real("1.23E"), + ( + OwnedValue::Float(1.23), + CastTextToRealResultCode::NotValidButPrefix, + ), + ); + assert_eq!( + text_to_real("1.23E-"), + ( + OwnedValue::Float(1.23), + CastTextToRealResultCode::NotValidButPrefix, + ), + ); + assert_eq!( + text_to_real("0"), + (OwnedValue::Float(0.0), CastTextToRealResultCode::PureInt,), + ); + assert_eq!( + text_to_real("-0"), + (OwnedValue::Float(-0.0), CastTextToRealResultCode::PureInt,), + ); + assert_eq!( + text_to_real("-0"), + (OwnedValue::Float(0.0), CastTextToRealResultCode::PureInt,), + ); + assert_eq!( + text_to_real("-0.0"), + (OwnedValue::Float(0.0), CastTextToRealResultCode::HasDecimal,), + ); + assert_eq!( + text_to_real("0.0"), + (OwnedValue::Float(0.0), CastTextToRealResultCode::HasDecimal,), + ); + assert_eq!( + text_to_real("-"), + (OwnedValue::Float(0.0), CastTextToRealResultCode::NotValid,), + ); } } diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 25f7cb6ac..84a98ab83 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -3676,7 +3676,7 @@ fn checked_cast_text_to_numeric(text: &str) -> std::result::Result OwnedValue { let (real_cast, rc_real) = cast_text_to_real(text); let (int_cast, rc_int) = cast_text_to_integer(text); - + dbg!(&real_cast, &rc_real, &int_cast, &rc_int); match (rc_real, rc_int) { ( CastTextToRealResultCode::NotValid, @@ -3684,12 +3684,13 @@ fn cast_text_to_numeric(text: &str) -> OwnedValue { | CastTextToIntResultCode::Success | CastTextToIntResultCode::NotInt, ) => int_cast, - (CastTextToRealResultCode::NotValidButPrefix, _) => real_cast, ( CastTextToRealResultCode::NotValid, CastTextToIntResultCode::TooLargeOrMalformed | CastTextToIntResultCode::SpecialCase, ) => real_cast, + (CastTextToRealResultCode::NotValidButPrefix, _) => real_cast, (CastTextToRealResultCode::PureInt, CastTextToIntResultCode::Success) => int_cast, + (CastTextToRealResultCode::HasDecimal, _) => real_cast, _ => real_cast, } } From 2e38aa1d6b9020c596926c2c9878fdd61df38a72 Mon Sep 17 00:00:00 2001 From: pedrocarlo Date: Thu, 20 Feb 2025 16:09:39 -0300 Subject: [PATCH 6/6] remove dbg --- core/util.rs | 10 ---------- core/vdbe/mod.rs | 1 - 2 files changed, 11 deletions(-) diff --git a/core/util.rs b/core/util.rs index 64c01e5e9..c8dc143be 100644 --- a/core/util.rs +++ b/core/util.rs @@ -540,16 +540,6 @@ pub fn text_to_real(text: &str) -> (OwnedValue, CastTextToRealResultCode) { } } - dbg!( - &has_decimal_separator, - &sign, - &exp_sign, - &has_exponent, - &has_digit, - &has_decimal_digit, - &excess_space - ); - if let Ok(num) = accum.parse::() { if !has_decimal_separator && !exp_sign && !has_exponent && !excess_space { return (OwnedValue::Float(num), CastTextToRealResultCode::PureInt); diff --git a/core/vdbe/mod.rs b/core/vdbe/mod.rs index 84a98ab83..264ae770e 100644 --- a/core/vdbe/mod.rs +++ b/core/vdbe/mod.rs @@ -3676,7 +3676,6 @@ fn checked_cast_text_to_numeric(text: &str) -> std::result::Result OwnedValue { let (real_cast, rc_real) = cast_text_to_real(text); let (int_cast, rc_int) = cast_text_to_integer(text); - dbg!(&real_cast, &rc_real, &int_cast, &rc_int); match (rc_real, rc_int) { ( CastTextToRealResultCode::NotValid,