diff --git a/core/schema.rs b/core/schema.rs index fbd3a86ec..ea6a26279 100644 --- a/core/schema.rs +++ b/core/schema.rs @@ -1,5 +1,5 @@ -use crate::VirtualTable; use crate::{util::normalize_ident, Result}; +use crate::{LimboError, VirtualTable}; use core::fmt; use fallible_iterator::FallibleIterator; use limbo_sqlite3_parser::ast::{Expr, Literal, SortOrder, TableOptions}; @@ -585,6 +585,20 @@ impl Affinity { Affinity::Numeric => SQLITE_AFF_NUMERIC, } } + + pub fn from_char(char: char) -> Result { + match char { + SQLITE_AFF_INTEGER => Ok(Affinity::Integer), + SQLITE_AFF_TEXT => Ok(Affinity::Text), + SQLITE_AFF_NONE => Ok(Affinity::Blob), + SQLITE_AFF_REAL => Ok(Affinity::Real), + SQLITE_AFF_NUMERIC => Ok(Affinity::Numeric), + _ => Err(LimboError::InternalError(format!( + "Invalid affinity character: {}", + char + ))), + } + } } impl fmt::Display for Type { diff --git a/core/vdbe/execute.rs b/core/vdbe/execute.rs index 7b151c093..66ced7233 100644 --- a/core/vdbe/execute.rs +++ b/core/vdbe/execute.rs @@ -4509,7 +4509,7 @@ pub fn op_not_found( return_if_io!(cursor.seek(SeekKey::IndexKey(&record), SeekOp::EQ)) } }; - + if found { state.pc += 1; } else { @@ -4519,6 +4519,40 @@ pub fn op_not_found( Ok(InsnFunctionStepResult::Step) } +pub fn op_affinity( + program: &Program, + state: &mut ProgramState, + insn: &Insn, + pager: &Rc, + mv_store: Option<&Rc>, +) -> Result { + let Insn::Affinity { + start_reg, + count, + affinities, + } = insn + else { + unreachable!("unexpected Insn {:?}", insn) + }; + + if affinities.len() != count.get() { + return Err(LimboError::InternalError( + "Affinity: the length of affinities does not match the count".into(), + )); + } + + for (i, affinity_char) in affinities.chars().enumerate().take(count.get()) { + let reg_index = *start_reg + i; + + let affinity = Affinity::from_char(affinity_char)?; + + apply_affinity_char(&mut state.registers[reg_index], affinity); + } + + state.pc += 1; + Ok(InsnFunctionStepResult::Step) +} + fn exec_lower(reg: &OwnedValue) -> Option { match reg { OwnedValue::Text(t) => Some(OwnedValue::build_text(&t.as_str().to_lowercase())), diff --git a/core/vdbe/explain.rs b/core/vdbe/explain.rs index 8149b4ba6..cbb546a11 100644 --- a/core/vdbe/explain.rs +++ b/core/vdbe/explain.rs @@ -1379,6 +1379,28 @@ pub fn insn_to_str( target_pc.to_debug_int() ), ), + Insn::Affinity { + start_reg, + count, + affinities, + } => ( + "Affinity", + *start_reg as i32, + count.get() as i32, + 0, + OwnedValue::build_text(""), + 0, + format!( + "r[{}..{}] = {}", + start_reg, + start_reg + count.get(), + affinities + .chars() + .map(|a| a.to_string()) + .collect::>() + .join(", ") + ), + ), }; format!( "{:<4} {:<17} {:<4} {:<4} {:<4} {:<13} {:<2} {}", diff --git a/core/vdbe/insn.rs b/core/vdbe/insn.rs index 8f1fbe580..32c3f9550 100644 --- a/core/vdbe/insn.rs +++ b/core/vdbe/insn.rs @@ -1,4 +1,7 @@ -use std::{num::NonZero, rc::Rc}; +use std::{ + num::{NonZero, NonZeroUsize}, + rc::Rc, +}; use super::{execute, AggFunc, BranchOffset, CursorID, FuncCtx, InsnFunction, PageIdx}; use crate::{ @@ -809,6 +812,12 @@ pub enum Insn { record_reg: usize, num_regs: usize, }, + /// Apply affinities to a range of registers. Affinities must have the same size of count + Affinity { + start_reg: usize, + count: NonZeroUsize, + affinities: String, + }, } impl Insn { @@ -924,6 +933,7 @@ impl Insn { Insn::OpenEphemeral { .. } | Insn::OpenAutoindex { .. } => execute::op_open_ephemeral, Insn::Once { .. } => execute::op_once, Insn::NotFound { .. } => execute::op_not_found, + Insn::Affinity { .. } => execute::op_affinity, } } }