Fix op_must_be_int to use strict numeric cast

This commit is contained in:
bit-aloo
2025-10-18 15:05:25 +05:30
parent e8f009af27
commit 64bbca9e12
2 changed files with 16 additions and 7 deletions

View File

@@ -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<Value, ()> {
///
/// `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<Value, ()> {
// 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::<i64>() {
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

View File

@@ -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 {