Align MustBeInt logic with sqlite

This commit is contained in:
psvri
2025-01-08 22:22:04 +05:30
parent bdc06f2d66
commit 845de125db
2 changed files with 50 additions and 12 deletions

View File

@@ -2038,11 +2038,25 @@ impl Program {
state.pc += 1;
}
Insn::MustBeInt { reg } => {
match state.registers[*reg] {
match &state.registers[*reg] {
OwnedValue::Integer(_) => {}
OwnedValue::Float(f) => match cast_real_to_integer(*f) {
Ok(i) => state.registers[*reg] = OwnedValue::Integer(i),
Err(_) => crate::bail_parse_error!(
"MustBeInt: the value in register cannot be cast to integer"
),
},
OwnedValue::Text(text) => match checked_cast_text_to_numeric(&text.value) {
Ok(OwnedValue::Integer(i)) => {
state.registers[*reg] = OwnedValue::Integer(i)
}
_ => crate::bail_parse_error!(
"MustBeInt: the value in register cannot be cast to integer"
),
},
_ => {
crate::bail_parse_error!(
"MustBeInt: the value in the register is not an integer"
"MustBeInt: the value in register cannot be cast to integer"
);
}
};
@@ -3079,23 +3093,38 @@ fn cast_text_to_real(text: &str) -> OwnedValue {
/// 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.
fn cast_text_to_numeric(text: &str) -> OwnedValue {
fn checked_cast_text_to_numeric(text: &str) -> std::result::Result<OwnedValue, ()> {
if !text.contains('.') && !text.contains('e') && !text.contains('E') {
// Looks like an integer
if let Ok(i) = text.parse::<i64>() {
return OwnedValue::Integer(i);
return Ok(OwnedValue::Integer(i));
}
}
// Try as float
if let Ok(f) = text.parse::<f64>() {
// Check if can be losslessly converted to 51-bit integer
let i = f as i64;
if f == i as f64 && i.abs() < (1i64 << 51) {
return OwnedValue::Integer(i);
}
return OwnedValue::Float(f);
return match cast_real_to_integer(f) {
Ok(i) => Ok(OwnedValue::Integer(i)),
Err(_) => Ok(OwnedValue::Float(f)),
};
}
OwnedValue::Integer(0)
Err(())
}
// try casting to numeric if not possible return integer 0
fn cast_text_to_numeric(text: &str) -> OwnedValue {
match checked_cast_text_to_numeric(text) {
Ok(value) => value,
Err(_) => OwnedValue::Integer(0),
}
}
// Check if float can be losslessly converted to 51-bit integer
fn cast_real_to_integer(float: f64) -> std::result::Result<i64, ()> {
let i = float as i64;
if float == i as f64 && i.abs() < (1i64 << 51) {
return Ok(i);
}
Err(())
}
fn execute_sqlite_version(version_integer: i64) -> String {

View File

@@ -6,4 +6,13 @@ do_execsql_test_on_specific_db {:memory:} basic-insert {
create table temp (t1 integer, primary key (t1));
insert into temp values (1);
select * from temp;
} {1}
} {1}
do_execsql_test_on_specific_db {:memory:} must-be-int-insert {
create table temp (t1 integer, primary key (t1));
insert into temp values (1),(2.0),('3'),('4.0');
select * from temp;
} {1
2
3
4}