From 64bbca9e1209dc27d857d2e858c47c182e62b35b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 18 Oct 2025 15:05:25 +0530 Subject: [PATCH] Fix op_must_be_int to use strict numeric cast --- core/util.rs | 12 ++++++++++-- core/vdbe/execute.rs | 11 ++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/core/util.rs b/core/util.rs index 77062fd7d..ef1a27d05 100644 --- a/core/util.rs +++ b/core/util.rs @@ -855,11 +855,19 @@ pub fn cast_text_to_real(text: &str) -> Value { /// IEEE 754 64-bit float and thus provides a 1-bit of margin for the text-to-float conversion operation.) /// Any text input that describes a value outside the range of a 64-bit signed integer yields a REAL result. /// Casting a REAL or INTEGER value to NUMERIC is a no-op, even if a real value could be losslessly converted to an integer. -pub fn checked_cast_text_to_numeric(text: &str) -> std::result::Result { +/// +/// `lossless`: If `true`, rejects the input if any characters remain after the numeric prefix (strict / exact conversion). +pub fn checked_cast_text_to_numeric(text: &str, lossless: bool) -> std::result::Result { // sqlite will parse the first N digits of a string to numeric value, then determine // whether _that_ value is more likely a real or integer value. e.g. // '-100234-2344.23e14' evaluates to -100234 instead of -100234.0 + let original_len = text.trim().len(); let (kind, text) = parse_numeric_str(text)?; + + if original_len != text.len() && lossless { + return Err(()); + } + match kind { ValueType::Integer => match text.parse::() { Ok(i) => Ok(Value::Integer(i)), @@ -940,7 +948,7 @@ fn parse_numeric_str(text: &str) -> Result<(ValueType, &str), ()> { } pub fn cast_text_to_numeric(txt: &str) -> Value { - checked_cast_text_to_numeric(txt).unwrap_or(Value::Integer(0)) + checked_cast_text_to_numeric(txt, false).unwrap_or(Value::Integer(0)) } // Check if float can be losslessly converted to 51-bit integer diff --git a/core/vdbe/execute.rs b/core/vdbe/execute.rs index 30ece2be8..33f7063d2 100644 --- a/core/vdbe/execute.rs +++ b/core/vdbe/execute.rs @@ -6699,11 +6699,12 @@ pub fn op_must_be_int( Ok(i) => state.registers[*reg] = Register::Value(Value::Integer(i)), Err(_) => crate::bail_parse_error!("datatype mismatch"), }, - Value::Text(text) => match checked_cast_text_to_numeric(text.as_str()) { + Value::Text(text) => match checked_cast_text_to_numeric(text.as_str(), true) { Ok(Value::Integer(i)) => state.registers[*reg] = Register::Value(Value::Integer(i)), - Ok(Value::Float(f)) => { - state.registers[*reg] = Register::Value(Value::Integer(f as i64)) - } + Ok(Value::Float(f)) => match cast_real_to_integer(f) { + Ok(i) => state.registers[*reg] = Register::Value(Value::Integer(i)), + Err(_) => crate::bail_parse_error!("datatype mismatch"), + }, _ => crate::bail_parse_error!("datatype mismatch"), }, _ => { @@ -9697,7 +9698,7 @@ fn apply_affinity_char(target: &mut Register, affinity: Affinity) -> bool { if s.starts_with("0x") { return false; } - if let Ok(num) = checked_cast_text_to_numeric(s) { + if let Ok(num) = checked_cast_text_to_numeric(s, false) { *value = num; return true; } else {