mirror of
https://github.com/aljazceru/turso.git
synced 2026-02-08 01:34:23 +01:00
Unify comparison function to reduce code duplication
This commit is contained in:
committed by
Krishna Vishal
parent
5837f7329f
commit
5a1da026e6
@@ -5,6 +5,7 @@ use crate::storage::database::FileMemoryStorage;
|
||||
use crate::storage::page_cache::DumbLruPageCache;
|
||||
use crate::storage::pager::CreateBTreeFlags;
|
||||
use crate::storage::wal::DummyWAL;
|
||||
use crate::translate::collate::CollationSeq;
|
||||
use crate::types::ImmutableRecord;
|
||||
use crate::{
|
||||
error::{LimboError, SQLITE_CONSTRAINT, SQLITE_CONSTRAINT_PRIMARYKEY},
|
||||
@@ -531,38 +532,176 @@ pub fn op_not_null(
|
||||
Ok(InsnFunctionStepResult::Step)
|
||||
}
|
||||
|
||||
pub fn op_eq(
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
enum ComparisonOp {
|
||||
Eq,
|
||||
Ne,
|
||||
Lt,
|
||||
Le,
|
||||
Gt,
|
||||
Ge,
|
||||
}
|
||||
|
||||
impl ComparisonOp {
|
||||
fn compare(&self, lhs: &Value, rhs: &Value, collation: &CollationSeq) -> bool {
|
||||
match (lhs, rhs) {
|
||||
(Value::Text(lhs_text), Value::Text(rhs_text)) => {
|
||||
let order = collation.compare_strings(lhs_text.as_str(), rhs_text.as_str());
|
||||
match self {
|
||||
ComparisonOp::Eq => order.is_eq(),
|
||||
ComparisonOp::Ne => order.is_ne(),
|
||||
ComparisonOp::Lt => order.is_lt(),
|
||||
ComparisonOp::Le => order.is_le(),
|
||||
ComparisonOp::Gt => order.is_gt(),
|
||||
ComparisonOp::Ge => order.is_ge(),
|
||||
}
|
||||
}
|
||||
(_, _) => match self {
|
||||
ComparisonOp::Eq => *lhs == *rhs,
|
||||
ComparisonOp::Ne => *lhs != *rhs,
|
||||
ComparisonOp::Lt => *lhs < *rhs,
|
||||
ComparisonOp::Le => *lhs <= *rhs,
|
||||
ComparisonOp::Gt => *lhs > *rhs,
|
||||
ComparisonOp::Ge => *lhs >= *rhs,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn compare_integers(&self, lhs: &Value, rhs: &Value) -> bool {
|
||||
match self {
|
||||
ComparisonOp::Eq => lhs == rhs,
|
||||
ComparisonOp::Ne => lhs != rhs,
|
||||
ComparisonOp::Lt => lhs < rhs,
|
||||
ComparisonOp::Le => lhs <= rhs,
|
||||
ComparisonOp::Gt => lhs > rhs,
|
||||
ComparisonOp::Ge => lhs >= rhs,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_nulls(&self, lhs: &Value, rhs: &Value, null_eq: bool, jump_if_null: bool) -> bool {
|
||||
match self {
|
||||
ComparisonOp::Eq => {
|
||||
let both_null = lhs == rhs;
|
||||
(null_eq && both_null) || (!null_eq && jump_if_null)
|
||||
}
|
||||
ComparisonOp::Ne => {
|
||||
let at_least_one_null = lhs != rhs;
|
||||
(null_eq && at_least_one_null) || (!null_eq && jump_if_null)
|
||||
}
|
||||
ComparisonOp::Lt | ComparisonOp::Le | ComparisonOp::Gt | ComparisonOp::Ge => {
|
||||
jump_if_null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn op_comparison(
|
||||
program: &Program,
|
||||
state: &mut ProgramState,
|
||||
insn: &Insn,
|
||||
pager: &Rc<Pager>,
|
||||
mv_store: Option<&Rc<MvStore>>,
|
||||
) -> Result<InsnFunctionStepResult> {
|
||||
let Insn::Eq {
|
||||
lhs,
|
||||
rhs,
|
||||
target_pc,
|
||||
flags,
|
||||
collation,
|
||||
} = insn
|
||||
else {
|
||||
unreachable!("unexpected Insn {:?}", insn)
|
||||
let (lhs, rhs, target_pc, flags, collation, op) = match insn {
|
||||
Insn::Eq {
|
||||
lhs,
|
||||
rhs,
|
||||
target_pc,
|
||||
flags,
|
||||
collation,
|
||||
} => (
|
||||
*lhs,
|
||||
*rhs,
|
||||
*target_pc,
|
||||
*flags,
|
||||
collation.unwrap_or_default(),
|
||||
ComparisonOp::Eq,
|
||||
),
|
||||
Insn::Ne {
|
||||
lhs,
|
||||
rhs,
|
||||
target_pc,
|
||||
flags,
|
||||
collation,
|
||||
} => (
|
||||
*lhs,
|
||||
*rhs,
|
||||
*target_pc,
|
||||
*flags,
|
||||
collation.unwrap_or_default(),
|
||||
ComparisonOp::Ne,
|
||||
),
|
||||
Insn::Lt {
|
||||
lhs,
|
||||
rhs,
|
||||
target_pc,
|
||||
flags,
|
||||
collation,
|
||||
} => (
|
||||
*lhs,
|
||||
*rhs,
|
||||
*target_pc,
|
||||
*flags,
|
||||
collation.unwrap_or_default(),
|
||||
ComparisonOp::Lt,
|
||||
),
|
||||
Insn::Le {
|
||||
lhs,
|
||||
rhs,
|
||||
target_pc,
|
||||
flags,
|
||||
collation,
|
||||
} => (
|
||||
*lhs,
|
||||
*rhs,
|
||||
*target_pc,
|
||||
*flags,
|
||||
collation.unwrap_or_default(),
|
||||
ComparisonOp::Le,
|
||||
),
|
||||
Insn::Gt {
|
||||
lhs,
|
||||
rhs,
|
||||
target_pc,
|
||||
flags,
|
||||
collation,
|
||||
} => (
|
||||
*lhs,
|
||||
*rhs,
|
||||
*target_pc,
|
||||
*flags,
|
||||
collation.unwrap_or_default(),
|
||||
ComparisonOp::Gt,
|
||||
),
|
||||
Insn::Ge {
|
||||
lhs,
|
||||
rhs,
|
||||
target_pc,
|
||||
flags,
|
||||
collation,
|
||||
} => (
|
||||
*lhs,
|
||||
*rhs,
|
||||
*target_pc,
|
||||
*flags,
|
||||
collation.unwrap_or_default(),
|
||||
ComparisonOp::Ge,
|
||||
),
|
||||
_ => unreachable!("unexpected Insn {:?}", insn),
|
||||
};
|
||||
|
||||
assert!(target_pc.is_offset());
|
||||
let lhs = *lhs;
|
||||
let rhs = *rhs;
|
||||
let target_pc = *target_pc;
|
||||
|
||||
let nulleq = flags.has_nulleq();
|
||||
let jump_if_null = flags.has_jump_if_null();
|
||||
let affinity = flags.get_affinity();
|
||||
let collation = collation.unwrap_or_default();
|
||||
|
||||
let lhs_value = state.registers[lhs].get_owned_value();
|
||||
let rhs_value = state.registers[rhs].get_owned_value();
|
||||
|
||||
// Fast path for integers
|
||||
if matches!(lhs_value, Value::Integer(_)) && matches!(rhs_value, Value::Integer(_)) {
|
||||
if lhs_value == rhs_value {
|
||||
if op.compare_integers(lhs_value, rhs_value) {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
@@ -572,8 +711,7 @@ pub fn op_eq(
|
||||
|
||||
// Handle NULL values
|
||||
if matches!(lhs_value, Value::Null) || matches!(rhs_value, Value::Null) {
|
||||
let cond = lhs_value == rhs_value; // Both NULL
|
||||
if (nulleq && cond) || (!nulleq && jump_if_null) {
|
||||
if op.handle_nulls(lhs_value, rhs_value, nulleq, jump_if_null) {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
@@ -587,6 +725,7 @@ pub fn op_eq(
|
||||
let mut lhs_converted = false;
|
||||
let mut rhs_converted = false;
|
||||
|
||||
// Apply affinity conversions
|
||||
match affinity {
|
||||
Affinity::Numeric | Affinity::Integer => {
|
||||
let lhs_is_text = matches!(lhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
@@ -640,696 +779,16 @@ pub fn op_eq(
|
||||
Affinity::Blob => {} // Do nothing for blob affinity.
|
||||
}
|
||||
|
||||
let should_jump = match (
|
||||
let should_jump = op.compare(
|
||||
lhs_temp_reg.get_owned_value(),
|
||||
rhs_temp_reg.get_owned_value(),
|
||||
) {
|
||||
(Value::Text(lhs_text), Value::Text(rhs_text)) => {
|
||||
let order = collation.compare_strings(lhs_text.as_str(), rhs_text.as_str());
|
||||
order.is_eq()
|
||||
}
|
||||
(lhs, rhs) => *lhs == *rhs,
|
||||
};
|
||||
&collation,
|
||||
);
|
||||
|
||||
if lhs_converted {
|
||||
state.registers[lhs] = lhs_temp_reg;
|
||||
}
|
||||
if rhs_converted {
|
||||
state.registers[rhs] = rhs_temp_reg;
|
||||
}
|
||||
|
||||
if should_jump {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
|
||||
Ok(InsnFunctionStepResult::Step)
|
||||
}
|
||||
|
||||
pub fn op_ne(
|
||||
program: &Program,
|
||||
state: &mut ProgramState,
|
||||
insn: &Insn,
|
||||
pager: &Rc<Pager>,
|
||||
mv_store: Option<&Rc<MvStore>>,
|
||||
) -> Result<InsnFunctionStepResult> {
|
||||
let Insn::Ne {
|
||||
lhs,
|
||||
rhs,
|
||||
target_pc,
|
||||
flags,
|
||||
collation,
|
||||
} = insn
|
||||
else {
|
||||
unreachable!("unexpected Insn {:?}", insn)
|
||||
};
|
||||
assert!(target_pc.is_offset());
|
||||
let lhs = *lhs;
|
||||
let rhs = *rhs;
|
||||
let target_pc = *target_pc;
|
||||
let nulleq = flags.has_nulleq();
|
||||
let jump_if_null = flags.has_jump_if_null();
|
||||
let affinity = flags.get_affinity();
|
||||
let collation = collation.unwrap_or_default();
|
||||
|
||||
let lhs_value = state.registers[lhs].get_owned_value();
|
||||
let rhs_value = state.registers[rhs].get_owned_value();
|
||||
|
||||
// Fast path for integers
|
||||
if matches!(lhs_value, Value::Integer(_)) && matches!(rhs_value, Value::Integer(_)) {
|
||||
if lhs_value != rhs_value {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
return Ok(InsnFunctionStepResult::Step);
|
||||
}
|
||||
|
||||
// Handle NULL values
|
||||
if matches!(lhs_value, Value::Null) || matches!(rhs_value, Value::Null) {
|
||||
let cond = lhs_value != rhs_value; // At least one NULL
|
||||
if (nulleq && cond) || (!nulleq && jump_if_null) {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
return Ok(InsnFunctionStepResult::Step);
|
||||
}
|
||||
|
||||
let mut lhs_temp_reg = state.registers[lhs].clone();
|
||||
let mut rhs_temp_reg = state.registers[rhs].clone();
|
||||
|
||||
let mut lhs_converted = false;
|
||||
let mut rhs_converted = false;
|
||||
|
||||
match affinity {
|
||||
Affinity::Numeric | Affinity::Integer => {
|
||||
let lhs_is_text = matches!(lhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
let rhs_is_text = matches!(rhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
|
||||
if lhs_is_text || rhs_is_text {
|
||||
if lhs_is_text {
|
||||
lhs_converted = apply_numeric_affinity(&mut lhs_temp_reg, false);
|
||||
}
|
||||
if rhs_is_text {
|
||||
rhs_converted = apply_numeric_affinity(&mut rhs_temp_reg, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Affinity::Text => {
|
||||
let lhs_is_text = matches!(lhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
let rhs_is_text = matches!(rhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
|
||||
if lhs_is_text || rhs_is_text {
|
||||
if is_numeric_value(&lhs_temp_reg) {
|
||||
lhs_converted = stringify_register(&mut lhs_temp_reg);
|
||||
}
|
||||
|
||||
if is_numeric_value(&rhs_temp_reg) {
|
||||
rhs_converted = stringify_register(&mut rhs_temp_reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Affinity::Real => {
|
||||
if matches!(lhs_temp_reg.get_owned_value(), Value::Text(_)) {
|
||||
lhs_converted = apply_numeric_affinity(&mut lhs_temp_reg, false);
|
||||
}
|
||||
|
||||
if matches!(rhs_temp_reg.get_owned_value(), Value::Text(_)) {
|
||||
rhs_converted = apply_numeric_affinity(&mut rhs_temp_reg, false);
|
||||
}
|
||||
|
||||
if let Value::Integer(i) = lhs_temp_reg.get_owned_value() {
|
||||
lhs_temp_reg = Register::Value(Value::Float(*i as f64));
|
||||
lhs_converted = true;
|
||||
}
|
||||
|
||||
if let Value::Integer(i) = rhs_temp_reg.get_owned_value() {
|
||||
rhs_temp_reg = Register::Value(Value::Float(*i as f64));
|
||||
rhs_converted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Affinity::Blob => {} // Do nothing for blob affinity.
|
||||
}
|
||||
|
||||
let should_jump = match (
|
||||
lhs_temp_reg.get_owned_value(),
|
||||
rhs_temp_reg.get_owned_value(),
|
||||
) {
|
||||
(Value::Text(lhs_text), Value::Text(rhs_text)) => {
|
||||
let order = collation.compare_strings(lhs_text.as_str(), rhs_text.as_str());
|
||||
order.is_ne()
|
||||
}
|
||||
(lhs, rhs) => *lhs != *rhs,
|
||||
};
|
||||
|
||||
if lhs_converted {
|
||||
state.registers[lhs] = lhs_temp_reg;
|
||||
}
|
||||
if rhs_converted {
|
||||
state.registers[rhs] = rhs_temp_reg;
|
||||
}
|
||||
|
||||
if should_jump {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
|
||||
Ok(InsnFunctionStepResult::Step)
|
||||
}
|
||||
|
||||
pub fn op_lt(
|
||||
program: &Program,
|
||||
state: &mut ProgramState,
|
||||
insn: &Insn,
|
||||
pager: &Rc<Pager>,
|
||||
mv_store: Option<&Rc<MvStore>>,
|
||||
) -> Result<InsnFunctionStepResult> {
|
||||
let Insn::Lt {
|
||||
lhs,
|
||||
rhs,
|
||||
target_pc,
|
||||
flags,
|
||||
collation,
|
||||
} = insn
|
||||
else {
|
||||
unreachable!("unexpected Insn {:?}", insn)
|
||||
};
|
||||
assert!(target_pc.is_offset());
|
||||
let lhs = *lhs;
|
||||
let rhs = *rhs;
|
||||
let target_pc = *target_pc;
|
||||
let jump_if_null = flags.has_jump_if_null();
|
||||
let affinity = flags.get_affinity();
|
||||
let collation = collation.unwrap_or_default();
|
||||
|
||||
let lhs_value = state.registers[lhs].get_owned_value();
|
||||
let rhs_value = state.registers[rhs].get_owned_value();
|
||||
|
||||
// Fast path for integers
|
||||
if matches!(lhs_value, Value::Integer(_)) && matches!(rhs_value, Value::Integer(_)) {
|
||||
if lhs_value < rhs_value {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
return Ok(InsnFunctionStepResult::Step);
|
||||
}
|
||||
|
||||
// Handle NULL values
|
||||
if matches!(lhs_value, Value::Null) || matches!(rhs_value, Value::Null) {
|
||||
if jump_if_null {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
return Ok(InsnFunctionStepResult::Step);
|
||||
}
|
||||
|
||||
let mut lhs_temp_reg = state.registers[lhs].clone();
|
||||
let mut rhs_temp_reg = state.registers[rhs].clone();
|
||||
|
||||
let mut lhs_converted = false;
|
||||
let mut rhs_converted = false;
|
||||
|
||||
match affinity {
|
||||
Affinity::Numeric | Affinity::Integer => {
|
||||
let lhs_is_text = matches!(lhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
let rhs_is_text = matches!(rhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
|
||||
if lhs_is_text || rhs_is_text {
|
||||
if lhs_is_text {
|
||||
lhs_converted = apply_numeric_affinity(&mut lhs_temp_reg, false);
|
||||
}
|
||||
if rhs_is_text {
|
||||
rhs_converted = apply_numeric_affinity(&mut rhs_temp_reg, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Affinity::Text => {
|
||||
let lhs_is_text = matches!(lhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
let rhs_is_text = matches!(rhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
|
||||
if lhs_is_text || rhs_is_text {
|
||||
if is_numeric_value(&lhs_temp_reg) {
|
||||
lhs_converted = stringify_register(&mut lhs_temp_reg);
|
||||
}
|
||||
|
||||
if is_numeric_value(&rhs_temp_reg) {
|
||||
rhs_converted = stringify_register(&mut rhs_temp_reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Affinity::Real => {
|
||||
if matches!(lhs_temp_reg.get_owned_value(), Value::Text(_)) {
|
||||
lhs_converted = apply_numeric_affinity(&mut lhs_temp_reg, false);
|
||||
}
|
||||
|
||||
if matches!(rhs_temp_reg.get_owned_value(), Value::Text(_)) {
|
||||
rhs_converted = apply_numeric_affinity(&mut rhs_temp_reg, false);
|
||||
}
|
||||
|
||||
if let Value::Integer(i) = lhs_temp_reg.get_owned_value() {
|
||||
lhs_temp_reg = Register::Value(Value::Float(*i as f64));
|
||||
lhs_converted = true;
|
||||
}
|
||||
|
||||
if let Value::Integer(i) = rhs_temp_reg.get_owned_value() {
|
||||
rhs_temp_reg = Register::Value(Value::Float(*i as f64));
|
||||
rhs_converted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Affinity::Blob => {} // Do nothing for blob affinity.
|
||||
}
|
||||
|
||||
let should_jump = match (
|
||||
lhs_temp_reg.get_owned_value(),
|
||||
rhs_temp_reg.get_owned_value(),
|
||||
) {
|
||||
(Value::Text(lhs_text), Value::Text(rhs_text)) => {
|
||||
let order = collation.compare_strings(lhs_text.as_str(), rhs_text.as_str());
|
||||
order.is_lt()
|
||||
}
|
||||
(lhs, rhs) => *lhs < *rhs,
|
||||
};
|
||||
|
||||
if lhs_converted {
|
||||
state.registers[lhs] = lhs_temp_reg;
|
||||
}
|
||||
if rhs_converted {
|
||||
state.registers[rhs] = rhs_temp_reg;
|
||||
}
|
||||
|
||||
if should_jump {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
|
||||
Ok(InsnFunctionStepResult::Step)
|
||||
}
|
||||
|
||||
pub fn op_le(
|
||||
program: &Program,
|
||||
state: &mut ProgramState,
|
||||
insn: &Insn,
|
||||
pager: &Rc<Pager>,
|
||||
mv_store: Option<&Rc<MvStore>>,
|
||||
) -> Result<InsnFunctionStepResult> {
|
||||
let Insn::Le {
|
||||
lhs,
|
||||
rhs,
|
||||
target_pc,
|
||||
flags,
|
||||
collation,
|
||||
} = insn
|
||||
else {
|
||||
unreachable!("unexpected Insn {:?}", insn)
|
||||
};
|
||||
assert!(target_pc.is_offset());
|
||||
let lhs = *lhs;
|
||||
let rhs = *rhs;
|
||||
let target_pc = *target_pc;
|
||||
let jump_if_null = flags.has_jump_if_null();
|
||||
let affinity = flags.get_affinity();
|
||||
let collation = collation.unwrap_or_default();
|
||||
|
||||
let lhs_value = state.registers[lhs].get_owned_value();
|
||||
let rhs_value = state.registers[rhs].get_owned_value();
|
||||
|
||||
// Fast path for integers
|
||||
if matches!(lhs_value, Value::Integer(_)) && matches!(rhs_value, Value::Integer(_)) {
|
||||
if lhs_value <= rhs_value {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
return Ok(InsnFunctionStepResult::Step);
|
||||
}
|
||||
|
||||
// Handle NULL values
|
||||
if matches!(lhs_value, Value::Null) || matches!(rhs_value, Value::Null) {
|
||||
if jump_if_null {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
return Ok(InsnFunctionStepResult::Step);
|
||||
}
|
||||
|
||||
let mut lhs_temp_reg = state.registers[lhs].clone();
|
||||
let mut rhs_temp_reg = state.registers[rhs].clone();
|
||||
|
||||
let mut lhs_converted = false;
|
||||
let mut rhs_converted = false;
|
||||
|
||||
match affinity {
|
||||
Affinity::Numeric | Affinity::Integer => {
|
||||
let lhs_is_text = matches!(lhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
let rhs_is_text = matches!(rhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
|
||||
if lhs_is_text || rhs_is_text {
|
||||
if lhs_is_text {
|
||||
lhs_converted = apply_numeric_affinity(&mut lhs_temp_reg, false);
|
||||
}
|
||||
if rhs_is_text {
|
||||
rhs_converted = apply_numeric_affinity(&mut rhs_temp_reg, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Affinity::Text => {
|
||||
let lhs_is_text = matches!(lhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
let rhs_is_text = matches!(rhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
|
||||
if lhs_is_text || rhs_is_text {
|
||||
if is_numeric_value(&lhs_temp_reg) {
|
||||
lhs_converted = stringify_register(&mut lhs_temp_reg);
|
||||
}
|
||||
|
||||
if is_numeric_value(&rhs_temp_reg) {
|
||||
rhs_converted = stringify_register(&mut rhs_temp_reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Affinity::Real => {
|
||||
if matches!(lhs_temp_reg.get_owned_value(), Value::Text(_)) {
|
||||
lhs_converted = apply_numeric_affinity(&mut lhs_temp_reg, false);
|
||||
}
|
||||
|
||||
if matches!(rhs_temp_reg.get_owned_value(), Value::Text(_)) {
|
||||
rhs_converted = apply_numeric_affinity(&mut rhs_temp_reg, false);
|
||||
}
|
||||
|
||||
if let Value::Integer(i) = lhs_temp_reg.get_owned_value() {
|
||||
lhs_temp_reg = Register::Value(Value::Float(*i as f64));
|
||||
lhs_converted = true;
|
||||
}
|
||||
|
||||
if let Value::Integer(i) = rhs_temp_reg.get_owned_value() {
|
||||
rhs_temp_reg = Register::Value(Value::Float(*i as f64));
|
||||
rhs_converted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Affinity::Blob => {} // Do nothing for blob affinity.
|
||||
}
|
||||
|
||||
let should_jump = match (
|
||||
lhs_temp_reg.get_owned_value(),
|
||||
rhs_temp_reg.get_owned_value(),
|
||||
) {
|
||||
(Value::Text(lhs_text), Value::Text(rhs_text)) => {
|
||||
let order = collation.compare_strings(lhs_text.as_str(), rhs_text.as_str());
|
||||
order.is_le()
|
||||
}
|
||||
(lhs, rhs) => *lhs <= *rhs,
|
||||
};
|
||||
|
||||
if lhs_converted {
|
||||
state.registers[lhs] = lhs_temp_reg;
|
||||
}
|
||||
if rhs_converted {
|
||||
state.registers[rhs] = rhs_temp_reg;
|
||||
}
|
||||
|
||||
if should_jump {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
|
||||
Ok(InsnFunctionStepResult::Step)
|
||||
}
|
||||
|
||||
pub fn op_gt(
|
||||
program: &Program,
|
||||
state: &mut ProgramState,
|
||||
insn: &Insn,
|
||||
pager: &Rc<Pager>,
|
||||
mv_store: Option<&Rc<MvStore>>,
|
||||
) -> Result<InsnFunctionStepResult> {
|
||||
let Insn::Gt {
|
||||
lhs,
|
||||
rhs,
|
||||
target_pc,
|
||||
flags,
|
||||
collation,
|
||||
} = insn
|
||||
else {
|
||||
unreachable!("unexpected Insn {:?}", insn)
|
||||
};
|
||||
assert!(target_pc.is_offset());
|
||||
let lhs = *lhs;
|
||||
let rhs = *rhs;
|
||||
let target_pc = *target_pc;
|
||||
let jump_if_null = flags.has_jump_if_null();
|
||||
let affinity = flags.get_affinity();
|
||||
let collation = collation.unwrap_or_default();
|
||||
|
||||
let lhs_value = state.registers[lhs].get_owned_value();
|
||||
let rhs_value = state.registers[rhs].get_owned_value();
|
||||
|
||||
// Fast path for integers
|
||||
if matches!(lhs_value, Value::Integer(_)) && matches!(rhs_value, Value::Integer(_)) {
|
||||
if lhs_value > rhs_value {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
return Ok(InsnFunctionStepResult::Step);
|
||||
}
|
||||
|
||||
// Handle NULL values
|
||||
if matches!(lhs_value, Value::Null) || matches!(rhs_value, Value::Null) {
|
||||
if jump_if_null {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
return Ok(InsnFunctionStepResult::Step);
|
||||
}
|
||||
|
||||
let mut lhs_temp_reg = state.registers[lhs].clone();
|
||||
let mut rhs_temp_reg = state.registers[rhs].clone();
|
||||
|
||||
let mut lhs_converted = false;
|
||||
let mut rhs_converted = false;
|
||||
|
||||
match affinity {
|
||||
Affinity::Numeric | Affinity::Integer => {
|
||||
let lhs_is_text = matches!(lhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
let rhs_is_text = matches!(rhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
|
||||
if lhs_is_text || rhs_is_text {
|
||||
if lhs_is_text {
|
||||
lhs_converted = apply_numeric_affinity(&mut lhs_temp_reg, false);
|
||||
}
|
||||
if rhs_is_text {
|
||||
rhs_converted = apply_numeric_affinity(&mut rhs_temp_reg, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Affinity::Text => {
|
||||
let lhs_is_text = matches!(lhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
let rhs_is_text = matches!(rhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
|
||||
if lhs_is_text || rhs_is_text {
|
||||
if is_numeric_value(&lhs_temp_reg) {
|
||||
lhs_converted = stringify_register(&mut lhs_temp_reg);
|
||||
}
|
||||
|
||||
if is_numeric_value(&rhs_temp_reg) {
|
||||
rhs_converted = stringify_register(&mut rhs_temp_reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Affinity::Real => {
|
||||
if matches!(lhs_temp_reg.get_owned_value(), Value::Text(_)) {
|
||||
lhs_converted = apply_numeric_affinity(&mut lhs_temp_reg, false);
|
||||
}
|
||||
if matches!(rhs_temp_reg.get_owned_value(), Value::Text(_)) {
|
||||
rhs_converted = apply_numeric_affinity(&mut rhs_temp_reg, false);
|
||||
}
|
||||
|
||||
if let Value::Integer(i) = lhs_temp_reg.get_owned_value() {
|
||||
lhs_temp_reg = Register::Value(Value::Float(*i as f64));
|
||||
lhs_converted = true;
|
||||
}
|
||||
|
||||
if let Value::Integer(i) = rhs_temp_reg.get_owned_value() {
|
||||
rhs_temp_reg = Register::Value(Value::Float(*i as f64));
|
||||
rhs_converted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Affinity::Blob => {} // Do nothing for blob affinity.
|
||||
}
|
||||
|
||||
let should_jump = match (
|
||||
lhs_temp_reg.get_owned_value(),
|
||||
rhs_temp_reg.get_owned_value(),
|
||||
) {
|
||||
(Value::Text(lhs_text), Value::Text(rhs_text)) => {
|
||||
let order = collation.compare_strings(lhs_text.as_str(), rhs_text.as_str());
|
||||
order.is_gt()
|
||||
}
|
||||
(lhs, rhs) => *lhs > *rhs,
|
||||
};
|
||||
|
||||
if lhs_converted {
|
||||
state.registers[lhs] = lhs_temp_reg;
|
||||
}
|
||||
if rhs_converted {
|
||||
state.registers[rhs] = rhs_temp_reg;
|
||||
}
|
||||
|
||||
if should_jump {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
Ok(InsnFunctionStepResult::Step)
|
||||
}
|
||||
|
||||
pub fn op_ge(
|
||||
program: &Program,
|
||||
state: &mut ProgramState,
|
||||
insn: &Insn,
|
||||
pager: &Rc<Pager>,
|
||||
mv_store: Option<&Rc<MvStore>>,
|
||||
) -> Result<InsnFunctionStepResult> {
|
||||
let Insn::Ge {
|
||||
lhs,
|
||||
rhs,
|
||||
target_pc,
|
||||
flags,
|
||||
collation,
|
||||
} = insn
|
||||
else {
|
||||
unreachable!("unexpected Insn: {:?}", insn)
|
||||
};
|
||||
|
||||
assert!(target_pc.is_offset());
|
||||
|
||||
let lhs = *lhs;
|
||||
let rhs = *rhs;
|
||||
let target_pc = *target_pc;
|
||||
let jump_if_null = flags.has_jump_if_null();
|
||||
let affinity = flags.get_affinity();
|
||||
let collation = collation.unwrap_or_default();
|
||||
|
||||
let lhs_value = state.registers[lhs].get_owned_value();
|
||||
let rhs_value = state.registers[rhs].get_owned_value();
|
||||
|
||||
if matches!(lhs_value, Value::Integer(_)) && matches!(rhs_value, Value::Integer(_)) {
|
||||
if lhs_value >= rhs_value {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
return Ok(InsnFunctionStepResult::Step);
|
||||
}
|
||||
|
||||
if matches!(lhs_value, Value::Null) || matches!(rhs_value, Value::Null) {
|
||||
if jump_if_null {
|
||||
state.pc = target_pc.to_offset_int();
|
||||
} else {
|
||||
state.pc += 1;
|
||||
}
|
||||
return Ok(InsnFunctionStepResult::Step);
|
||||
}
|
||||
|
||||
let mut lhs_temp_reg = state.registers[lhs].clone();
|
||||
let mut rhs_temp_reg = state.registers[rhs].clone();
|
||||
|
||||
let mut lhs_converted = false;
|
||||
let mut rhs_converted = false;
|
||||
|
||||
match affinity {
|
||||
Affinity::Numeric | Affinity::Integer => {
|
||||
let lhs_is_text = matches!(lhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
let rhs_is_text = matches!(rhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
|
||||
if lhs_is_text || rhs_is_text {
|
||||
if lhs_is_text {
|
||||
lhs_converted = apply_numeric_affinity(&mut lhs_temp_reg, false);
|
||||
}
|
||||
if rhs_is_text {
|
||||
rhs_converted = apply_numeric_affinity(&mut rhs_temp_reg, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Affinity::Text => {
|
||||
let lhs_is_text = matches!(lhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
let rhs_is_text = matches!(rhs_temp_reg.get_owned_value(), Value::Text(_));
|
||||
|
||||
if lhs_is_text || rhs_is_text {
|
||||
if is_numeric_value(&lhs_temp_reg) {
|
||||
lhs_converted = stringify_register(&mut lhs_temp_reg);
|
||||
}
|
||||
|
||||
if is_numeric_value(&rhs_temp_reg) {
|
||||
rhs_converted = stringify_register(&mut rhs_temp_reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Affinity::Real => {
|
||||
if matches!(lhs_temp_reg.get_owned_value(), Value::Text(_)) {
|
||||
lhs_converted = apply_numeric_affinity(&mut lhs_temp_reg, false);
|
||||
}
|
||||
if matches!(rhs_temp_reg.get_owned_value(), Value::Text(_)) {
|
||||
rhs_converted = apply_numeric_affinity(&mut rhs_temp_reg, false);
|
||||
}
|
||||
|
||||
if let Value::Integer(i) = lhs_temp_reg.get_owned_value() {
|
||||
lhs_temp_reg = Register::Value(Value::Float(*i as f64));
|
||||
lhs_converted = true;
|
||||
}
|
||||
|
||||
if let Value::Integer(i) = rhs_temp_reg.get_owned_value() {
|
||||
rhs_temp_reg = Register::Value(Value::Float(*i as f64));
|
||||
rhs_converted = true;
|
||||
}
|
||||
}
|
||||
|
||||
Affinity::Blob => {} // Do nothing for blob affinity.
|
||||
}
|
||||
|
||||
let should_jump = match (
|
||||
lhs_temp_reg.get_owned_value(),
|
||||
rhs_temp_reg.get_owned_value(),
|
||||
) {
|
||||
(Value::Text(lhs_text), Value::Text(rhs_text)) => {
|
||||
let order = collation.compare_strings(lhs_text.as_str(), rhs_text.as_str());
|
||||
order.is_ge()
|
||||
}
|
||||
|
||||
(lhs, rhs) => {
|
||||
if *lhs >= *rhs {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if lhs_converted {
|
||||
state.registers[lhs] = lhs_temp_reg;
|
||||
}
|
||||
if rhs_converted {
|
||||
state.registers[rhs] = rhs_temp_reg;
|
||||
}
|
||||
|
||||
@@ -951,12 +951,12 @@ impl Insn {
|
||||
Insn::Move { .. } => execute::op_move,
|
||||
Insn::IfPos { .. } => execute::op_if_pos,
|
||||
Insn::NotNull { .. } => execute::op_not_null,
|
||||
Insn::Eq { .. } => execute::op_eq,
|
||||
Insn::Ne { .. } => execute::op_ne,
|
||||
Insn::Lt { .. } => execute::op_lt,
|
||||
Insn::Le { .. } => execute::op_le,
|
||||
Insn::Gt { .. } => execute::op_gt,
|
||||
Insn::Ge { .. } => execute::op_ge,
|
||||
Insn::Eq { .. }
|
||||
| Insn::Ne { .. }
|
||||
| Insn::Lt { .. }
|
||||
| Insn::Le { .. }
|
||||
| Insn::Gt { .. }
|
||||
| Insn::Ge { .. } => execute::op_comparison,
|
||||
Insn::If { .. } => execute::op_if,
|
||||
Insn::IfNot { .. } => execute::op_if_not,
|
||||
Insn::OpenRead { .. } => execute::op_open_read,
|
||||
|
||||
Reference in New Issue
Block a user