//! VDBE bytecode interpreter — the fetch-execute engine.
//!
//! Takes a [`VdbeProgram`] (produced by codegen) and executes it instruction by
//! instruction. The engine maintains a register file (`Vec<SqliteValue>`) and
//! accumulates result rows emitted by `OP_ResultRow`.
//!
//! This implementation covers the core opcode set needed for expression
//! evaluation, control flow, arithmetic, comparison, and row output.
//! Cursor-based opcodes (OpenRead, Rewind, Next, Column, etc.) are stubbed
//! and will be wired to the B-tree layer in Phase 5.

#![allow(
    clippy::match_same_arms,
    clippy::inline_always,
    clippy::unnecessary_wraps,
    clippy::option_option,
    clippy::useless_let_if_seq,
    clippy::collapsible_if
)]
use hashbrown::{HashMap, HashSet};
use std::any::Any;
use std::borrow::Cow;
#[cfg(test)]
use std::cell::Cell;
use std::cell::RefCell;
use std::cmp::Ordering;
use std::collections::{BTreeMap, BTreeSet, VecDeque};

use fsqlite_btree::swiss_index::SwissIndex;
use fsqlite_types::PageSize;
use std::rc::Rc;

/// bd-perf (V1.6): Flat array replacing HashMap for cursor storage.
/// Cursor IDs are small non-negative integers (0-15 typical). Direct array
/// indexing is ~10-20x faster than HashMap probing for every cursor access.
/// API matches SwissIndex<i32, V> so call sites need minimal changes.
#[derive(Debug)]
struct CursorSlots<V> {
    slots: Vec<Option<V>>,
}

impl<V> Default for CursorSlots<V> {
    fn default() -> Self {
        Self::new()
    }
}

#[allow(clippy::trivially_copy_pass_by_ref)]
impl<V> CursorSlots<V> {
    #[inline]
    fn new() -> Self {
        Self { slots: Vec::new() }
    }

    #[inline]
    fn ensure_slot(&mut self, id: i32) {
        // SAFETY FIX: Guard against negative IDs which would wrap to usize::MAX
        if id < 0 {
            return;
        }
        let idx = id as usize;
        if idx >= self.slots.len() {
            self.slots.resize_with(idx + 1, || None);
        }
    }

    #[inline]
    pub fn get(&self, id: &i32) -> Option<&V> {
        if *id < 0 {
            return None;
        }
        self.slots.get(*id as usize).and_then(Option::as_ref)
    }

    #[inline]
    pub fn get_mut(&mut self, id: &i32) -> Option<&mut V> {
        if *id < 0 {
            return None;
        }
        self.slots.get_mut(*id as usize).and_then(Option::as_mut)
    }

    #[inline]
    pub fn insert(&mut self, id: i32, value: V) -> Option<V> {
        if id < 0 {
            return None;
        }
        self.ensure_slot(id);
        self.slots[id as usize].replace(value)
    }

    #[inline]
    pub fn remove(&mut self, id: &i32) -> Option<V> {
        if *id < 0 {
            return None;
        }
        let idx = *id as usize;
        if idx < self.slots.len() {
            self.slots[idx].take()
        } else {
            None
        }
    }

    #[inline]
    pub fn contains_key(&self, id: &i32) -> bool {
        if *id < 0 {
            return false;
        }
        self.slots.get(*id as usize).is_some_and(Option::is_some)
    }

    #[inline]
    pub fn clear(&mut self) {
        for slot in &mut self.slots {
            *slot = None;
        }
    }

    #[inline]
    pub fn is_empty(&self) -> bool {
        self.slots.iter().all(Option::is_none)
    }

    pub fn values(&self) -> impl Iterator<Item = &V> {
        self.slots.iter().filter_map(Option::as_ref)
    }

    /// Iterate over occupied (key, &value) pairs. Keys are derived from slot indices.
    /// Used by diagnostic/parity-cert paths, not on the hot path.
    #[allow(clippy::cast_possible_wrap)]
    pub fn iter(&self) -> impl Iterator<Item = (i32, &V)> {
        self.slots
            .iter()
            .enumerate()
            .filter_map(|(i, slot)| slot.as_ref().map(|v| (i as i32, v)))
    }
}
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering as AtomicOrdering};
use std::sync::{Arc, LazyLock, Mutex};
use std::time::{Duration, Instant};

use fsqlite_btree::cursor::{CursorPositionStamp, FirstIndexKeyIntegerLocalRunSegment};
use fsqlite_btree::{
    BtCursor, BtreeCursorOps, BtreePageHeader, BtreePageType, MemPageStore, PageReader, PageWriter,
    SeekResult, header_offset_for_page,
};
use fsqlite_error::{ErrorCode, FrankenError, Result};
use fsqlite_func::collation::CollationRegistry;
use fsqlite_func::vtab::ColumnContext;
use fsqlite_func::{ErasedAggregateFunction, ErasedWindowFunction, FunctionRegistry};
use fsqlite_mvcc::ConcurrentPageState;
use fsqlite_mvcc::{
    AllocatorKey, CommitIndex, CommitLog, ConcurrentRowIdAllocator, InProcessPageLockTable,
    MvccError, SharedConcurrentHandle, TimeTravelSnapshot, TimeTravelTarget, VersionStore,
    concurrent_clear_page_state, concurrent_free_page, concurrent_has_page_state,
    concurrent_page_is_synthetic_conflict_only, concurrent_page_read_status, concurrent_page_state,
    concurrent_prepare_write_page, concurrent_restore_page_state,
    concurrent_stage_prepared_write_marker, concurrent_track_write_conflict_page,
    create_time_travel_snapshot,
};
#[cfg(test)]
use fsqlite_mvcc::{concurrent_read_page, concurrent_write_page};
use fsqlite_pager::{TransactionHandle, TransactionKind};
use fsqlite_types::cx::Cx;
use fsqlite_types::opcode::{Opcode, P4, VdbeOp};
use fsqlite_types::record::{
    ColumnOffset, PrecomputedSerialTypeKind, RecordProfileScope, enter_record_profile_scope,
    parse_record, record_iter_with_precomputed_header_exact_size, serialize_record,
    serialize_record_iter_with_precomputed_header_into,
    serialize_record_iter_with_precomputed_header_into_slice, simd_serialize_integer_record,
};
use fsqlite_types::serial_type::{
    SerialTypeClass, classify_serial_type, read_varint, serial_type_len,
};
use fsqlite_types::value::{
    SmallText, SqlLikeFastPathKind, SqliteValue, pool_return_reusable, sql_like_fast_path_matches,
};
use fsqlite_types::{
    CommitSeq, DATABASE_HEADER_SIZE, DatabaseHeader, PageData, PageNumber, RowId, RowIdMode,
    SchemaEpoch, StrictColumnType, TableId, WitnessKey,
};

use crate::{
    TableIndexMetaMap, VdbeProgram, enter_vdbe_decode_profile_stage,
    enter_vdbe_execute_profile_stage,
    jit::{
        CompiledProgram, CompiledRecordBuilder, ConstantResultRowTemplate, FullScanSelectTemplate,
        InsertValueSource, RowidLookupSelectTemplate, SimpleInsertTemplate, try_compile_program,
    },
    opcode_register_spans,
};

const VDBE_EXECUTION_CHECKPOINT_INTERVAL: u64 = 4096;
/// FrankenSQLite-specific p5 flag for `Insert`/`Delete` opcodes emitted from
/// UPDATE rewrites.
///
/// The low 4 bits of `Insert.p5` are reserved for OE_* conflict behavior in
/// this engine, so the UPDATE marker must live above them.
const OPFLAG_ISUPDATE: u16 = 0x10;
const STORAGE_CURSOR_LAYOUT_PREFIX_BYTES: usize = 256;
#[cfg(test)]
const VDBE_ENGINE_INLINE_SIZE_BUDGET_BYTES: usize = 3 * 1024;
static BUILTIN_COLLATION_REGISTRY: LazyLock<CollationRegistry> =
    LazyLock::new(CollationRegistry::default);

#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
struct StatementColdState(u16);

impl StatementColdState {
    const AGGREGATES: Self = Self(1 << 0);
    const CONFLICT_TRACKING: Self = Self(1 << 1);
    const ROWSETS: Self = Self(1 << 2);
    const SEQUENCE_COUNTERS: Self = Self(1 << 3);
    const VTAB_CURSORS: Self = Self(1 << 4);
    const WINDOW_CONTEXTS: Self = Self(1 << 5);
    const REGISTER_SUBTYPES: Self = Self(1 << 6);
    const BLOOM_FILTERS: Self = Self(1 << 7);

    #[must_use]
    const fn empty() -> Self {
        Self(0)
    }

    #[cfg(test)]
    #[must_use]
    #[allow(dead_code)]
    const fn contains(self, other: Self) -> bool {
        self.0 & other.0 != 0
    }

    #[must_use]
    const fn is_empty(self) -> bool {
        self.0 == 0
    }

    fn insert(&mut self, other: Self) {
        self.0 |= other.0;
    }

    fn clear(&mut self) {
        self.0 = 0;
    }
}

#[inline]
fn observe_execution_cancellation(cx: &Cx) -> Result<()> {
    cx.checkpoint().map_err(|_| FrankenError::Abort)
}

const MAKE_RECORD_FIXED_WIDTH_RESERVE_BYTES: usize = 9;
const MAKE_RECORD_VARIABLE_WIDTH_RESERVE_MIN: usize = 64;
const MAKE_RECORD_VARIABLE_WIDTH_RESERVE_MAX: usize = 512;

#[inline]
fn compute_record_header_size_hint(content_size: usize) -> usize {
    let mut header_size = content_size + 1;
    loop {
        let needed = fsqlite_types::serial_type::varint_len(header_size as u64) + content_size;
        if needed <= header_size {
            return header_size;
        }
        header_size = needed;
    }
}

#[inline]
fn make_record_variable_width_reserve(page_size: PageSize) -> usize {
    usize::try_from(page_size.get())
        .unwrap_or(fsqlite_types::limits::DEFAULT_PAGE_SIZE as usize)
        .div_ceil(16)
        .clamp(
            MAKE_RECORD_VARIABLE_WIDTH_RESERVE_MIN,
            MAKE_RECORD_VARIABLE_WIDTH_RESERVE_MAX,
        )
}

fn estimate_make_record_capacity_for_affinity(
    affinity: Option<&str>,
    n_cols: usize,
    variable_width_hint: usize,
) -> usize {
    let chars = affinity.map_or_else(
        || smallvec::SmallVec::<[char; 16]>::from_elem('C', n_cols),
        |aff| aff.chars().collect(),
    );
    let column_count = chars.len().max(n_cols);
    let mut header_content_size = 0usize;
    let mut body_size = 0usize;

    for idx in 0..column_count {
        match chars.get(idx).copied().unwrap_or('C') {
            'X' => {
                header_content_size += 1;
            }
            'C' | 'D' | 'E' => {
                header_content_size += 1;
                body_size += MAKE_RECORD_FIXED_WIDTH_RESERVE_BYTES;
            }
            _ => {
                header_content_size += 2;
                body_size += variable_width_hint;
            }
        }
    }

    compute_record_header_size_hint(header_content_size) + body_size
}

fn estimate_make_record_buffer_capacity(program: &VdbeProgram, page_size: PageSize) -> usize {
    let variable_width_hint = make_record_variable_width_reserve(page_size);
    program
        .ops()
        .iter()
        .filter(|op| op.opcode == Opcode::MakeRecord)
        .map(|op| -> usize {
            let n_cols = usize::try_from(op.p2.max(0)).unwrap_or(0);
            let capacity = match &op.p4 {
                P4::Affinity(aff) => estimate_make_record_capacity_for_affinity(
                    Some(aff.as_str()),
                    n_cols,
                    variable_width_hint,
                ),
                P4::PrecomputedHeader(header) => {
                    let body_size = header
                        .slots
                        .iter()
                        .map(|slot| match slot.kind {
                            PrecomputedSerialTypeKind::NullPlaceholder => 0,
                            PrecomputedSerialTypeKind::AnyOneByteVarintOrNull => {
                                MAKE_RECORD_VARIABLE_WIDTH_RESERVE_MIN
                            }
                            PrecomputedSerialTypeKind::IntegerOrNull
                            | PrecomputedSerialTypeKind::RealOrNull => {
                                MAKE_RECORD_FIXED_WIDTH_RESERVE_BYTES
                            }
                        })
                        .sum::<usize>();
                    header.template.len() + body_size
                }
                _ => estimate_make_record_capacity_for_affinity(None, n_cols, variable_width_hint),
            };
            capacity
        })
        .max()
        .unwrap_or(0)
}

#[inline]
fn vtab_exec_outcome(opcode: &str, err: FrankenError) -> Result<ExecOutcome> {
    if matches!(err, FrankenError::Abort) {
        return Err(FrankenError::Abort);
    }
    Ok(ExecOutcome::Error {
        code: 1,
        message: format!("{opcode} error: {err}"),
    })
}

#[inline]
fn duration_ns_saturating(duration: Duration) -> u64 {
    u64::try_from(duration.as_nanos()).unwrap_or(u64::MAX)
}

#[inline]
fn transient_mem_root_pgno(root_pgno: PageNumber) -> PageNumber {
    if root_pgno.get() == 1 {
        // MemPageStore pages do not include the 100-byte SQLite database
        // header, so synthetic cursors must avoid page 1.
        PageNumber::new(2).expect("page 2 must be a valid transient root page")
    } else {
        root_pgno
    }
}

#[inline]
fn add_vdbe_counter(counter: &AtomicU64, delta: u64) {
    if FSQLITE_VDBE_METRICS_ENABLED.load(AtomicOrdering::Relaxed) {
        counter.fetch_add(delta, AtomicOrdering::Relaxed);
    }
}

#[inline]
fn add_vdbe_counter_if(enabled: bool, counter: &AtomicU64, delta: u64) {
    if enabled {
        counter.fetch_add(delta, AtomicOrdering::Relaxed);
    }
}

#[inline]
fn add_vdbe_duration_if(enabled: bool, counter: &AtomicU64, started: Instant) {
    if enabled {
        counter.fetch_add(
            duration_ns_saturating(started.elapsed()),
            AtomicOrdering::Relaxed,
        );
    }
}

#[derive(Debug, Clone, Copy)]
struct BtreeCursorPageLayout {
    usable_size: u32,
    page_size: u32,
}

impl BtreeCursorPageLayout {
    const fn no_reserved_bytes(page_size: PageSize) -> Self {
        let page_size = page_size.get();
        Self {
            usable_size: page_size,
            page_size,
        }
    }
}

fn btree_cursor_page_layout_from_page_one<P: PageReader>(
    page_reader: &P,
    cx: &Cx,
) -> Option<BtreeCursorPageLayout> {
    let page_one = page_reader.read_page(cx, PageNumber::ONE).ok()?;
    let header_prefix: [u8; DATABASE_HEADER_SIZE] =
        page_one.get(..DATABASE_HEADER_SIZE)?.try_into().ok()?;
    let header = DatabaseHeader::from_bytes(&header_prefix).ok()?;
    let usable_size = header.page_size.usable(header.reserved_per_page);
    if usable_size <= 4 {
        return None;
    }
    Some(BtreeCursorPageLayout {
        usable_size,
        page_size: header.page_size.get(),
    })
}

fn btree_cursor_page_layout_for_reader_or_default<P: PageReader>(
    page_reader: &P,
    cx: &Cx,
    default_page_size: PageSize,
) -> BtreeCursorPageLayout {
    // Synthetic pager mocks used in unit tests often do not populate page 1
    // with a real SQLite database header. Fall back to the engine's configured
    // page size in that case, but prefer the real header whenever it exists so
    // transaction-backed cursors honor reserved bytes.
    btree_cursor_page_layout_from_page_one(page_reader, cx)
        .unwrap_or_else(|| BtreeCursorPageLayout::no_reserved_bytes(default_page_size))
}

fn configure_btree_cursor_page_size<P>(
    cursor: &mut BtCursor<P>,
    page_layout: BtreeCursorPageLayout,
) {
    if page_layout.page_size != page_layout.usable_size {
        cursor.set_page_size(page_layout.page_size);
    }
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct ReusableTableExecutionStateOutcome {
    pub metadata_rebind_count: u32,
    /// W1: Track function cache clears so callers can verify the optimization.
    pub function_cache_cleared: bool,
}

#[derive(Clone)]
#[allow(clippy::struct_excessive_bools)]
pub struct ReusableTableExecutionState {
    pub func_registry: Arc<FunctionRegistry>,
    pub collation_registry: Arc<Mutex<CollationRegistry>>,
    pub schema_cookie: u32,
    pub autoincrement_seq_by_root_page: HashMap<i32, i64>,
    pub rowid_alias_col_by_root_page: Arc<HashMap<i32, usize>>,
    pub table_column_count_by_root_page: Arc<HashMap<i32, usize>>,
    pub first_not_null_non_ipk_col_by_root_page: Arc<HashMap<i32, usize>>,
    pub column_defaults_by_root_page: Arc<HashMap<i32, Vec<Option<SqliteValue>>>>,
    pub index_desc_flags_by_root_page: Arc<HashMap<i32, Vec<bool>>>,
    pub index_collations_by_root_page: Arc<HashMap<i32, Vec<Option<String>>>>,
    pub reject_mem_fallback: bool,
    pub memdb_rows_loaded: bool,
    pub storage_cursor_memdb_count_shortcuts_safe: bool,
    pub version_store: Option<Arc<VersionStore>>,
    pub collect_result_rows: bool,
    pub max_collected_result_rows: Option<usize>,
}

// ── In-Memory Table Store ──────────────────────────────────────────────────
//
// Phase 4 in-memory cursor backend. Allows the VDBE engine to execute
// CREATE TABLE / INSERT / SELECT / UPDATE / DELETE against a lightweight
// row store without requiring the full B-tree + pager + VFS stack.

/// A row in an in-memory table: (rowid, column values).
pub type MemRowValues = Vec<SqliteValue>;

/// A row in an in-memory table: (rowid, column values).
#[derive(Debug, Clone, PartialEq)]
struct MemRow {
    rowid: i64,
    values: MemRowValues,
}

/// In-memory table storage (Phase 4 backend).
#[derive(Debug, Clone)]
struct UniqueConstraintState {
    columns: Vec<usize>,
    collations: Vec<Option<String>>,
    index: Option<BTreeMap<Vec<u8>, BTreeSet<i64>>>,
}

fn unique_constraint_collation_is_indexable(collation: Option<&str>) -> bool {
    match collation {
        None => true,
        Some(name)
            if name.eq_ignore_ascii_case("BINARY")
                || name.eq_ignore_ascii_case("NOCASE")
                || name.eq_ignore_ascii_case("RTRIM") =>
        {
            true
        }
        Some(_) => false,
    }
}

fn unique_constraint_supports_index(collations: &[Option<String>]) -> bool {
    collations
        .iter()
        .all(|collation| unique_constraint_collation_is_indexable(collation.as_deref()))
}

fn append_unique_constraint_key_component(
    key: &mut Vec<u8>,
    value: &SqliteValue,
    collation: Option<&str>,
) -> bool {
    if !unique_constraint_collation_is_indexable(collation) {
        return false;
    }

    let is_nocase = collation.is_some_and(|name| name.eq_ignore_ascii_case("NOCASE"));
    let is_rtrim = collation.is_some_and(|name| name.eq_ignore_ascii_case("RTRIM"));

    match value {
        SqliteValue::Null => return false,
        SqliteValue::Integer(i) => {
            key.push(1);
            key.extend_from_slice(&i.to_le_bytes());
        }
        SqliteValue::Float(f) => {
            if (-9_223_372_036_854_775_808.0..9_223_372_036_854_775_808.0).contains(f) {
                #[allow(clippy::cast_possible_truncation)]
                let i = *f as i64;
                #[allow(clippy::cast_precision_loss)]
                if (i as f64) == *f {
                    key.push(1);
                    key.extend_from_slice(&i.to_le_bytes());
                    return true;
                }
            }
            key.push(2);
            key.extend_from_slice(&f.to_bits().to_le_bytes());
        }
        SqliteValue::Text(text) => {
            key.push(3);
            if is_nocase {
                let folded = text.to_ascii_lowercase();
                #[allow(clippy::cast_possible_truncation)]
                key.extend_from_slice(&(folded.len() as u64).to_le_bytes());
                key.extend_from_slice(folded.as_bytes());
            } else if is_rtrim {
                let trimmed = trim_rtrim_collation_text(text.as_bytes());
                #[allow(clippy::cast_possible_truncation)]
                key.extend_from_slice(&(trimmed.len() as u64).to_le_bytes());
                key.extend_from_slice(trimmed);
            } else {
                #[allow(clippy::cast_possible_truncation)]
                key.extend_from_slice(&(text.len() as u64).to_le_bytes());
                key.extend_from_slice(text.as_bytes());
            }
        }
        SqliteValue::Blob(bytes) => {
            key.push(4);
            #[allow(clippy::cast_possible_truncation)]
            key.extend_from_slice(&(bytes.len() as u64).to_le_bytes());
            key.extend_from_slice(bytes);
        }
    }

    true
}

impl UniqueConstraintState {
    fn new(columns: Vec<usize>, mut collations: Vec<Option<String>>) -> Self {
        if collations.len() < columns.len() {
            collations.resize(columns.len(), None);
        } else if collations.len() > columns.len() {
            collations.truncate(columns.len());
        }
        let index = unique_constraint_supports_index(&collations).then(BTreeMap::new);
        Self {
            columns,
            collations,
            index,
        }
    }
}

#[derive(Debug, Clone)]
pub struct MemTable {
    /// Column count for this table (used when creating the table;
    /// actual row widths may vary).
    pub num_columns: usize,
    /// Rows kept in ascending rowid order.
    rows: Vec<MemRow>,
    /// Next auto-increment rowid.
    next_rowid: i64,
    /// UNIQUE constraints tracked for MemDatabase-side enforcement. Builtin
    /// collations (`BINARY`, `NOCASE`, `RTRIM`) use canonical byte-key
    /// indexes; other collations fall back to an exact row scan through the
    /// shared collation registry.
    unique_constraints: Vec<UniqueConstraintState>,
    /// Shared collation registry consulted when a UNIQUE constraint uses a
    /// non-builtin collation that cannot be normalized into a byte key.
    collation_registry: Arc<Mutex<CollationRegistry>>,
}

impl MemTable {
    /// Create a new empty table with the given column count.
    fn new(num_columns: usize) -> Self {
        Self {
            num_columns,
            rows: Vec::new(),
            next_rowid: 1,
            unique_constraints: Vec::new(),
            collation_registry: Arc::new(Mutex::new(CollationRegistry::new())),
        }
    }

    pub fn set_collation_registry(&mut self, registry: Arc<Mutex<CollationRegistry>>) {
        self.collation_registry = registry;
    }

    /// Register a group of columns that together form a UNIQUE constraint.
    pub fn add_unique_column_group(&mut self, cols: Vec<usize>) {
        self.add_unique_column_group_with_collations(cols, Vec::new());
    }

    /// Register a group of columns that together form a UNIQUE constraint
    /// with explicit per-column collation metadata.
    pub fn add_unique_column_group_with_collations(
        &mut self,
        cols: Vec<usize>,
        collations: Vec<Option<String>>,
    ) {
        let mut constraint = UniqueConstraintState::new(cols, collations);
        if let Some(index) = constraint.index.as_mut() {
            for row in &self.rows {
                if let Some(key) = Self::unique_key_for_constraint(
                    &row.values,
                    &constraint.columns,
                    &constraint.collations,
                ) {
                    index.entry(key).or_default().insert(row.rowid);
                }
            }
        }
        self.unique_constraints.push(constraint);
    }

    /// Find rows that conflict with `new_values` on any UNIQUE constraint.
    /// Returns the rowids of all conflicting rows.
    pub fn find_unique_conflicts(&self, new_values: &[SqliteValue]) -> Vec<i64> {
        let mut conflicts = BTreeSet::new();
        let mut collations = None;
        for constraint in &self.unique_constraints {
            if let Some(index) = &constraint.index {
                if let Some(key) = Self::unique_key_for_constraint(
                    new_values,
                    &constraint.columns,
                    &constraint.collations,
                ) && let Some(rowids) = index.get(&key)
                {
                    conflicts.extend(rowids.iter().copied());
                }
                continue;
            }

            let registry = collations.get_or_insert_with(|| {
                self.collation_registry
                    .lock()
                    .unwrap_or_else(|poisoned| poisoned.into_inner())
            });
            for row in &self.rows {
                if Self::unique_constraint_matches_row(
                    &row.values,
                    new_values,
                    &constraint.columns,
                    &constraint.collations,
                    registry,
                ) {
                    conflicts.insert(row.rowid);
                }
            }
        }
        conflicts.into_iter().collect()
    }

    /// Allocate a new unique rowid.
    pub fn alloc_rowid(&mut self) -> i64 {
        let id = self.next_rowid;
        self.next_rowid = self.next_rowid.saturating_add(1);
        id
    }

    /// Return the next implicit rowid that would be allocated for this table.
    #[must_use]
    pub fn next_rowid_hint(&self) -> i64 {
        self.next_rowid
    }

    /// Return the exact visible row count.
    #[must_use]
    pub fn row_count(&self) -> usize {
        self.rows.len()
    }

    /// Return the maximum currently visible rowid.
    #[must_use]
    pub fn max_visible_rowid(&self) -> Option<i64> {
        self.rows.last().map(|row| row.rowid)
    }

    /// Insert a row with the given rowid and values.
    fn insert<V>(&mut self, rowid: i64, values: V)
    where
        V: Into<MemRowValues>,
    {
        let values = values.into();
        // Update next_rowid if needed.
        if rowid >= self.next_rowid {
            self.next_rowid = rowid.saturating_add(1);
        }
        let new_keys = self.unique_keys_for_values(&values);
        if self.rows.last().is_none_or(|row| row.rowid < rowid) {
            self.rows.push(MemRow { rowid, values });
            self.insert_unique_keys(rowid, &new_keys);
            return;
        }
        // Replace if rowid already exists (UPSERT semantics).
        match self.rows.binary_search_by_key(&rowid, |r| r.rowid) {
            Ok(idx) => {
                let old_values = {
                    let row = &mut self.rows[idx];
                    std::mem::replace(&mut row.values, values)
                };
                self.remove_unique_entries(rowid, &old_values);
                self.insert_unique_keys(rowid, &new_keys);
            }
            Err(idx) => {
                self.rows.insert(idx, MemRow { rowid, values });
                self.insert_unique_keys(rowid, &new_keys);
            }
        }
    }

    /// Delete a row by rowid. Returns true if a row was found and deleted.
    pub fn delete_by_rowid(&mut self, rowid: i64) -> bool {
        if let Ok(idx) = self.rows.binary_search_by_key(&rowid, |r| r.rowid) {
            let row = self.rows.remove(idx);
            self.remove_unique_entries(row.rowid, &row.values);
            true
        } else {
            false
        }
    }

    /// Remove all rows from the table.
    pub fn clear(&mut self) {
        self.rows.clear();
        for constraint in &mut self.unique_constraints {
            if let Some(index) = constraint.index.as_mut() {
                index.clear();
            }
        }
    }

    /// Pad all existing rows to have at least `target_cols` columns,
    /// appending `default_val` for any missing trailing columns.
    /// Used after ALTER TABLE ADD COLUMN to make pre-existing rows
    /// visible with the new column's default value.
    pub fn pad_rows_to_column_count(&mut self, target_cols: usize, default_val: &SqliteValue) {
        self.num_columns = target_cols;
        for row in &mut self.rows {
            while row.values.len() < target_cols {
                row.values.push(default_val.clone());
            }
        }
        self.rebuild_unique_indexes();
    }

    /// Find a row by rowid. Returns the index.
    #[inline]
    pub fn find_by_rowid(&self, rowid: i64) -> Option<usize> {
        if let Some(idx) = self.dense_rowid_offset(rowid) {
            return Some(idx);
        }
        self.rows.binary_search_by_key(&rowid, |r| r.rowid).ok()
    }

    #[inline]
    fn dense_rowid_offset(&self, rowid: i64) -> Option<usize> {
        let first_rowid = self.rows.first()?.rowid;
        let offset = rowid.checked_sub(first_rowid)?;
        let idx = usize::try_from(offset).ok()?;
        let row = self.rows.get(idx)?;
        (row.rowid == rowid).then_some(idx)
    }

    fn rowid_lower_bound(&self, rowid: i64) -> usize {
        let Some(first) = self.rows.first() else {
            return 0;
        };
        if rowid <= first.rowid {
            return 0;
        }
        if self.rows.last().is_some_and(|last| rowid > last.rowid) {
            return self.rows.len();
        }
        if let Some(idx) = self.dense_rowid_offset(rowid) {
            return idx;
        }
        match self.rows.binary_search_by_key(&rowid, |r| r.rowid) {
            Ok(idx) | Err(idx) => idx,
        }
    }

    /// Return the values for a rowid, if present.
    #[inline]
    pub fn row_values_by_rowid(&self, rowid: i64) -> Option<&[SqliteValue]> {
        let idx = self.find_by_rowid(rowid)?;
        Some(self.rows[idx].values.as_slice())
    }

    /// Count rows whose rowid is in `[lower_inclusive, upper_exclusive)`.
    pub fn count_rowid_range(&self, lower_inclusive: i64, upper_exclusive: i64) -> usize {
        let start = self.rowid_lower_bound(lower_inclusive);
        let end = self.rowid_lower_bound(upper_exclusive);
        end.saturating_sub(start)
    }

    /// Iterate rows whose rowid is in `[lower_inclusive, upper_exclusive)`.
    pub fn iter_rows_in_rowid_range(
        &self,
        lower_inclusive: i64,
        upper_exclusive: i64,
    ) -> impl Iterator<Item = (i64, &[SqliteValue])> + '_ {
        let start = self.rowid_lower_bound(lower_inclusive);
        let end = self.rowid_lower_bound(upper_exclusive);
        let end = end.max(start);
        self.rows[start..end]
            .iter()
            .map(|row| (row.rowid, row.values.as_slice()))
    }

    /// Iterate all rows as `(rowid, values)` pairs.
    ///
    /// Used by the compat persistence layer to dump table contents to
    /// real SQLite format files.
    pub fn iter_rows(&self) -> impl Iterator<Item = (i64, &[SqliteValue])> + '_ {
        self.rows.iter().map(|r| (r.rowid, r.values.as_slice()))
    }

    /// Insert a row with an explicit rowid (for loading from file).
    ///
    /// This is the public entry point used by the compat persistence
    /// loader. It delegates to the private `insert` method.
    pub fn insert_row<V>(&mut self, rowid: i64, values: V)
    where
        V: Into<MemRowValues>,
    {
        self.insert(rowid, values);
    }

    fn unique_key_for_constraint(
        values: &[SqliteValue],
        columns: &[usize],
        collations: &[Option<String>],
    ) -> Option<Vec<u8>> {
        let mut key = Vec::with_capacity(columns.len() * 8);
        for (position, &col_idx) in columns.iter().enumerate() {
            let value = values.get(col_idx)?;
            if matches!(value, SqliteValue::Null) {
                return None;
            }
            let collation = collations.get(position).and_then(|c| c.as_deref());
            if !append_unique_constraint_key_component(&mut key, value, collation) {
                return None;
            }
        }
        Some(key)
    }

    fn unique_constraint_matches_row(
        existing_values: &[SqliteValue],
        new_values: &[SqliteValue],
        columns: &[usize],
        collations: &[Option<String>],
        registry: &CollationRegistry,
    ) -> bool {
        for (position, &col_idx) in columns.iter().enumerate() {
            let Some(existing_value) = existing_values.get(col_idx) else {
                return false;
            };
            let Some(new_value) = new_values.get(col_idx) else {
                return false;
            };
            if existing_value.is_null() || new_value.is_null() {
                return false;
            }
            let collation = collations.get(position).and_then(|c| c.as_deref());
            if cmp_values_collated(existing_value, new_value, collation, registry)
                != Ordering::Equal
            {
                return false;
            }
        }
        true
    }

    fn remove_unique_entries(&mut self, rowid: i64, values: &[SqliteValue]) {
        for constraint in &mut self.unique_constraints {
            let Some(index) = constraint.index.as_mut() else {
                continue;
            };
            let Some(key) = Self::unique_key_for_constraint(
                values,
                &constraint.columns,
                &constraint.collations,
            ) else {
                continue;
            };
            let remove_entry = if let Some(rowids) = index.get_mut(&key) {
                rowids.remove(&rowid);
                rowids.is_empty()
            } else {
                false
            };
            if remove_entry {
                index.remove(&key);
            }
        }
    }

    fn unique_keys_for_values(&self, values: &[SqliteValue]) -> Vec<Option<Vec<u8>>> {
        self.unique_constraints
            .iter()
            .map(|constraint| {
                constraint.index.as_ref().and_then(|_| {
                    Self::unique_key_for_constraint(
                        values,
                        &constraint.columns,
                        &constraint.collations,
                    )
                })
            })
            .collect()
    }

    fn insert_unique_keys(&mut self, rowid: i64, keys: &[Option<Vec<u8>>]) {
        for (maybe_key, constraint) in keys.iter().zip(&mut self.unique_constraints) {
            if let (Some(key), Some(index)) = (maybe_key, constraint.index.as_mut()) {
                index.entry(key.clone()).or_default().insert(rowid);
            }
        }
    }

    fn rebuild_unique_indexes(&mut self) {
        for constraint in &mut self.unique_constraints {
            if let Some(index) = constraint.index.as_mut() {
                index.clear();
            }
        }
        for row in &self.rows {
            for constraint in &mut self.unique_constraints {
                let Some(index) = constraint.index.as_mut() else {
                    continue;
                };
                if let Some(key) = Self::unique_key_for_constraint(
                    &row.values,
                    &constraint.columns,
                    &constraint.collations,
                ) {
                    index.entry(key).or_default().insert(row.rowid);
                }
            }
        }
    }
}

/// Cursor state for traversing an in-memory table.
#[derive(Debug, Clone)]
struct MemCursor {
    /// Root page (used as table identifier).
    root_page: i32,
    /// Whether this cursor is writable (enforced at the Connection level).
    #[allow(dead_code)]
    writable: bool,
    /// Current row position (None = not positioned).
    position: Option<usize>,
    /// Pseudo-table data (for OpenPseudo: a single row set by RowData/MakeRecord).
    pseudo_row: Option<Vec<SqliteValue>>,
    /// Cached pseudo-row values parsed from `pseudo_reg`.
    cached_pseudo_row: Option<(SqliteValue, Vec<SqliteValue>)>,
    /// Register containing the pseudo-row data blob.
    pseudo_reg: Option<i32>,
    /// Whether this is a pseudo cursor (OpenPseudo).
    is_pseudo: bool,
}

impl MemCursor {
    fn new(root_page: i32, writable: bool) -> Self {
        Self {
            root_page,
            writable,
            position: None,
            pseudo_row: None,
            cached_pseudo_row: None,
            pseudo_reg: None,
            is_pseudo: false,
        }
    }

    fn new_pseudo(reg: i32) -> Self {
        Self {
            root_page: -1,
            writable: false,
            position: None,
            pseudo_row: None,
            cached_pseudo_row: None,
            pseudo_reg: Some(reg),
            is_pseudo: true,
        }
    }
}

/// A single row in the sorter.
///
/// Stores only the decoded **sort-key prefix** (first `key_columns` values)
/// for comparison, plus the raw record blob for output.  Prior to the lazy
/// key decode optimization, ALL columns were eagerly decoded into `values`
/// — now only the sort-key columns are materialized.
#[derive(Debug, Clone)]
struct SorterRow {
    /// Decoded sort-key columns (first `key_columns` values only).
    values: Vec<SqliteValue>,
    /// Raw serialized record for output via `SorterData`.
    blob: Vec<u8>,
}

/// Cursor state for sorter opcodes (`SorterOpen`, `SorterInsert`, ...).
///
/// Supports external merge sort: when in-memory rows exceed `spill_threshold`
/// bytes, the current batch is sorted and flushed to a temporary file as a
/// "run".  At `SorterSort` time, all runs (plus any remaining in-memory rows)
/// are merged via k-way merge.
#[derive(Clone)]
struct SorterCursor {
    /// Number of leading columns used as sort key.
    key_columns: usize,
    /// Per-key sort direction (length == key_columns).
    sort_key_orders: Vec<SortKeyOrder>,
    /// Per-key collation sequence (e.g. "NOCASE"). `None` means BINARY.
    collations: Vec<Option<String>>,
    /// Shared collation registry consulted during comparison.
    collation_registry: Arc<Mutex<CollationRegistry>>,
    /// Inserted records.
    rows: Vec<SorterRow>,
    /// Current position after `SorterSort`/`SorterNext`.
    position: Option<usize>,
    /// Position for which the lazy output decode cache is currently valid.
    cached_row_position: Option<usize>,
    /// Sorter-owned decode/materialization scratch for the current output row.
    cached_row_decode: RowDecodeScratch,
    /// Estimated bytes consumed by `rows` (approximate).
    memory_used: usize,
    /// Memory limit before spilling to disk (default 100 MiB).
    spill_threshold: usize,
    /// Sorted runs that have been spilled to disk.
    spill_runs: Vec<SpillRun>,
    /// Keep only the best N rows during insertion when ORDER BY is paired with
    /// a simple LIMIT. This avoids sorting the full input when the tail can
    /// never survive to the final output.
    top_n_limit: Option<usize>,
    /// Total rows sorted (across all runs + final merge).
    rows_sorted_total: u64,
    /// Total pages spilled to disk.
    spill_pages_total: u64,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SortKeyOrder {
    /// ASC with NULLS FIRST (SQLite default for ASC).
    Asc,
    /// DESC with NULLS LAST (SQLite default for DESC).
    Desc,
    /// ASC with NULLS LAST (explicit NULLS LAST).
    AscNullsLast,
    /// DESC with NULLS FIRST (explicit NULLS FIRST).
    DescNullsFirst,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum DecodeCacheInvalidationReason {
    PositionChange,
    WriteMutation,
    PseudoRowChange,
}

/// Default spill threshold: 100 MiB.
///
/// When in-memory sort data exceeds this limit, the sorter flushes sorted
/// runs to a temporary file. C SQLite's equivalent is configurable via
/// `SQLITE_CONFIG_PMASZ`; this is a compile-time default that could be
/// exposed via `PRAGMA temp_store_max_size` in a future enhancement.
const SORTER_DEFAULT_SPILL_THRESHOLD: usize = 100 * 1024 * 1024;

/// Number of 64-bit words in a Bloom filter (8192 bits = 1 KiB).
const BLOOM_FILTER_WORDS: usize = 128;

/// Compute a simple hash of a `SqliteValue` for Bloom filter lookups.
fn bloom_hash(val: &SqliteValue) -> u64 {
    // FNV-1a-style hash — sufficient for a Bloom filter.
    let mut h: u64 = 0xcbf2_9ce4_8422_2325;
    let bytes: &[u8] = match val {
        SqliteValue::Null => &[0],
        SqliteValue::Integer(i) => {
            // Hash inline to avoid allocation.
            h ^= 1;
            h = h.wrapping_mul(0x0100_0000_01b3);
            for b in i.to_le_bytes() {
                h ^= u64::from(b);
                h = h.wrapping_mul(0x0100_0000_01b3);
            }
            return h;
        }
        SqliteValue::Float(f) => {
            h ^= 2;
            h = h.wrapping_mul(0x0100_0000_01b3);
            for b in f.to_le_bytes() {
                h ^= u64::from(b);
                h = h.wrapping_mul(0x0100_0000_01b3);
            }
            return h;
        }
        SqliteValue::Text(s) => s.as_bytes(),
        SqliteValue::Blob(b) => b,
    };
    for &b in bytes {
        h ^= u64::from(b);
        h = h.wrapping_mul(0x0100_0000_01b3);
    }
    h
}

/// Approximate page size for spill accounting.
///
/// Used only for counting pages written during sorter spill operations.
/// Ideally this would come from the database's actual page size, but the
/// sorter runs at the VDBE level where the page size is available via
/// `self.page_size`. For now, use the default. The spill page count metric
/// is informational — it does not affect correctness.
#[cfg(not(target_arch = "wasm32"))]
const SORTER_SPILL_PAGE_SIZE: usize = fsqlite_types::limits::DEFAULT_PAGE_SIZE as usize;

/// A sorted run that has been flushed to a temporary file.
///
/// Each run stores length-prefixed serialized records in sorted order.
/// Format per record: `[u32-le length][serialized record bytes]`.
#[derive(Debug, Clone)]
struct SpillRun {
    /// Path to the temporary file containing serialized sorted records.
    path: std::path::PathBuf,
    /// Number of records in this run (used for merge accounting).
    #[allow(dead_code)]
    record_count: u64,
    /// Total bytes written (used for page accounting).
    #[allow(dead_code)]
    bytes_written: u64,
}

impl SorterCursor {
    #[cfg(test)]
    fn new(
        key_columns: usize,
        sort_key_orders: Vec<SortKeyOrder>,
        collations: Vec<Option<String>>,
    ) -> Self {
        Self::with_collation_registry(
            key_columns,
            sort_key_orders,
            collations,
            Arc::new(Mutex::new(CollationRegistry::new())),
            None,
        )
    }

    fn with_collation_registry(
        key_columns: usize,
        mut sort_key_orders: Vec<SortKeyOrder>,
        collations: Vec<Option<String>>,
        collation_registry: Arc<Mutex<CollationRegistry>>,
        top_n_limit: Option<usize>,
    ) -> Self {
        let key_columns = key_columns.max(1);
        if sort_key_orders.len() < key_columns {
            sort_key_orders.resize(key_columns, SortKeyOrder::Asc);
        }
        sort_key_orders.truncate(key_columns);
        Self {
            key_columns,
            sort_key_orders,
            collations,
            collation_registry,
            rows: Vec::new(),
            position: None,
            cached_row_position: None,
            cached_row_decode: RowDecodeScratch::default(),
            memory_used: 0,
            spill_threshold: SORTER_DEFAULT_SPILL_THRESHOLD,
            spill_runs: Vec::new(),
            top_n_limit,
            rows_sorted_total: 0,
            spill_pages_total: 0,
        }
    }

    fn insert_sorted_top_n_row(&mut self, row: SorterRow, collation_registry: &CollationRegistry) {
        let mut low = 0usize;
        let mut high = self.rows.len();
        while low < high {
            let mid = low + (high - low) / 2;
            let ordering = compare_sorter_rows(
                &row.values,
                &self.rows[mid].values,
                self.key_columns,
                &self.sort_key_orders,
                &self.collations,
                collation_registry,
            );
            if ordering == Ordering::Less {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
        self.rows.insert(low, row);
    }

    /// Estimate the memory footprint of a sorter row.
    fn estimate_row_size(values: &[SqliteValue], blob: &[u8]) -> usize {
        // Base overhead per Vec element + per-value overhead + blob size
        let mut size = std::mem::size_of::<SorterRow>() + values.len() * 24 + blob.len();
        for val in values {
            match val {
                SqliteValue::Text(s) => size += s.len(),
                SqliteValue::Blob(b) => size += b.len(),
                _ => {}
            }
        }
        size
    }

    /// Insert a row and spill to disk if memory exceeds the threshold.
    fn insert_row(&mut self, values: Vec<SqliteValue>, blob: Vec<u8>) -> Result<()> {
        if let Some(limit) = self.top_n_limit {
            if limit == 0 {
                return Ok(());
            }

            let new_row_size = Self::estimate_row_size(&values, &blob);
            let new_row = SorterRow { values, blob };
            let collation_registry = Arc::clone(&self.collation_registry);
            let coll_guard = collation_registry.lock().unwrap_or_else(|e| e.into_inner());
            if self.rows.len() < limit {
                self.insert_sorted_top_n_row(new_row, &coll_guard);
                self.memory_used += new_row_size;
                self.invalidate_output_row_cache();
                return Ok(());
            }

            if let Some(worst_row) = self.rows.last() {
                let ordering = compare_sorter_rows(
                    &new_row.values,
                    &worst_row.values,
                    self.key_columns,
                    &self.sort_key_orders,
                    &self.collations,
                    &coll_guard,
                );
                if ordering != Ordering::Less {
                    return Ok(());
                }
                self.insert_sorted_top_n_row(new_row, &coll_guard);
                let removed_row = self
                    .rows
                    .pop()
                    .expect("top-N sorter should remove one retained row");
                let removed_size = Self::estimate_row_size(&removed_row.values, &removed_row.blob);
                self.memory_used = self.memory_used.saturating_sub(removed_size);
                self.memory_used = self.memory_used.saturating_add(new_row_size);
                self.invalidate_output_row_cache();
            }
            return Ok(());
        }

        self.memory_used += Self::estimate_row_size(&values, &blob);
        self.rows.push(SorterRow { values, blob });
        self.invalidate_output_row_cache();

        if self.memory_used >= self.spill_threshold {
            self.spill_to_disk()?;
        }
        Ok(())
    }

    /// Sort the in-memory rows, write them to a temp file, and clear them.
    #[cfg(not(target_arch = "wasm32"))]
    fn spill_to_disk(&mut self) -> Result<()> {
        use std::io::Write;

        if self.rows.is_empty() {
            return Ok(());
        }

        // Sort current batch — lock collation registry once for entire sort.
        let key_columns = self.key_columns;
        let orders = self.sort_key_orders.clone();
        let colls = self.collations.clone();
        let coll_guard = self
            .collation_registry
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        self.rows.sort_by(|lhs, rhs| {
            compare_sorter_rows(
                &lhs.values,
                &rhs.values,
                key_columns,
                &orders,
                &colls,
                &coll_guard,
            )
        });
        drop(coll_guard);

        // Write to temp file.  We use `keep()` to detach the auto-delete
        // guard so the file persists until we explicitly remove it in
        // `sort()` / `reset()`.
        let tmp = tempfile::NamedTempFile::new()
            .map_err(|e| FrankenError::internal(format!("sorter spill tempfile: {e}")))?;
        let (file, path) = tmp
            .keep()
            .map_err(|e| FrankenError::internal(format!("sorter spill keep: {e}")))?;
        let mut writer = std::io::BufWriter::new(file);

        let record_count = self.rows.len() as u64;
        let mut bytes_written: u64 = 0;
        for row in &self.rows {
            // Write the raw blob directly instead of re-serializing from
            // decoded values.  The blob is the original record bytes from
            // MakeRecord and is identical to what serialize_record would
            // produce (but without the decode→re-encode round-trip).
            #[allow(clippy::cast_possible_truncation)]
            let len_bytes = (row.blob.len() as u32).to_le_bytes();
            writer
                .write_all(&len_bytes)
                .map_err(|e| FrankenError::internal(format!("sorter spill write len: {e}")))?;
            writer
                .write_all(&row.blob)
                .map_err(|e| FrankenError::internal(format!("sorter spill write data: {e}")))?;
            bytes_written += 4 + row.blob.len() as u64;
        }
        writer
            .flush()
            .map_err(|e| FrankenError::internal(format!("sorter spill flush: {e}")))?;

        #[allow(clippy::cast_possible_truncation)]
        let pages = (bytes_written as usize).div_ceil(SORTER_SPILL_PAGE_SIZE);
        self.spill_pages_total += pages as u64;

        tracing::warn!(
            rows = record_count,
            bytes = bytes_written,
            pages,
            run_index = self.spill_runs.len(),
            "sorter spilling to disk"
        );

        self.spill_runs.push(SpillRun {
            path,
            record_count,
            bytes_written,
        });

        self.rows_sorted_total += record_count;
        self.rows.clear();
        self.invalidate_output_row_cache();
        self.memory_used = 0;
        Ok(())
    }

    /// Browser builds do not have a portable temp-file story yet, so sorter
    /// spill currently degrades to a pure in-memory sort path.
    #[cfg(target_arch = "wasm32")]
    fn spill_to_disk(&mut self) -> Result<()> {
        self.spill_threshold = usize::MAX;
        tracing::warn!(
            rows = self.rows.len(),
            bytes = self.memory_used,
            "sorter spill requested on wasm; keeping rows in memory"
        );
        Ok(())
    }

    /// Sort the sorter, merging any spilled runs with in-memory rows.
    ///
    /// After this call, `self.rows` contains the fully sorted result and
    /// `self.spill_runs` is drained.
    #[allow(clippy::too_many_lines)]
    fn sort(&mut self) -> Result<()> {
        // Lock collation registry once for entire sort operation.
        let coll_guard = self
            .collation_registry
            .lock()
            .unwrap_or_else(|e| e.into_inner());

        if self.spill_runs.is_empty() {
            if self.top_n_limit.is_some() {
                self.rows_sorted_total += self.rows.len() as u64;
                return Ok(());
            }

            // Pure in-memory sort — fast path.
            let key_columns = self.key_columns;
            let orders = self.sort_key_orders.clone();
            let colls = self.collations.clone();
            self.rows.sort_by(|lhs, rhs| {
                compare_sorter_rows(
                    &lhs.values,
                    &rhs.values,
                    key_columns,
                    &orders,
                    &colls,
                    &coll_guard,
                )
            });
            self.rows_sorted_total += self.rows.len() as u64;
            return Ok(());
        }

        // Sort remaining in-memory rows as one more "run".
        let key_columns = self.key_columns;
        let orders = self.sort_key_orders.clone();
        let colls = self.collations.clone();
        self.rows.sort_by(|lhs, rhs| {
            compare_sorter_rows(
                &lhs.values,
                &rhs.values,
                key_columns,
                &orders,
                &colls,
                &coll_guard,
            )
        });

        // Collect all runs: disk runs first, then in-memory remainder.
        let mut run_iters: Vec<RunIterator> = Vec::with_capacity(self.spill_runs.len() + 1);
        for run in &self.spill_runs {
            run_iters.push(RunIterator::from_file(&run.path, self.key_columns)?);
        }
        if !self.rows.is_empty() {
            let mem_rows = std::mem::take(&mut self.rows);
            self.rows_sorted_total += mem_rows.len() as u64;
            run_iters.push(RunIterator::from_memory(mem_rows));
        }

        // K-way merge using a simple tournament approach.
        let mut merged: Vec<SorterRow> = Vec::with_capacity(self.rows_sorted_total as usize);

        // Advance all iterators to their first element.
        for iter in &mut run_iters {
            iter.advance()?;
        }

        loop {
            // Find the run with the smallest current element.
            let mut best_idx: Option<usize> = None;
            for (i, iter) in run_iters.iter().enumerate() {
                let Some(row) = iter.current_values() else {
                    continue;
                };
                if let Some(bi) = best_idx {
                    if let Some(best_row) = run_iters[bi].current_values() {
                        if compare_sorter_rows(
                            row,
                            best_row,
                            key_columns,
                            &orders,
                            &colls,
                            &coll_guard,
                        ) == Ordering::Less
                        {
                            best_idx = Some(i);
                        }
                    }
                } else {
                    best_idx = Some(i);
                }
            }

            let Some(idx) = best_idx else {
                break; // All runs exhausted.
            };

            if let Some(row) = run_iters[idx].take_current() {
                merged.push(row);
            }
            run_iters[idx].advance()?;
        }

        tracing::debug!(
            rows = merged.len(),
            runs = self.spill_runs.len() + 1,
            "sorter merge complete"
        );

        // Clean up temp files.
        for run in &self.spill_runs {
            let _ = std::fs::remove_file(&run.path);
        }
        self.spill_runs.clear();
        self.rows = merged;
        drop(coll_guard);
        self.invalidate_output_row_cache();
        self.memory_used = 0;
        Ok(())
    }

    /// Clear all rows and spill state (for `ResetSorter`).
    fn reset(&mut self) {
        self.rows.clear();
        self.position = None;
        self.invalidate_output_row_cache();
        self.memory_used = 0;
        // Clean up temp files.
        for run in &self.spill_runs {
            let _ = std::fs::remove_file(&run.path);
        }
        self.spill_runs.clear();
    }

    fn invalidate_output_row_cache(&mut self) {
        self.cached_row_position = None;
        self.cached_row_decode.invalidate();
    }
}

impl Drop for SorterCursor {
    fn drop(&mut self) {
        for run in &self.spill_runs {
            let _ = std::fs::remove_file(&run.path);
        }
    }
}

/// Iterator over records in a sorted run (either disk-backed or in-memory).
enum RunIterator {
    /// Records read from a temporary file.
    File {
        reader: std::io::BufReader<std::fs::File>,
        current: Option<SorterRow>,
        /// Number of leading sort-key columns to decode from spilled records.
        key_columns: usize,
    },
    /// Records from an in-memory Vec (used for the final unsorted batch).
    Memory {
        rows: std::vec::IntoIter<SorterRow>,
        current: Option<SorterRow>,
    },
}

impl RunIterator {
    fn from_file(path: &std::path::Path, key_columns: usize) -> Result<Self> {
        let file = std::fs::File::open(path)
            .map_err(|e| FrankenError::internal(format!("sorter run open: {e}")))?;
        Ok(Self::File {
            reader: std::io::BufReader::new(file),
            current: None,
            key_columns,
        })
    }

    fn from_memory(rows: Vec<SorterRow>) -> Self {
        Self::Memory {
            rows: rows.into_iter(),
            current: None,
        }
    }

    fn current_values(&self) -> Option<&Vec<SqliteValue>> {
        match self {
            Self::File { current, .. } | Self::Memory { current, .. } => {
                current.as_ref().map(|r| &r.values)
            }
        }
    }

    fn take_current(&mut self) -> Option<SorterRow> {
        match self {
            Self::File { current, .. } | Self::Memory { current, .. } => current.take(),
        }
    }

    fn advance(&mut self) -> Result<()> {
        match self {
            Self::File {
                reader,
                current,
                key_columns,
            } => {
                use std::io::Read;
                let mut len_buf = [0u8; 4];
                match reader.read_exact(&mut len_buf) {
                    Ok(()) => {
                        let len = u32::from_le_bytes(len_buf) as usize;
                        let mut buf = vec![0u8; len];
                        reader
                            .read_exact(&mut buf)
                            .map_err(|e| FrankenError::internal(format!("sorter run read: {e}")))?;
                        // Decode only the sort-key prefix — not all columns.
                        let values = fsqlite_types::record::parse_record_prefix(&buf, *key_columns)
                            .ok_or_else(|| {
                                FrankenError::internal("sorter run: malformed record")
                            })?;
                        *current = Some(SorterRow { values, blob: buf });
                    }
                    Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
                        *current = None;
                    }
                    Err(e) => {
                        return Err(FrankenError::internal(format!("sorter run read len: {e}")));
                    }
                }
            }
            Self::Memory { rows, current } => {
                *current = rows.next();
            }
        }
        Ok(())
    }
}

// ── Shared Transaction Page I/O ─────────────────────────────────────────
//
// Phase 5 (bd-2a3y): Adapter that lets multiple `BtCursor` instances
// share a single pager transaction via `Rc<RefCell<…>>`.  The
// `PageReader`/`PageWriter` impls delegate through the `RefCell` borrow
// so that cursors can read/write pages on the real MVCC stack.

// ── MVCC Concurrent Context (bd-kivg / 5E.2) ────────────────────────────
//
// When concurrent mode is enabled, page-level locks must be acquired
// before writes. The write set is used for FCW validation at commit time.

/// MVCC concurrent mode context for page-level locking (bd-kivg / 5E.2).
///
/// When a transaction is in concurrent mode, this context enables:
/// - Acquiring page-level locks before writes via [`concurrent_write_page`]
/// - Recording written pages in the write set for FCW validation at commit
#[derive(Clone)]
struct ConcurrentContext {
    /// Session ID for this concurrent transaction.
    session_id: u64,
    /// Stable transaction ID used in hot-path logging.
    txn_id: u64,
    /// Immutable snapshot upper bound for this concurrent transaction.
    snapshot_high: CommitSeq,
    /// Stable shared handle for this concurrent transaction.
    handle: SharedConcurrentHandle,
    /// Shared reference to the page-level lock table.
    lock_table: Arc<InProcessPageLockTable>,
    /// Shared reference to the FCW commit index.
    commit_index: Arc<CommitIndex>,
    /// Busy-timeout budget used when contending on page-level locks.
    busy_timeout_ms: u64,
}

impl ConcurrentContext {
    fn new(
        session_id: u64,
        handle: SharedConcurrentHandle,
        lock_table: Arc<InProcessPageLockTable>,
        commit_index: Arc<CommitIndex>,
        busy_timeout_ms: u64,
    ) -> Self {
        let (txn_id, snapshot_high) = {
            let handle_guard = handle.lock();
            (
                handle_guard.txn_token().id.get(),
                handle_guard.snapshot().high,
            )
        };
        Self {
            session_id,
            txn_id,
            snapshot_high,
            handle,
            lock_table,
            commit_index,
            busy_timeout_ms,
        }
    }
}

/// Shared wrapper around a boxed [`TransactionHandle`] so multiple
/// storage cursors can share one transaction.
///
/// Optionally includes [`ConcurrentContext`] for MVCC page-level locking
/// (bd-kivg / 5E.2).
#[derive(Clone)]
pub struct SharedTxnPageIo {
    txn: Rc<RefCell<TransactionKind>>,
    /// MVCC concurrent context (bd-kivg / 5E.2). When present, enables
    /// page-level locking for write operations.
    concurrent: Rc<RefCell<Option<ConcurrentContext>>>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ConcurrentWriteTier {
    Tier0AlreadyOwned,
    Tier1FirstTouch,
    Tier2CommitSurfaceRare,
}

impl std::fmt::Debug for SharedTxnPageIo {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("SharedTxnPageIo")
            .field("rc_count", &Rc::strong_count(&self.txn))
            .field("concurrent", &self.concurrent.borrow().is_some())
            .finish()
    }
}

impl SharedTxnPageIo {
    fn from_parts(txn: TransactionKind, concurrent: Option<ConcurrentContext>) -> Self {
        Self {
            txn: Rc::new(RefCell::new(txn)),
            concurrent: Rc::new(RefCell::new(concurrent)),
        }
    }

    #[cfg(test)]
    fn new(txn: impl Into<TransactionKind>) -> Self {
        Self::from_parts(txn.into(), None)
    }

    /// Create with MVCC concurrent context (bd-kivg / 5E.2).
    pub fn with_concurrent(
        txn: impl Into<TransactionKind>,
        session_id: u64,
        handle: SharedConcurrentHandle,
        lock_table: Arc<InProcessPageLockTable>,
        commit_index: Arc<CommitIndex>,
        busy_timeout_ms: u64,
    ) -> Self {
        Self::from_parts(
            txn.into(),
            Some(ConcurrentContext::new(
                session_id,
                handle,
                lock_table,
                commit_index,
                busy_timeout_ms,
            )),
        )
    }

    /// Unwrap back to the owned transaction handle.
    /// Returns an error if other Rc clones still exist.
    pub fn into_inner(self) -> Result<TransactionKind> {
        match Rc::try_unwrap(self.txn) {
            Ok(cell) => Ok(cell.into_inner()),
            Err(rc) => Err(FrankenError::Internal(format!(
                "SharedTxnPageIo: {} outstanding Rc references",
                Rc::strong_count(&rc),
            ))),
        }
    }

    /// bd-perf: Swap the inner transaction without breaking Rc references.
    /// Retained StorageCursors hold clones of the same Rc, so replacing
    /// the inner value keeps them valid. Returns the old transaction.
    fn refill(
        &self,
        txn: TransactionKind,
        concurrent: Option<ConcurrentContext>,
    ) -> TransactionKind {
        self.concurrent.replace(concurrent);
        self.txn.replace(txn)
    }

    /// bd-perf: Extract the inner transaction without consuming self.
    /// Used by take_transaction when cursors are retained — we need to
    /// return the txn to the connection while keeping the Rc alive for
    /// retained cursors (they'll get a fresh txn via refill next time).
    fn drain(&self) -> TransactionKind {
        self.txn.replace(TransactionKind::Drained)
    }

    fn concurrent_context(&self) -> Option<ConcurrentContext> {
        self.concurrent.borrow().clone()
    }

    fn clear_stale_synthetic_pending_commit_surface(
        &self,
        _cx: &Cx,
        operation: &str,
    ) -> Result<()> {
        let Some(ctx) = self.concurrent_context() else {
            return Ok(());
        };
        // The live engine only synthesizes conflict-only tracking for page 1.
        // Avoid rebuilding the full pending-commit surface after every write
        // just to learn that page 1 is still (or is no longer) required.
        let page_one_is_synthetic = {
            let handle = ctx.handle.lock();
            concurrent_page_is_synthetic_conflict_only(&handle, PageNumber::ONE)
        };
        if !page_one_is_synthetic {
            return Ok(());
        }
        if self.txn.borrow().page_one_in_pending_commit_surface()? {
            return Ok(());
        }

        let metrics_enabled = vdbe_metrics_enabled();
        let clear_started = metrics_enabled.then(Instant::now);
        let mut handle = ctx.handle.lock();
        if concurrent_page_is_synthetic_conflict_only(&handle, PageNumber::ONE) {
            concurrent_clear_page_state(
                &mut handle,
                &ctx.lock_table,
                ctx.session_id,
                PageNumber::ONE,
            )
            .map_err(|restore_error| {
                FrankenError::Internal(format!(
                    "MVCC pending commit surface clear failed for page {} during {operation}: {restore_error}",
                    PageNumber::ONE.get()
                ))
            })?;
            add_vdbe_counter_if(
                metrics_enabled,
                &FSQLITE_VDBE_MVCC_PENDING_SURFACE_CLEARS_TOTAL,
                1,
            );
            if let Some(clear_started) = clear_started {
                add_vdbe_duration_if(
                    metrics_enabled,
                    &FSQLITE_VDBE_MVCC_PENDING_SURFACE_CLEAR_TIME_NS_TOTAL,
                    clear_started,
                );
            }
        }

        Ok(())
    }

    fn classify_concurrent_write_tier(
        ctx: &ConcurrentContext,
        page_no: PageNumber,
    ) -> ConcurrentWriteTier {
        if ctx.handle.lock().holds_page_lock(page_no) {
            return ConcurrentWriteTier::Tier0AlreadyOwned;
        }

        if page_no == PageNumber::ONE {
            ConcurrentWriteTier::Tier2CommitSurfaceRare
        } else {
            ConcurrentWriteTier::Tier1FirstTouch
        }
    }

    fn restore_concurrent_page_state(
        ctx: &ConcurrentContext,
        page_state: &ConcurrentPageState,
        restore_label: &str,
    ) -> Result<()> {
        let mut handle = ctx.handle.lock();
        concurrent_restore_page_state(&mut handle, &ctx.lock_table, ctx.session_id, page_state)
            .map_err(|restore_error| {
                FrankenError::Internal(format!("{restore_label}: {restore_error}"))
            })
    }

    fn write_page_tier0_already_owned(
        &self,
        cx: &Cx,
        ctx: &ConcurrentContext,
        page_no: PageNumber,
        page_data_base: PageData,
    ) -> Result<()> {
        add_vdbe_counter(&FSQLITE_VDBE_MVCC_TIER0_ALREADY_OWNED_WRITES_TOTAL, 1);
        let mut handle = ctx.handle.lock();
        let prior_state = concurrent_page_state(&handle, page_no);
        if let Err(prepare_error) =
            concurrent_prepare_write_page(&mut handle, &ctx.lock_table, ctx.session_id, page_no)
        {
            return Err(FrankenError::Internal(format!(
                "MVCC fast-path prepare failed: {prepare_error}"
            )));
        }
        if let Err(stage_error) = concurrent_stage_prepared_write_marker(&mut handle, page_no) {
            if let Err(restore_error) = concurrent_restore_page_state(
                &mut handle,
                &ctx.lock_table,
                ctx.session_id,
                &prior_state,
            ) {
                return Err(FrankenError::Internal(format!(
                    "MVCC fast-path staging failed: {stage_error}; MVCC fast-path restore failed: {restore_error}"
                )));
            }
            return Err(FrankenError::Internal(format!(
                "MVCC fast-path staging failed: {stage_error}"
            )));
        }
        drop(handle);

        if let Err(write_error) = self
            .txn
            .borrow_mut()
            .write_page_data(cx, page_no, page_data_base)
        {
            Self::restore_concurrent_page_state(
                ctx,
                &prior_state,
                &format!("pager write_page failed: {write_error}; MVCC fast-path restore failed"),
            )?;
            return Err(write_error);
        }

        self.clear_stale_synthetic_pending_commit_surface(cx, "write_page_fast")?;
        Ok(())
    }

    fn write_page_tier1_first_touch(
        &self,
        cx: &Cx,
        ctx: &ConcurrentContext,
        page_no: PageNumber,
        page_data_base: PageData,
    ) -> Result<()> {
        add_vdbe_counter(&FSQLITE_VDBE_MVCC_TIER1_FIRST_TOUCH_WRITES_TOTAL, 1);
        let prior_page_state = {
            let handle = ctx.handle.lock();
            concurrent_page_state(&handle, page_no)
        };

        let started = Instant::now();
        let deadline = Duration::from_millis(ctx.busy_timeout_ms);
        loop {
            observe_execution_cancellation(cx)?;
            let (write_result, conflicting_commit_seq) = {
                let mut handle = ctx.handle.lock();
                let conflicting_commit_seq = (!handle.holds_page_lock(page_no))
                    .then(|| ctx.commit_index.latest(page_no))
                    .flatten()
                    .filter(|seq| *seq > ctx.snapshot_high);
                let write_result = conflicting_commit_seq.is_none().then(|| {
                    concurrent_prepare_write_page(
                        &mut handle,
                        &ctx.lock_table,
                        ctx.session_id,
                        page_no,
                    )
                });
                (write_result, conflicting_commit_seq)
            };
            let txn_id = ctx.txn_id;
            let snapshot_high = ctx.snapshot_high.get();

            if let Some(conflicting_commit_seq) = conflicting_commit_seq {
                add_vdbe_counter(&FSQLITE_VDBE_MVCC_STALE_SNAPSHOT_REJECTS_TOTAL, 1);
                let error = FrankenError::BusySnapshot {
                    conflicting_pages: page_no.get().to_string(),
                };
                Self::restore_concurrent_page_state(
                    ctx,
                    &prior_page_state,
                    &format!("{error}; MVCC state restore failed"),
                )?;
                tracing::warn!(
                    txn_id,
                    commit_seq = conflicting_commit_seq.get(),
                    snapshot_high,
                    page_id = page_no.get(),
                    visibility_decision = "write_snapshot_stale",
                    conflict_reason = "fcw_base_drift",
                    "mvcc write rejected due to stale snapshot"
                );
                return Err(error);
            }

            match write_result.ok_or_else(|| {
                FrankenError::Internal("write result must exist when snapshot is valid".to_owned())
            })? {
                Ok(()) => {
                    tracing::debug!(
                        txn_id,
                        commit_seq = snapshot_high,
                        snapshot_high,
                        page_id = page_no.get(),
                        visibility_decision = "write_lock_acquired",
                        conflict_reason = "none",
                        write_tier = "tier1_first_touch",
                        "mvcc write visibility decision"
                    );
                    break;
                }
                Err(MvccError::Busy) => {
                    let remaining = deadline.saturating_sub(started.elapsed());
                    match wait_for_page_lock_holder_change(cx, ctx, page_no, remaining) {
                        Ok(true) => {
                            add_vdbe_counter(&FSQLITE_VDBE_MVCC_WRITE_BUSY_RETRIES_TOTAL, 1);
                            tracing::warn!(
                                txn_id,
                                commit_seq = snapshot_high,
                                snapshot_high,
                                page_id = page_no.get(),
                                visibility_decision = "write_retry",
                                conflict_reason = "page_lock_busy",
                                retry_policy = "park_wake",
                                retry_wait_ms = remaining.as_millis(),
                                write_tier = "tier1_first_touch",
                                "mvcc write conflict detected"
                            );
                        }
                        Ok(false) => {
                            add_vdbe_counter(&FSQLITE_VDBE_MVCC_WRITE_BUSY_TIMEOUTS_TOTAL, 1);
                            let error = FrankenError::Busy;
                            Self::restore_concurrent_page_state(
                                ctx,
                                &prior_page_state,
                                &format!("{error}; MVCC state restore failed"),
                            )?;
                            tracing::warn!(
                                txn_id,
                                commit_seq = snapshot_high,
                                snapshot_high,
                                page_id = page_no.get(),
                                visibility_decision = "write_busy_timeout",
                                conflict_reason = "page_lock_busy",
                                retry_policy = "park_wake",
                                write_tier = "tier1_first_touch",
                                "mvcc write conflict exceeded busy timeout"
                            );
                            return Err(error);
                        }
                        Err(wait_error) => {
                            Self::restore_concurrent_page_state(
                                ctx,
                                &prior_page_state,
                                &format!("{wait_error}; MVCC state restore failed"),
                            )?;
                            return Err(wait_error);
                        }
                    }
                }
                Err(e) => {
                    let error = FrankenError::Internal(format!("MVCC write_page failed: {e}"));
                    Self::restore_concurrent_page_state(
                        ctx,
                        &prior_page_state,
                        &format!("{error}; MVCC state restore failed"),
                    )?;
                    tracing::warn!(
                        txn_id,
                        commit_seq = snapshot_high,
                        snapshot_high,
                        page_id = page_no.get(),
                        visibility_decision = "write_abort",
                        conflict_reason = %e,
                        write_tier = "tier1_first_touch",
                        "mvcc write failed"
                    );
                    return Err(error);
                }
            }
        }

        {
            let mut handle = ctx.handle.lock();
            if let Err(stage_error) = concurrent_stage_prepared_write_marker(&mut handle, page_no) {
                Self::restore_concurrent_page_state(
                    ctx,
                    &prior_page_state,
                    &format!("MVCC write staging failed: {stage_error}; MVCC state restore failed"),
                )?;
                return Err(FrankenError::Internal(format!(
                    "MVCC write staging failed: {stage_error}"
                )));
            }
        }

        if let Err(write_error) = self
            .txn
            .borrow_mut()
            .write_page_data(cx, page_no, page_data_base)
        {
            Self::restore_concurrent_page_state(
                ctx,
                &prior_page_state,
                &format!("pager write_page failed: {write_error}; MVCC state restore failed"),
            )?;
            return Err(write_error);
        }

        self.clear_stale_synthetic_pending_commit_surface(cx, "write_page_tier1")?;
        Ok(())
    }

    fn write_page_tier2_commit_surface_rare(
        &self,
        cx: &Cx,
        ctx: &ConcurrentContext,
        page_no: PageNumber,
        page_data_base: PageData,
    ) -> Result<()> {
        add_vdbe_counter(&FSQLITE_VDBE_MVCC_TIER2_COMMIT_SURFACE_WRITES_TOTAL, 1);
        let page_one_tracking_required = if page_no == PageNumber::ONE {
            false
        } else {
            self.txn
                .borrow()
                .write_page_requires_page_one_conflict_tracking(page_no)?
        };
        let handle = ctx.handle.lock();
        let prior_page_state = Some(concurrent_page_state(&handle, page_no));
        let page_one_state =
            if page_one_tracking_required && !handle.tracks_write_conflict_page(PageNumber::ONE) {
                Some(concurrent_page_state(&handle, PageNumber::ONE))
            } else {
                None
            };
        drop(handle);

        let restore_concurrent_state = || -> std::result::Result<(), String> {
            let mut handle = ctx.handle.lock();
            if let Some(prior_page_state) = prior_page_state.as_ref()
                && let Err(restore_error) = concurrent_restore_page_state(
                    &mut handle,
                    &ctx.lock_table,
                    ctx.session_id,
                    prior_page_state,
                )
            {
                return Err(format!("MVCC state restore failed: {restore_error}"));
            }
            if let Some(page_one_state) = page_one_state.as_ref()
                && let Err(restore_error) = concurrent_restore_page_state(
                    &mut handle,
                    &ctx.lock_table,
                    ctx.session_id,
                    page_one_state,
                )
            {
                return Err(format!("MVCC page1 restore failed: {restore_error}"));
            }
            Ok(())
        };

        if page_one_state.is_some() {
            track_concurrent_conflict_only_page(cx, ctx, PageNumber::ONE, "write_page")?;
        }
        let started = Instant::now();
        let deadline = Duration::from_millis(ctx.busy_timeout_ms);

        loop {
            observe_execution_cancellation(cx)?;
            let (write_result, conflicting_commit_seq) = {
                let mut handle = ctx.handle.lock();
                let conflicting_commit_seq = (!handle.holds_page_lock(page_no))
                    .then(|| ctx.commit_index.latest(page_no))
                    .flatten()
                    .filter(|seq| *seq > ctx.snapshot_high);
                let write_result = conflicting_commit_seq.is_none().then(|| {
                    concurrent_prepare_write_page(
                        &mut handle,
                        &ctx.lock_table,
                        ctx.session_id,
                        page_no,
                    )
                });
                (write_result, conflicting_commit_seq)
            };
            let txn_id = ctx.txn_id;
            let snapshot_high = ctx.snapshot_high.get();

            if let Some(conflicting_commit_seq) = conflicting_commit_seq {
                add_vdbe_counter(&FSQLITE_VDBE_MVCC_STALE_SNAPSHOT_REJECTS_TOTAL, 1);
                let error = FrankenError::BusySnapshot {
                    conflicting_pages: page_no.get().to_string(),
                };
                if let Err(restore_error) = restore_concurrent_state() {
                    return Err(FrankenError::Internal(format!("{error}; {restore_error}")));
                }
                tracing::warn!(
                    txn_id,
                    commit_seq = conflicting_commit_seq.get(),
                    snapshot_high,
                    page_id = page_no.get(),
                    visibility_decision = "write_snapshot_stale",
                    conflict_reason = "fcw_base_drift",
                    write_tier = "tier2_commit_surface_rare",
                    "mvcc write rejected due to stale snapshot"
                );
                return Err(error);
            }

            match write_result.ok_or_else(|| {
                FrankenError::Internal("write result must exist when snapshot is valid".to_owned())
            })? {
                Ok(()) => {
                    tracing::debug!(
                        txn_id,
                        commit_seq = snapshot_high,
                        snapshot_high,
                        page_id = page_no.get(),
                        visibility_decision = "write_lock_acquired",
                        conflict_reason = "none",
                        write_tier = "tier2_commit_surface_rare",
                        "mvcc write visibility decision"
                    );
                    break;
                }
                Err(MvccError::Busy) => {
                    let remaining = deadline.saturating_sub(started.elapsed());
                    match wait_for_page_lock_holder_change(cx, ctx, page_no, remaining) {
                        Ok(true) => {
                            add_vdbe_counter(&FSQLITE_VDBE_MVCC_WRITE_BUSY_RETRIES_TOTAL, 1);
                            tracing::warn!(
                                txn_id,
                                commit_seq = snapshot_high,
                                snapshot_high,
                                page_id = page_no.get(),
                                visibility_decision = "write_retry",
                                conflict_reason = "page_lock_busy",
                                retry_policy = "park_wake",
                                retry_wait_ms = remaining.as_millis(),
                                write_tier = "tier2_commit_surface_rare",
                                "mvcc write conflict detected"
                            );
                        }
                        Ok(false) => {
                            add_vdbe_counter(&FSQLITE_VDBE_MVCC_WRITE_BUSY_TIMEOUTS_TOTAL, 1);
                            let error = FrankenError::Busy;
                            if let Err(restore_error) = restore_concurrent_state() {
                                return Err(FrankenError::Internal(format!(
                                    "{error}; {restore_error}"
                                )));
                            }
                            tracing::warn!(
                                txn_id,
                                commit_seq = snapshot_high,
                                snapshot_high,
                                page_id = page_no.get(),
                                visibility_decision = "write_busy_timeout",
                                conflict_reason = "page_lock_busy",
                                retry_policy = "park_wake",
                                write_tier = "tier2_commit_surface_rare",
                                "mvcc write conflict exceeded busy timeout"
                            );
                            return Err(error);
                        }
                        Err(wait_error) => {
                            if let Err(restore_error) = restore_concurrent_state() {
                                return Err(FrankenError::Internal(format!(
                                    "{wait_error}; {restore_error}"
                                )));
                            }
                            return Err(wait_error);
                        }
                    }
                }
                Err(e) => {
                    let error = FrankenError::Internal(format!("MVCC write_page failed: {e}"));
                    if let Err(restore_error) = restore_concurrent_state() {
                        return Err(FrankenError::Internal(format!("{error}; {restore_error}")));
                    }
                    tracing::warn!(
                        txn_id,
                        commit_seq = snapshot_high,
                        snapshot_high,
                        page_id = page_no.get(),
                        visibility_decision = "write_abort",
                        conflict_reason = %e,
                        write_tier = "tier2_commit_surface_rare",
                        "mvcc write failed"
                    );
                    return Err(error);
                }
            }
        }

        let stage_result = {
            let mut handle = ctx.handle.lock();
            concurrent_stage_prepared_write_marker(&mut handle, page_no)
        };
        if let Err(stage_error) = stage_result {
            let error = FrankenError::Internal(format!("MVCC write staging failed: {stage_error}"));
            if let Err(restore_error) = restore_concurrent_state() {
                return Err(FrankenError::Internal(format!("{error}; {restore_error}")));
            }
            return Err(error);
        }

        if let Err(write_error) = self
            .txn
            .borrow_mut()
            .write_page_data(cx, page_no, page_data_base)
        {
            let mut handle = ctx.handle.lock();
            if let Some(prior_page_state) = prior_page_state.as_ref()
                && let Err(restore_error) = concurrent_restore_page_state(
                    &mut handle,
                    &ctx.lock_table,
                    ctx.session_id,
                    prior_page_state,
                )
            {
                return Err(FrankenError::Internal(format!(
                    "pager write_page failed: {write_error}; MVCC state restore failed: {restore_error}"
                )));
            }
            if let Some(page_one_state) = page_one_state.as_ref()
                && let Err(restore_error) = concurrent_restore_page_state(
                    &mut handle,
                    &ctx.lock_table,
                    ctx.session_id,
                    page_one_state,
                )
            {
                return Err(FrankenError::Internal(format!(
                    "pager write_page failed: {write_error}; MVCC page1 restore failed: {restore_error}"
                )));
            }
            return Err(write_error);
        }

        self.clear_stale_synthetic_pending_commit_surface(cx, "write_page")?;
        Ok(())
    }

    fn write_page_internal(
        &self,
        cx: &Cx,
        page_no: PageNumber,
        page_data_base: PageData,
    ) -> Result<()> {
        let Some(ctx) = self.concurrent_context() else {
            return self
                .txn
                .borrow_mut()
                .write_page_data(cx, page_no, page_data_base);
        };

        match Self::classify_concurrent_write_tier(&ctx, page_no) {
            ConcurrentWriteTier::Tier0AlreadyOwned => {
                self.write_page_tier0_already_owned(cx, &ctx, page_no, page_data_base)
            }
            ConcurrentWriteTier::Tier1FirstTouch => {
                self.write_page_tier1_first_touch(cx, &ctx, page_no, page_data_base)
            }
            ConcurrentWriteTier::Tier2CommitSurfaceRare => {
                self.write_page_tier2_commit_surface_rare(cx, &ctx, page_no, page_data_base)
            }
        }
    }
}

const PAGE_LOCK_WAIT_CANCELLATION_POLL: Duration = Duration::from_millis(5);
const PAGE_LOCK_WAIT_FULL_CHECKPOINT_POLL: Duration = Duration::from_millis(50);

fn normalize_owned_page_data(page_size: usize, data: &[u8]) -> Result<PageData> {
    let metrics_enabled = vdbe_metrics_enabled();
    add_vdbe_counter_if(
        metrics_enabled,
        &FSQLITE_VDBE_PAGE_DATA_BORROWED_NORMALIZATION_CALLS_TOTAL,
        1,
    );
    if data.len() == page_size {
        add_vdbe_counter_if(
            metrics_enabled,
            &FSQLITE_VDBE_PAGE_DATA_BORROWED_EXACT_SIZE_COPIES_TOTAL,
            1,
        );
        add_vdbe_counter_if(
            metrics_enabled,
            &FSQLITE_VDBE_PAGE_DATA_NORMALIZED_PAYLOAD_BYTES_TOTAL,
            u64::try_from(data.len()).unwrap_or(u64::MAX),
        );
        return Ok(PageData::from_vec(data.to_vec()));
    }
    if data.len() > page_size {
        return Err(FrankenError::Internal(format!(
            "page buffer exceeds page size invariant: {} > {}",
            data.len(),
            page_size
        )));
    }

    let mut page = vec![0_u8; page_size];
    page[..data.len()].copy_from_slice(data);
    add_vdbe_counter_if(
        metrics_enabled,
        &FSQLITE_VDBE_PAGE_DATA_NORMALIZED_PAYLOAD_BYTES_TOTAL,
        u64::try_from(data.len()).unwrap_or(u64::MAX),
    );
    add_vdbe_counter_if(
        metrics_enabled,
        &FSQLITE_VDBE_PAGE_DATA_NORMALIZED_ZERO_FILL_BYTES_TOTAL,
        u64::try_from(page_size.saturating_sub(data.len())).unwrap_or(u64::MAX),
    );
    Ok(PageData::from_vec(page))
}

fn normalize_page_data_to_size(page_size: usize, data: PageData) -> Result<PageData> {
    let metrics_enabled = vdbe_metrics_enabled();
    add_vdbe_counter_if(
        metrics_enabled,
        &FSQLITE_VDBE_PAGE_DATA_OWNED_NORMALIZATION_CALLS_TOTAL,
        1,
    );
    if data.len() == page_size {
        add_vdbe_counter_if(
            metrics_enabled,
            &FSQLITE_VDBE_PAGE_DATA_OWNED_PASSTHROUGH_TOTAL,
            1,
        );
        return Ok(data);
    }
    if data.len() > page_size {
        return Err(FrankenError::Internal(format!(
            "page buffer exceeds page size invariant: {} > {}",
            data.len(),
            page_size
        )));
    }

    let payload_len = data.len();
    let zero_fill_len = page_size.saturating_sub(payload_len);
    let mut data = data;
    if data.try_zero_extend_owned_to(page_size) {
        add_vdbe_counter_if(
            metrics_enabled,
            &FSQLITE_VDBE_PAGE_DATA_OWNED_IN_PLACE_ZERO_EXTENDS_TOTAL,
            1,
        );
        add_vdbe_counter_if(
            metrics_enabled,
            &FSQLITE_VDBE_PAGE_DATA_NORMALIZED_ZERO_FILL_BYTES_TOTAL,
            u64::try_from(zero_fill_len).unwrap_or(u64::MAX),
        );
        return Ok(data);
    }

    let mut page = vec![0_u8; page_size];
    page[..payload_len].copy_from_slice(data.as_bytes());
    add_vdbe_counter_if(
        metrics_enabled,
        &FSQLITE_VDBE_PAGE_DATA_OWNED_RESIZED_COPIES_TOTAL,
        1,
    );
    add_vdbe_counter_if(
        metrics_enabled,
        &FSQLITE_VDBE_PAGE_DATA_NORMALIZED_PAYLOAD_BYTES_TOTAL,
        u64::try_from(payload_len).unwrap_or(u64::MAX),
    );
    add_vdbe_counter_if(
        metrics_enabled,
        &FSQLITE_VDBE_PAGE_DATA_NORMALIZED_ZERO_FILL_BYTES_TOTAL,
        u64::try_from(zero_fill_len).unwrap_or(u64::MAX),
    );
    Ok(PageData::from_vec(page))
}

fn wait_for_page_lock_holder_change(
    cx: &Cx,
    ctx: &ConcurrentContext,
    page_no: PageNumber,
    remaining: Duration,
) -> Result<bool> {
    // Outer check runs the full `Cx::checkpoint` (observes e-process oracle +
    // native cx plane) before entering the wait loop.
    observe_execution_cancellation(cx)?;

    let Some(holder) = ctx.lock_table.holder(page_no) else {
        return Ok(true);
    };
    if remaining.is_zero() {
        return Ok(false);
    }

    let metrics_enabled = vdbe_metrics_enabled();
    let started = Instant::now();
    let mut next_full_checkpoint = PAGE_LOCK_WAIT_FULL_CHECKPOINT_POLL;
    loop {
        let mut elapsed = started.elapsed();
        // Keep the hot per-slice path cheap, but do not make e-process/native
        // cancellation wait for the whole busy timeout when the same holder
        // stays parked on the page. The old code ran a full checkpoint every
        // 5 ms; this bounds non-local cancellation latency to 50 ms while
        // preserving most of the perf win from the cheap local check.
        if elapsed >= next_full_checkpoint {
            observe_execution_cancellation(cx)?;
            elapsed = started.elapsed();
            next_full_checkpoint = elapsed.saturating_add(PAGE_LOCK_WAIT_FULL_CHECKPOINT_POLL);
        } else if cx.is_cancel_requested() {
            return Err(FrankenError::Abort);
        }

        let wait_budget = remaining.saturating_sub(elapsed);
        if wait_budget.is_zero() {
            return Ok(false);
        }

        let wait_slice = wait_budget.min(PAGE_LOCK_WAIT_CANCELLATION_POLL);
        if ctx
            .lock_table
            .wait_for_holder_change(page_no, holder, wait_slice)
        {
            add_vdbe_counter_if(metrics_enabled, &FSQLITE_VDBE_MVCC_PAGE_LOCK_WAITS_TOTAL, 1);
            add_vdbe_duration_if(
                metrics_enabled,
                &FSQLITE_VDBE_MVCC_PAGE_LOCK_WAIT_TIME_NS_TOTAL,
                started,
            );
            return Ok(true);
        }

        if wait_budget == wait_slice {
            add_vdbe_counter_if(metrics_enabled, &FSQLITE_VDBE_MVCC_PAGE_LOCK_WAITS_TOTAL, 1);
            add_vdbe_duration_if(
                metrics_enabled,
                &FSQLITE_VDBE_MVCC_PAGE_LOCK_WAIT_TIME_NS_TOTAL,
                started,
            );
            return Ok(false);
        }
    }
}

fn track_concurrent_conflict_only_page(
    cx: &Cx,
    ctx: &ConcurrentContext,
    page_no: PageNumber,
    operation: &str,
) -> Result<()> {
    let metrics_enabled = vdbe_metrics_enabled();
    let track_started = metrics_enabled.then(Instant::now);
    let started = Instant::now();
    let deadline = Duration::from_millis(ctx.busy_timeout_ms);

    loop {
        observe_execution_cancellation(cx)?;
        let (track_result, already_tracked, conflicting_commit_seq) = {
            let mut handle = ctx.handle.lock();
            let already_tracked = handle.tracks_write_conflict_page(page_no);
            let conflicting_commit_seq = (!handle.holds_page_lock(page_no))
                .then(|| ctx.commit_index.latest(page_no))
                .flatten()
                .filter(|seq| *seq > ctx.snapshot_high);
            let track_result = conflicting_commit_seq.is_none().then(|| {
                concurrent_track_write_conflict_page(
                    &mut handle,
                    &ctx.lock_table,
                    ctx.session_id,
                    page_no,
                )
            });
            (track_result, already_tracked, conflicting_commit_seq)
        };
        let txn_id = ctx.txn_id;
        let snapshot_high = ctx.snapshot_high.get();

        if already_tracked {
            tracing::debug!(
                txn_id,
                commit_seq = snapshot_high,
                snapshot_high,
                page_id = page_no.get(),
                visibility_decision = "conflict_only_already_tracked",
                conflict_reason = "none",
                operation,
                "mvcc conflict-only page already tracked"
            );
            return Ok(());
        }

        if let Some(conflicting_commit_seq) = conflicting_commit_seq {
            tracing::warn!(
                txn_id,
                commit_seq = conflicting_commit_seq.get(),
                snapshot_high,
                page_id = page_no.get(),
                visibility_decision = "conflict_only_snapshot_stale",
                conflict_reason = "fcw_base_drift",
                operation,
                "mvcc conflict-only page rejected due to stale snapshot"
            );
            return Err(FrankenError::BusySnapshot {
                conflicting_pages: page_no.get().to_string(),
            });
        }

        match track_result.ok_or_else(|| {
            FrankenError::Internal("track result must exist when snapshot is valid".to_owned())
        })? {
            Ok(()) => {
                add_vdbe_counter_if(
                    metrics_enabled,
                    &FSQLITE_VDBE_MVCC_PAGE_ONE_CONFLICT_TRACKS_TOTAL,
                    1,
                );
                if let Some(track_started) = track_started {
                    add_vdbe_duration_if(
                        metrics_enabled,
                        &FSQLITE_VDBE_MVCC_PAGE_ONE_CONFLICT_TRACK_TIME_NS_TOTAL,
                        track_started,
                    );
                }
                tracing::debug!(
                    txn_id,
                    commit_seq = snapshot_high,
                    snapshot_high,
                    page_id = page_no.get(),
                    visibility_decision = "conflict_only_tracked",
                    conflict_reason = "none",
                    operation,
                    "mvcc conflict-only page tracked"
                );
                return Ok(());
            }
            Err(MvccError::Busy) => {
                let remaining = deadline.saturating_sub(started.elapsed());
                if !wait_for_page_lock_holder_change(cx, ctx, page_no, remaining)? {
                    tracing::warn!(
                        txn_id,
                        commit_seq = snapshot_high,
                        snapshot_high,
                        page_id = page_no.get(),
                        visibility_decision = "conflict_only_busy_timeout",
                        conflict_reason = "page_lock_busy",
                        operation,
                        retry_policy = "park_wake",
                        "mvcc conflict-only page exceeded busy timeout"
                    );
                    return Err(FrankenError::Busy);
                }

                tracing::warn!(
                    txn_id,
                    commit_seq = snapshot_high,
                    snapshot_high,
                    page_id = page_no.get(),
                    visibility_decision = "conflict_only_retry",
                    conflict_reason = "page_lock_busy",
                    operation,
                    retry_policy = "park_wake",
                    retry_wait_ms = remaining.as_millis(),
                    "mvcc conflict-only page contention detected"
                );
            }
            Err(e) => {
                tracing::warn!(
                    txn_id,
                    commit_seq = snapshot_high,
                    snapshot_high,
                    page_id = page_no.get(),
                    visibility_decision = "conflict_only_abort",
                    conflict_reason = %e,
                    operation,
                    "mvcc conflict-only page tracking failed"
                );
                return Err(FrankenError::Internal(format!(
                    "MVCC conflict-only page tracking failed: {e}"
                )));
            }
        }
    }
}

impl PageReader for SharedTxnPageIo {
    fn read_page(&self, cx: &Cx, page_no: PageNumber) -> Result<Vec<u8>> {
        if let Some(ctx) = self.concurrent_context() {
            // Read-own-writes visibility: if this txn already wrote the page,
            // return the pager transaction's authoritative staged image first
            // and still record the read for SSI.
            let txn_id = ctx.txn_id;
            let snapshot_high = ctx.snapshot_high.get();
            let (handle, has_staged_write) = {
                let mut handle = ctx.handle.lock();
                // Read-only transactions never stage page state, so skip the
                // compound probe entirely on the empty-map fast path.
                let (is_freed, has_staged_write) = if concurrent_has_page_state(&handle) {
                    concurrent_page_read_status(&handle, page_no)
                } else {
                    (false, false)
                };
                if is_freed {
                    return Err(FrankenError::DatabaseCorrupt {
                        detail: format!(
                            "page {} was freed earlier in concurrent transaction {}",
                            page_no.get(),
                            txn_id
                        ),
                    });
                }
                handle.record_read(page_no);
                (handle, has_staged_write)
            };
            drop(handle);

            if has_staged_write {
                let page = self.txn.borrow().get_page(cx, page_no)?;
                tracing::debug!(
                    txn_id,
                    commit_seq = snapshot_high,
                    snapshot_high,
                    page_id = page_no.get(),
                    visibility_decision = "write_set_hit",
                    conflict_reason = "none",
                    "mvcc visibility decision"
                );
                return Ok(page.into_vec());
            }

            tracing::debug!(
                txn_id,
                commit_seq = snapshot_high,
                snapshot_high,
                page_id = page_no.get(),
                visibility_decision = "snapshot_pager_read",
                conflict_reason = "none",
                "mvcc visibility decision"
            );
        }

        let page = self.txn.borrow().get_page(cx, page_no)?.into_vec();
        Ok(page)
    }

    // bd-perf: Override to avoid Vec<u8> round-trip (read_page returns Vec,
    // default read_page_data wraps in PageData — wasteful 4KB alloc+copy).
    fn read_page_data(&self, cx: &Cx, page_no: PageNumber) -> Result<PageData> {
        if let Some(ctx) = self.concurrent_context() {
            let has_staged_write = {
                let mut handle = ctx.handle.lock();
                let (is_freed, has_staged_write) = if concurrent_has_page_state(&handle) {
                    concurrent_page_read_status(&handle, page_no)
                } else {
                    (false, false)
                };
                if is_freed {
                    return Err(FrankenError::DatabaseCorrupt {
                        detail: format!(
                            "page {} was freed earlier in concurrent transaction {}",
                            page_no.get(),
                            ctx.txn_id
                        ),
                    });
                }
                handle.record_read(page_no);
                has_staged_write
            };

            if has_staged_write {
                return self.txn.borrow().get_page(cx, page_no);
            }
        }
        self.txn.borrow().get_page(cx, page_no)
    }

    fn read_btree_page_data(&self, cx: &Cx, page_no: PageNumber) -> Result<PageData> {
        if let Some(ctx) = self.concurrent_context() {
            let has_staged_write = {
                let handle = ctx.handle.lock();
                let (is_freed, has_staged_write) = if concurrent_has_page_state(&handle) {
                    concurrent_page_read_status(&handle, page_no)
                } else {
                    (false, false)
                };
                if is_freed {
                    return Err(FrankenError::DatabaseCorrupt {
                        detail: format!(
                            "page {} was freed earlier in concurrent transaction {}",
                            page_no.get(),
                            ctx.txn_id
                        ),
                    });
                }
                has_staged_write
            };

            if has_staged_write {
                return self.txn.borrow().get_page(cx, page_no);
            }
        }
        self.txn.borrow().get_page(cx, page_no)
    }

    fn record_read_witness(&self, _cx: &Cx, key: WitnessKey) {
        if let Some(ctx) = self.concurrent_context() {
            ctx.handle.lock().record_read_witness(key);
        }
    }

    fn is_dirty(&self, page_no: PageNumber) -> bool {
        if let Some(ctx) = self.concurrent_context() {
            return ctx.handle.lock().tracks_write_conflict_page(page_no);
        }
        false
    }
}

impl PageWriter for SharedTxnPageIo {
    fn write_page(&mut self, cx: &Cx, page_no: PageNumber, data: &[u8]) -> Result<()> {
        let page_size = self.txn.borrow().page_size().as_usize();
        let page_data = normalize_owned_page_data(page_size, data)?;
        self.write_page_internal(cx, page_no, page_data)
    }

    fn write_page_data(&mut self, cx: &Cx, page_no: PageNumber, data: PageData) -> Result<()> {
        let page_size = self.txn.borrow().page_size().as_usize();
        let page_data = normalize_page_data_to_size(page_size, data)?;
        self.write_page_internal(cx, page_no, page_data)
    }

    fn try_mutate_staged_page_data(
        &mut self,
        page_no: PageNumber,
        f: &mut dyn FnMut(&mut PageData),
    ) -> bool {
        if let Some(ctx) = self.concurrent_context() {
            let has_staged_write = {
                let handle = ctx.handle.lock();
                let (is_freed, has_staged_write) = concurrent_page_read_status(&handle, page_no);
                !is_freed && has_staged_write
            };
            if !has_staged_write {
                return false;
            }
        }
        self.txn
            .borrow_mut()
            .try_mutate_staged_page_data(page_no, f)
    }

    fn allocate_page(&mut self, cx: &Cx) -> Result<PageNumber> {
        let concurrent = self.concurrent_context();
        let page_one_tracking_required = self
            .concurrent_context()
            .as_ref()
            .map(|_| {
                self.txn
                    .borrow()
                    .allocate_page_requires_page_one_conflict_tracking()
            })
            .transpose()?
            .unwrap_or(false);
        let page_one_state = concurrent.as_ref().and_then(|ctx| {
            let handle = ctx.handle.lock();
            if page_one_tracking_required {
                if !handle.tracks_write_conflict_page(PageNumber::ONE) {
                    Some(concurrent_page_state(&handle, PageNumber::ONE))
                } else {
                    None
                }
            } else {
                None
            }
        });
        if let Some(ctx) = concurrent.as_ref()
            && page_one_state.is_some()
        {
            track_concurrent_conflict_only_page(cx, ctx, PageNumber::ONE, "allocate_page")?;
        }
        let allocate_result = self.txn.borrow_mut().allocate_page(cx);
        if let Err(allocate_error) = &allocate_result {
            if let (Some(ctx), Some(page_one_state)) =
                (concurrent.as_ref(), page_one_state.as_ref())
            {
                let mut handle = ctx.handle.lock();
                if let Err(restore_error) = concurrent_restore_page_state(
                    &mut handle,
                    &ctx.lock_table,
                    ctx.session_id,
                    page_one_state,
                ) {
                    return Err(FrankenError::Internal(format!(
                        "pager allocate_page failed: {allocate_error}; MVCC state restore failed: {restore_error}"
                    )));
                }
            }
        }
        let page_no = allocate_result?;
        self.clear_stale_synthetic_pending_commit_surface(cx, "allocate_page")?;
        Ok(page_no)
    }

    fn free_page(&mut self, cx: &Cx, page_no: PageNumber) -> Result<()> {
        let concurrent = self.concurrent_context();
        let page_one_tracking_required = self
            .concurrent_context()
            .as_ref()
            .map(|_| {
                self.txn
                    .borrow()
                    .free_page_requires_page_one_conflict_tracking(page_no)
            })
            .transpose()?
            .unwrap_or(false);
        let (prior_page_state, page_one_state) = if let Some(ctx) = concurrent.as_ref() {
            let handle = ctx.handle.lock();
            let prior_page_state = Some(concurrent_page_state(&handle, page_no));
            let page_one_state = if page_one_tracking_required {
                if !handle.tracks_write_conflict_page(PageNumber::ONE) {
                    Some(concurrent_page_state(&handle, PageNumber::ONE))
                } else {
                    None
                }
            } else {
                None
            };
            (prior_page_state, page_one_state)
        } else {
            (None, None)
        };
        if let Some(ctx) = concurrent.as_ref() {
            if page_one_state.is_some() {
                track_concurrent_conflict_only_page(cx, ctx, PageNumber::ONE, "free_page")?;
            }
            let concurrent_free_result = (|| -> Result<()> {
                let started = Instant::now();
                let deadline = Duration::from_millis(ctx.busy_timeout_ms);

                loop {
                    observe_execution_cancellation(cx)?;
                    let (free_result, conflicting_commit_seq) = {
                        let mut handle = ctx.handle.lock();
                        let conflicting_commit_seq = (!handle.holds_page_lock(page_no))
                            .then(|| ctx.commit_index.latest(page_no))
                            .flatten()
                            .filter(|seq| *seq > ctx.snapshot_high);
                        let free_result = conflicting_commit_seq.is_none().then(|| {
                            concurrent_free_page(
                                &mut handle,
                                &ctx.lock_table,
                                ctx.session_id,
                                page_no,
                            )
                        });
                        (free_result, conflicting_commit_seq)
                    };
                    let txn_id = ctx.txn_id;
                    let snapshot_high = ctx.snapshot_high.get();

                    if let Some(conflicting_commit_seq) = conflicting_commit_seq {
                        tracing::warn!(
                            txn_id,
                            commit_seq = conflicting_commit_seq.get(),
                            snapshot_high,
                            page_id = page_no.get(),
                            visibility_decision = "free_snapshot_stale",
                            conflict_reason = "fcw_base_drift",
                            "mvcc free rejected due to stale snapshot"
                        );
                        return Err(FrankenError::BusySnapshot {
                            conflicting_pages: page_no.get().to_string(),
                        });
                    }

                    match free_result.ok_or_else(|| {
                        FrankenError::Internal(
                            "free result must exist when snapshot is valid".to_owned(),
                        )
                    })? {
                        Ok(()) => {
                            tracing::debug!(
                                txn_id,
                                commit_seq = snapshot_high,
                                snapshot_high,
                                page_id = page_no.get(),
                                visibility_decision = "free_set_recorded",
                                conflict_reason = "none",
                                "mvcc free visibility decision"
                            );
                            return Ok(());
                        }
                        Err(MvccError::Busy) => {
                            let remaining = deadline.saturating_sub(started.elapsed());
                            if !wait_for_page_lock_holder_change(cx, ctx, page_no, remaining)? {
                                tracing::warn!(
                                    txn_id,
                                    commit_seq = snapshot_high,
                                    snapshot_high,
                                    page_id = page_no.get(),
                                    visibility_decision = "free_busy_timeout",
                                    conflict_reason = "page_lock_busy",
                                    retry_policy = "park_wake",
                                    "mvcc free conflict exceeded busy timeout"
                                );
                                return Err(FrankenError::Busy);
                            }

                            tracing::warn!(
                                txn_id,
                                commit_seq = snapshot_high,
                                snapshot_high,
                                page_id = page_no.get(),
                                visibility_decision = "free_retry",
                                conflict_reason = "page_lock_busy",
                                retry_policy = "park_wake",
                                retry_wait_ms = remaining.as_millis(),
                                "mvcc free conflict detected"
                            );
                        }
                        Err(e) => {
                            tracing::warn!(
                                txn_id,
                                commit_seq = snapshot_high,
                                snapshot_high,
                                page_id = page_no.get(),
                                visibility_decision = "free_abort",
                                conflict_reason = %e,
                                "mvcc free failed"
                            );
                            return Err(FrankenError::Internal(format!(
                                "MVCC free_page failed: {e}"
                            )));
                        }
                    }
                }
            })();
            if let Err(error) = concurrent_free_result {
                let mut handle = ctx.handle.lock();
                if let Some(prior_page_state) = prior_page_state.as_ref()
                    && let Err(restore_error) = concurrent_restore_page_state(
                        &mut handle,
                        &ctx.lock_table,
                        ctx.session_id,
                        prior_page_state,
                    )
                {
                    return Err(FrankenError::Internal(format!(
                        "{error}; MVCC page restore failed: {restore_error}"
                    )));
                }
                if let Some(page_one_state) = page_one_state.as_ref()
                    && let Err(restore_error) = concurrent_restore_page_state(
                        &mut handle,
                        &ctx.lock_table,
                        ctx.session_id,
                        page_one_state,
                    )
                {
                    return Err(FrankenError::Internal(format!(
                        "{error}; MVCC page1 restore failed: {restore_error}"
                    )));
                }
                return Err(error);
            }
        }
        let free_result = self.txn.borrow_mut().free_page(cx, page_no);
        if let Err(free_error) = free_result {
            if let (Some(ctx), Some(prior_page_state)) =
                (concurrent.as_ref(), prior_page_state.as_ref())
            {
                let mut handle = ctx.handle.lock();
                if let Err(restore_error) = concurrent_restore_page_state(
                    &mut handle,
                    &ctx.lock_table,
                    ctx.session_id,
                    prior_page_state,
                ) {
                    return Err(FrankenError::Internal(format!(
                        "pager free_page failed: {free_error}; MVCC state restore failed: {restore_error}"
                    )));
                }
                if let Some(page_one_state) = page_one_state.as_ref() {
                    if let Err(restore_error) = concurrent_restore_page_state(
                        &mut handle,
                        &ctx.lock_table,
                        ctx.session_id,
                        page_one_state,
                    ) {
                        return Err(FrankenError::Internal(format!(
                            "pager free_page failed: {free_error}; MVCC page1 restore failed: {restore_error}"
                        )));
                    }
                }
            }
            return Err(free_error);
        }
        self.clear_stale_synthetic_pending_commit_surface(cx, "free_page")?;
        Ok(())
    }

    fn record_write_witness(&mut self, cx: &Cx, key: WitnessKey) {
        if let Some(ctx) = self.concurrent_context() {
            ctx.handle.lock().record_write_witness(key);
            return;
        }
        self.txn.borrow_mut().record_write_witness(cx, key);
    }
}

// ── Time-Travel Page I/O ──────────────────────────────────────────────
//
// Wraps a `SharedTxnPageIo` and a `TimeTravelSnapshot` + `VersionStore`
// to intercept page reads and return historical page versions when
// available. Falls back to the underlying transaction for pages not
// present in the version store (i.e., pages unchanged since the
// time-travel target).

/// Read-only page I/O that serves historical page versions for
/// time-travel queries (`FOR SYSTEM_TIME AS OF ...`).
///
/// On `read_page`, the wrapper first resolves the page through the MVCC
/// `VersionStore` at the snapshot's commit sequence. If a historical
/// version is found, its `PageData` is returned directly. Otherwise the
/// read falls through to the underlying transaction (the page has not
/// changed since the target commit, so the current version is correct).
///
/// Write operations are unconditionally rejected — time-travel cursors
/// are strictly read-only.
#[derive(Clone)]
struct TimeTravelPageIo {
    /// Underlying transaction page I/O for fall-through reads.
    inner: SharedTxnPageIo,
    /// MVCC version store for historical page resolution.
    version_store: Arc<VersionStore>,
    /// The pinned time-travel snapshot.
    snapshot: TimeTravelSnapshot,
}

impl std::fmt::Debug for TimeTravelPageIo {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("TimeTravelPageIo")
            .field("inner", &self.inner)
            .field("version_store", &"<Arc<VersionStore>>")
            .field(
                "target_commit_seq",
                &self.snapshot.target_commit_seq().get(),
            )
            .finish()
    }
}

impl PageReader for TimeTravelPageIo {
    fn read_page(&self, cx: &Cx, page_no: PageNumber) -> Result<Vec<u8>> {
        // Try to resolve the page at the historical snapshot first.
        let vs = &self.version_store;
        if let Some(idx) = self.snapshot.resolve_page(vs, page_no) {
            if let Some(version) = vs.get_version(idx) {
                tracing::trace!(
                    page_id = page_no.get(),
                    commit_seq = self.snapshot.target_commit_seq().get(),
                    "time-travel: serving historical page from version store"
                );
                return Ok(version.data.into_vec());
            }
        }

        // The VersionStore has no version for this page at this snapshot.
        //
        // Fallthrough to the current transaction is only correct when the
        // VersionStore is actively tracking page versions (i.e., Native mode
        // with a populated commit stream). In that case, absence from the
        // store means the page has not changed since the target commit, so
        // the current on-disk version is the correct historical version.
        //
        // However, if the VersionStore is empty (page_count == 0), the MVCC
        // subsystem is not tracking any historical state. Falling through
        // would silently return current data, which is incorrect and
        // violates the time-travel query contract. In that case, we must
        // fail explicitly.
        let store_has_versions = vs.page_count() > 0;

        if !store_has_versions {
            tracing::warn!(
                page_id = page_no.get(),
                commit_seq = self.snapshot.target_commit_seq().get(),
                "time-travel: VersionStore is empty — cannot serve historical \
                 page; historical data not available for this commit"
            );
            return Err(FrankenError::Internal(format!(
                "time-travel query failed: historical data not available for \
                 commit_seq={} (MVCC version store has no historical page \
                 versions; the database may be running in compatibility mode \
                 where time-travel is not yet supported)",
                self.snapshot.target_commit_seq().get(),
            )));
        }

        // VersionStore is populated but this specific page is absent —
        // the page has not changed since the target commit, so the
        // current transaction's version is correct.
        tracing::trace!(
            page_id = page_no.get(),
            commit_seq = self.snapshot.target_commit_seq().get(),
            "time-travel: page not in version store (unchanged since target \
             commit), falling through to txn"
        );
        self.inner.read_page(cx, page_no)
    }

    fn read_btree_page_data(&self, cx: &Cx, page_no: PageNumber) -> Result<PageData> {
        let vs = &self.version_store;
        if let Some(idx) = self.snapshot.resolve_page(vs, page_no)
            && let Some(version) = vs.get_version(idx)
        {
            tracing::trace!(
                page_id = page_no.get(),
                commit_seq = self.snapshot.target_commit_seq().get(),
                "time-travel: serving historical B-tree page from version store"
            );
            return Ok(version.data);
        }

        if vs.page_count() == 0 {
            tracing::warn!(
                page_id = page_no.get(),
                commit_seq = self.snapshot.target_commit_seq().get(),
                "time-travel: VersionStore is empty — cannot serve historical \
                 B-tree page; historical data not available for this commit"
            );
            return Err(FrankenError::Internal(format!(
                "time-travel query failed: historical data not available for \
                 commit_seq={} (MVCC version store has no historical page \
                 versions; the database may be running in compatibility mode \
                 where time-travel is not yet supported)",
                self.snapshot.target_commit_seq().get(),
            )));
        }

        self.inner.read_btree_page_data(cx, page_no)
    }
}

impl PageWriter for TimeTravelPageIo {
    fn write_page(&mut self, _cx: &Cx, _page_no: PageNumber, _data: &[u8]) -> Result<()> {
        Err(FrankenError::Internal(
            "time-travel cursors are read-only: write_page not permitted".to_owned(),
        ))
    }

    fn allocate_page(&mut self, _cx: &Cx) -> Result<PageNumber> {
        Err(FrankenError::Internal(
            "time-travel cursors are read-only: allocate_page not permitted".to_owned(),
        ))
    }

    fn free_page(&mut self, _cx: &Cx, _page_no: PageNumber) -> Result<()> {
        Err(FrankenError::Internal(
            "time-travel cursors are read-only: free_page not permitted".to_owned(),
        ))
    }

    fn record_write_witness(&mut self, _cx: &Cx, _key: WitnessKey) {}
}

// ── Cursor Backend Enum ────────────────────────────────────────────────
//
// Allows StorageCursor to work in three modes:
// - `Mem`: backed by MemPageStore (Phase 4 / tests)
// - `Txn`: backed by SharedTxnPageIo (Phase 5 production path)
// - `TimeTravel`: backed by TimeTravelPageIo (historical snapshot reads)

/// Backend for a storage cursor, dispatching between in-memory,
/// transaction-backed, and time-travel page I/O.
enum CursorBackend {
    /// In-memory page store (used by tests and Phase 4 fallback).
    Mem(BtCursor<MemPageStore>),
    /// Real pager transaction (Phase 5 production path, bd-2a3y).
    Txn(BtCursor<SharedTxnPageIo>),
    /// Time-travel snapshot (historical reads via MVCC version store).
    TimeTravel(BtCursor<TimeTravelPageIo>),
}

impl std::fmt::Debug for CursorBackend {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Mem(c) => f.debug_tuple("Mem").field(c).finish(),
            Self::Txn(c) => f.debug_tuple("Txn").field(c).finish(),
            Self::TimeTravel(c) => f.debug_tuple("TimeTravel").field(c).finish(),
        }
    }
}

impl CursorBackend {
    /// Returns `true` if this cursor is backed by the in-memory page store.
    #[must_use]
    fn is_mem(&self) -> bool {
        matches!(self, Self::Mem(_))
    }

    /// Returns `true` if this cursor is backed by the real pager transaction.
    #[must_use]
    fn is_txn(&self) -> bool {
        matches!(self, Self::Txn(_))
    }

    /// Returns `true` if this cursor is a time-travel cursor.
    #[must_use]
    #[allow(dead_code)]
    fn is_time_travel(&self) -> bool {
        matches!(self, Self::TimeTravel(_))
    }

    /// Returns a string identifying the backend kind for diagnostics.
    #[must_use]
    #[allow(dead_code)]
    fn kind_str(&self) -> &'static str {
        match self {
            Self::Mem(_) => "mem",
            Self::Txn(_) => "txn",
            Self::TimeTravel(_) => "time_travel",
        }
    }

    /// Whether the underlying B-tree cursor is for a table (intkey) B-tree.
    #[must_use]
    fn is_table_btree(&self) -> bool {
        match self {
            Self::Mem(c) => c.is_table(),
            Self::Txn(c) => c.is_table(),
            Self::TimeTravel(c) => c.is_table(),
        }
    }

    #[must_use]
    fn usable_size(&self) -> u32 {
        match self {
            Self::Mem(c) => c.usable_size(),
            Self::Txn(c) => c.usable_size(),
            Self::TimeTravel(c) => c.usable_size(),
        }
    }

    #[must_use]
    fn page_size(&self) -> u32 {
        match self {
            Self::Mem(c) => c.page_size(),
            Self::Txn(c) => c.page_size(),
            Self::TimeTravel(c) => c.page_size(),
        }
    }

    #[must_use]
    fn index_desc_flags(&self) -> &[bool] {
        match self {
            Self::Mem(c) => c.index_desc_flags(),
            Self::Txn(c) => c.index_desc_flags(),
            Self::TimeTravel(c) => c.index_desc_flags(),
        }
    }

    #[must_use]
    fn index_collations(&self) -> &[Option<String>] {
        match self {
            Self::Mem(c) => c.index_collations(),
            Self::Txn(c) => c.index_collations(),
            Self::TimeTravel(c) => c.index_collations(),
        }
    }

    #[must_use]
    fn collation_registry(&self) -> Arc<Mutex<CollationRegistry>> {
        match self {
            Self::Mem(c) => c.collation_registry(),
            Self::Txn(c) => c.collation_registry(),
            Self::TimeTravel(c) => c.collation_registry(),
        }
    }
}

/// Dispatch B-tree cursor operations across all backends.
impl CursorBackend {
    fn first(&mut self, cx: &Cx) -> Result<bool> {
        match self {
            Self::Mem(c) => c.first(cx),
            Self::Txn(c) => c.first(cx),
            Self::TimeTravel(c) => c.first(cx),
        }
    }

    fn last(&mut self, cx: &Cx) -> Result<bool> {
        match self {
            Self::Mem(c) => c.last(cx),
            Self::Txn(c) => c.last(cx),
            Self::TimeTravel(c) => c.last(cx),
        }
    }

    fn next(&mut self, cx: &Cx) -> Result<bool> {
        match self {
            Self::Mem(c) => c.next(cx),
            Self::Txn(c) => c.next(cx),
            Self::TimeTravel(c) => c.next(cx),
        }
    }

    /// bd-wwqen.1: Count all rows by walking leaf page headers without
    /// decoding cell payloads. Much cheaper than first()/while next().
    fn count_all_rows(&mut self, cx: &Cx) -> Result<i64> {
        match self {
            Self::Mem(c) => c.count_all_rows(cx),
            Self::Txn(c) => c.count_all_rows(cx),
            Self::TimeTravel(c) => c.count_all_rows(cx),
        }
    }

    fn prev(&mut self, cx: &Cx) -> Result<bool> {
        match self {
            Self::Mem(c) => c.prev(cx),
            Self::Txn(c) => c.prev(cx),
            Self::TimeTravel(c) => c.prev(cx),
        }
    }

    fn eof(&self) -> bool {
        match self {
            Self::Mem(c) => c.eof(),
            Self::Txn(c) => c.eof(),
            Self::TimeTravel(c) => c.eof(),
        }
    }

    fn rowid(&self, cx: &Cx) -> Result<i64> {
        match self {
            Self::Mem(c) => c.rowid(cx),
            Self::Txn(c) => c.rowid(cx),
            Self::TimeTravel(c) => c.rowid(cx),
        }
    }

    fn payload(&self, cx: &Cx) -> Result<Vec<u8>> {
        match self {
            Self::Mem(c) => c.payload(cx),
            Self::Txn(c) => c.payload(cx),
            Self::TimeTravel(c) => c.payload(cx),
        }
    }

    fn payload_into(&self, cx: &Cx, buf: &mut Vec<u8>) -> Result<()> {
        match self {
            Self::Mem(c) => c.payload_into(cx, buf),
            Self::Txn(c) => c.payload_into(cx, buf),
            Self::TimeTravel(c) => c.payload_into(cx, buf),
        }
    }

    fn payload_prefix_into(
        &self,
        cx: &Cx,
        max_prefix_bytes: usize,
        buf: &mut Vec<u8>,
    ) -> Result<()> {
        match self {
            Self::Mem(c) => c.payload_prefix_into(cx, max_prefix_bytes, buf),
            Self::Txn(c) => c.payload_prefix_into(cx, max_prefix_bytes, buf),
            Self::TimeTravel(c) => c.payload_prefix_into(cx, max_prefix_bytes, buf),
        }
    }

    fn try_probe_current_first_index_key_integer_local(
        &self,
        probe_value: i64,
    ) -> Result<Option<(bool, i64)>> {
        match self {
            Self::Mem(c) => c.try_probe_current_first_index_key_integer_local(probe_value),
            Self::Txn(c) => c.try_probe_current_first_index_key_integer_local(probe_value),
            Self::TimeTravel(c) => c.try_probe_current_first_index_key_integer_local(probe_value),
        }
    }

    fn count_equal_first_index_key_run_integer_local_segment(
        &mut self,
        cx: &Cx,
        probe_value: i64,
    ) -> Result<FirstIndexKeyIntegerLocalRunSegment> {
        match self {
            Self::Mem(c) => {
                c.count_equal_first_index_key_run_integer_local_segment(cx, probe_value)
            }
            Self::Txn(c) => {
                c.count_equal_first_index_key_run_integer_local_segment(cx, probe_value)
            }
            Self::TimeTravel(c) => {
                c.count_equal_first_index_key_run_integer_local_segment(cx, probe_value)
            }
        }
    }

    fn table_move_to(&mut self, cx: &Cx, rowid: i64) -> Result<SeekResult> {
        match self {
            Self::Mem(c) => c.table_move_to(cx, rowid),
            Self::Txn(c) => c.table_move_to(cx, rowid),
            Self::TimeTravel(c) => c.table_move_to(cx, rowid),
        }
    }

    fn table_advance_to(&mut self, cx: &Cx, rowid: i64) -> Result<SeekResult> {
        match self {
            Self::Mem(c) => c.advance_to(cx, rowid),
            Self::Txn(c) => c.advance_to(cx, rowid),
            Self::TimeTravel(c) => c.advance_to(cx, rowid),
        }
    }

    fn table_insert(&mut self, cx: &Cx, rowid: i64, data: &[u8]) -> Result<()> {
        match self {
            Self::Mem(c) => c.table_insert(cx, rowid, data),
            Self::Txn(c) => c.table_insert(cx, rowid, data),
            Self::TimeTravel(_) => Err(FrankenError::Internal(
                "time-travel cursors are read-only: table_insert not permitted".to_owned(),
            )),
        }
    }

    fn table_insert_prechecked_absent(&mut self, cx: &Cx, rowid: i64, data: &[u8]) -> Result<()> {
        match self {
            Self::Mem(c) => c.table_insert_prechecked_absent(cx, rowid, data),
            Self::Txn(c) => c.table_insert_prechecked_absent(cx, rowid, data),
            Self::TimeTravel(_) => Err(FrankenError::Internal(
                "time-travel cursors are read-only: table_insert not permitted".to_owned(),
            )),
        }
    }

    fn table_refresh_rightmost_leaf_cache_after_insert(
        &mut self,
        cx: &Cx,
        rowid: i64,
    ) -> Result<()> {
        match self {
            Self::Mem(c) => c.table_refresh_rightmost_leaf_cache_after_insert(cx, rowid),
            Self::Txn(c) => c.table_refresh_rightmost_leaf_cache_after_insert(cx, rowid),
            Self::TimeTravel(_) => Err(FrankenError::Internal(
                "time-travel cursors are read-only: table_insert not permitted".to_owned(),
            )),
        }
    }

    fn table_append_after_last_position(&mut self, cx: &Cx, rowid: i64, data: &[u8]) -> Result<()> {
        match self {
            Self::Mem(c) => c.table_append_after_last_position(cx, rowid, data),
            Self::Txn(c) => c.table_append_after_last_position(cx, rowid, data),
            Self::TimeTravel(_) => Err(FrankenError::Internal(
                "time-travel cursors are read-only: table_insert not permitted".to_owned(),
            )),
        }
    }

    fn table_append_after_last_position_with_writer<W>(
        &mut self,
        cx: &Cx,
        rowid: i64,
        payload_len: usize,
        writer: W,
    ) -> Result<bool>
    where
        W: FnOnce(&mut [u8]) -> Result<()>,
    {
        match self {
            Self::Mem(c) => {
                c.table_append_after_last_position_with_writer(cx, rowid, payload_len, writer)
            }
            Self::Txn(c) => {
                c.table_append_after_last_position_with_writer(cx, rowid, payload_len, writer)
            }
            Self::TimeTravel(_) => Err(FrankenError::Internal(
                "time-travel cursors are read-only: table_insert not permitted".to_owned(),
            )),
        }
    }

    fn delete(&mut self, cx: &Cx) -> Result<()> {
        match self {
            Self::Mem(c) => c.delete(cx),
            Self::Txn(c) => c.delete(cx),
            Self::TimeTravel(_) => Err(FrankenError::Internal(
                "time-travel cursors are read-only: delete not permitted".to_owned(),
            )),
        }
    }

    /// Position the cursor at the given key in an index B-tree.
    fn index_move_to(&mut self, cx: &Cx, key: &[u8]) -> Result<SeekResult> {
        match self {
            Self::Mem(c) => c.index_move_to(cx, key),
            Self::Txn(c) => c.index_move_to(cx, key),
            Self::TimeTravel(c) => c.index_move_to(cx, key),
        }
    }

    /// Insert a key into an index B-tree.
    fn index_insert(&mut self, cx: &Cx, key: &[u8]) -> Result<()> {
        match self {
            Self::Mem(c) => c.index_insert(cx, key),
            Self::Txn(c) => c.index_insert(cx, key),
            Self::TimeTravel(_) => Err(FrankenError::Internal(
                "time-travel cursors are read-only: index_insert not permitted".to_owned(),
            )),
        }
    }

    /// Insert a key into a UNIQUE index B-tree and report whether the key
    /// landed after the previously-existing rightmost key.
    fn index_insert_unique_with_rightmost_report(
        &mut self,
        cx: &Cx,
        key: &[u8],
        n_unique_cols: usize,
        columns_label: &str,
    ) -> Result<bool> {
        match self {
            Self::Mem(c) => {
                c.index_insert_unique_with_rightmost_report(cx, key, n_unique_cols, columns_label)
            }
            Self::Txn(c) => {
                c.index_insert_unique_with_rightmost_report(cx, key, n_unique_cols, columns_label)
            }
            Self::TimeTravel(_) => Err(FrankenError::Internal(
                "time-travel cursors are read-only: index_insert_unique not permitted".to_owned(),
            )),
        }
    }

    /// Append an index key from the current rightmost cursor position.
    fn index_append_after_current_rightmost_position(
        &mut self,
        cx: &Cx,
        key: &[u8],
    ) -> Result<bool> {
        match self {
            Self::Mem(c) => c.index_append_after_current_rightmost_position(cx, key),
            Self::Txn(c) => c.index_append_after_current_rightmost_position(cx, key),
            Self::TimeTravel(_) => Err(FrankenError::Internal(
                "time-travel cursors are read-only: index_insert not permitted".to_owned(),
            )),
        }
    }

    /// Force the cursor into EOF state so subsequent reads return NULL.
    ///
    /// Used by `OP_NullRow` to satisfy the SQLite contract that Column/Rowid
    /// after NullRow must return NULL.
    fn clear_position(&mut self) {
        match self {
            Self::Mem(c) => c.invalidate(),
            Self::Txn(c) => c.invalidate(),
            Self::TimeTravel(c) => c.invalidate(),
        }
    }

    #[must_use]
    fn position_stamp(&self) -> Option<CursorPositionStamp> {
        match self {
            Self::Mem(c) => c.position_stamp(),
            Self::Txn(c) => c.position_stamp(),
            Self::TimeTravel(c) => c.position_stamp(),
        }
    }
}

/// Storage-backed table cursor used by `OpenRead` and `OpenWrite`.
///
/// In Phase 5, `cursor` may be backed by either an in-memory [`MemPageStore`]
/// (for tests / Phase 4 fallback) or a real pager transaction via
/// [`SharedTxnPageIo`] (production path, bd-2a3y).
///
/// Decode/materialization scratch is owned by the cursor and reused only while
/// the cursor remains positioned on the same physical row image. Position
/// changes or write-path mutations invalidate it explicitly; nothing is shared
/// across cursors, statements, or transactions.
type RowDecodeScratch = fsqlite_types::record::RecordDecodeScratch;

#[derive(Debug)]
struct StorageCursor {
    cursor: CursorBackend,
    cx: Cx,
    /// Whether this cursor was opened for writing (`OpenWrite`).
    writable: bool,
    /// Stable root page associated with this cursor.
    root_page: i32,
    /// Rowid allocation mode for this root page.
    rowid_mode: RowIdMode,
    /// AUTOINCREMENT lower bound loaded once per execution for this cursor.
    autoincrement_high_water: i64,
    /// Highest rowid allocated by `NewRowid` on this cursor (bd-1yi8).
    /// Ensures consecutive allocations return unique values even when
    /// no Insert has been issued between them.
    last_alloc_rowid: i64,
    /// Pre-allocated buffer to read payloads into without allocating.
    payload_buf: Vec<u8>,
    /// Scratch buffer for parsing target index keys.
    target_vals_buf: Vec<SqliteValue>,
    /// Scratch buffer for parsing current index keys.
    cur_vals_buf: Vec<SqliteValue>,
    /// Cursor-owned decode/materialization scratch for the current row image.
    row_decode: RowDecodeScratch,
    /// Cache the cursor's physical position to avoid redundant payload reads.
    last_position_stamp: Option<CursorPositionStamp>,
    /// Last rowid known to have been inserted on the right edge via this cursor.
    ///
    /// This is intentionally stricter than "last inserted rowid". We only keep
    /// it when the caller proved the insert landed at EOF/rightmost, because
    /// only then can a strictly larger next rowid safely reuse the append path.
    last_successful_insert_rowid: Option<i64>,
    /// Last UNIQUE index prefix known to have been inserted at the right edge.
    ///
    /// For monotonic composite UNIQUE streams, this lets `IdxInsert` skip the
    /// duplicate-probe seek and append from the current right-edge position.
    last_rightmost_unique_index_prefix: Option<Vec<SqliteValue>>,
    /// Cursor position that produced `last_rightmost_unique_index_prefix`.
    last_rightmost_unique_index_position: Option<CursorPositionStamp>,
    /// Cached rowid for the current cursor position (avoids repeated B-tree lookups).
    cached_rowid: Option<i64>,
    /// Cached result of payload_includes_rowid_alias check for the current row.
    payload_includes_rowid_alias: Option<bool>,
    /// Schema-derived IPK column index for this root page (avoids per-Column HashMap probes).
    ipk_col_idx: Option<usize>,
    /// Schema-derived column count for this root page.
    table_column_count: Option<usize>,
    /// Schema-derived first NOT NULL non-IPK column for this root page.
    first_not_null_non_ipk_col: Option<usize>,
}

/// Lightweight version token for `MemDatabase` undo/rollback (bd-g6eo).
///
/// This is the MVCC-style snapshot identity for the in-memory store.
/// Returned by [`MemDatabase::undo_version`] and consumed by
/// [`MemDatabase::rollback_to`] to identify undo save-points.
/// The token is just the undo-log length — O(1) to capture, no cloning.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[must_use]
pub struct MemDbVersionToken(usize);

#[derive(Debug, Clone)]
#[allow(dead_code)] // Variants constructed by MemDatabase methods not yet wired to VDBE opcodes.
enum MemDbUndoOp {
    CreateTable {
        root_page: i32,
        prev_next_root_page: i32,
    },
    DestroyTable {
        root_page: i32,
        table: MemTable,
    },
    ClearTable {
        root_page: i32,
        table: MemTable,
    },
    BumpRowid {
        root_page: i32,
        prev_next_rowid: i64,
    },
    UpsertRow {
        root_page: i32,
        rowid: i64,
        prev_next_rowid: i64,
        old_values: Option<MemRowValues>,
    },
    DeleteRow {
        root_page: i32,
        row: MemRow,
        prev_next_rowid: i64,
    },
}

impl MemDbUndoOp {
    fn undo(self, db: &mut MemDatabase) {
        match self {
            Self::CreateTable {
                root_page,
                prev_next_root_page,
            } => {
                db.tables.remove(&root_page);
                db.next_root_page = prev_next_root_page;
            }
            Self::DestroyTable { root_page, table } | Self::ClearTable { root_page, table } => {
                db.tables.insert(root_page, table);
            }
            Self::BumpRowid {
                root_page,
                prev_next_rowid,
            } => {
                if let Some(table) = db.tables.get_mut(&root_page) {
                    table.next_rowid = prev_next_rowid;
                }
            }
            Self::UpsertRow {
                root_page,
                rowid,
                prev_next_rowid,
                old_values,
            } => {
                if let Some(table) = db.tables.get_mut(&root_page) {
                    match old_values {
                        Some(values) => {
                            table.insert(rowid, values);
                        }
                        None => {
                            table.delete_by_rowid(rowid);
                        }
                    }
                    table.next_rowid = prev_next_rowid;
                }
            }
            Self::DeleteRow {
                root_page,
                row,
                prev_next_rowid,
            } => {
                if let Some(table) = db.tables.get_mut(&root_page) {
                    table.insert(row.rowid, row.values);
                    table.next_rowid = prev_next_rowid;
                }
            }
        }
    }
}

/// Shared in-memory database backing the VDBE engine's cursor operations.
///
/// Maps root page numbers to in-memory tables. The Connection layer
/// populates this when processing CREATE TABLE and passes it to the engine.
#[derive(Debug, Clone)]
pub struct MemDatabase {
    /// Tables indexed by root page number.
    pub tables: SwissIndex<i32, MemTable>,
    /// Next available root page number.
    next_root_page: i32,
    /// Whether undo logging is enabled for transaction/savepoint rollback.
    undo_enabled: bool,
    /// Undo log. A version token is the log length at the snapshot point.
    undo_log: Vec<MemDbUndoOp>,
}

impl MemDatabase {
    /// Create a new empty in-memory database.
    pub fn new() -> Self {
        Self {
            tables: SwissIndex::new(),
            next_root_page: 2, // Page 1 is reserved for sqlite_master.
            undo_enabled: false,
            undo_log: Vec::new(),
        }
    }

    /// Propagate a shared collation registry to all tracked MemTables.
    pub fn set_collation_registry(&mut self, registry: Arc<Mutex<CollationRegistry>>) {
        for (_, table) in self.tables.iter_mut() {
            table.set_collation_registry(Arc::clone(&registry));
        }
    }

    /// Returns the number of tables in the database.
    #[must_use]
    pub fn table_count(&self) -> i32 {
        #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
        let count = self.tables.len() as i32;
        count
    }

    /// Return the next root page that would be assigned by `create_table`.
    #[must_use]
    pub fn next_root_page(&self) -> i32 {
        self.next_root_page
    }

    /// Advance the root page counter so future allocations start at `val`.
    ///
    /// Used to prevent MemDatabase root pages from colliding with pager-
    /// allocated pages (e.g. when materializing sqlite_master temp tables).
    pub fn set_next_root_page(&mut self, val: i32) {
        self.next_root_page = val;
    }

    /// Allocate and return the next root page number without creating a table.
    ///
    /// Used by `OpenAutoindex` which needs a unique page number for a
    /// `MemPageStore`-backed index cursor but must NOT pollute `self.tables`
    /// (a spurious `MemTable` entry would cause `get_table()` to return
    /// `Some`, misleading `open_storage_cursor` into treating the page as a
    /// table B-tree).
    pub fn allocate_root_page(&mut self) -> i32 {
        let root_page = self.next_root_page;
        self.next_root_page += 1;
        root_page
    }

    /// Create a table and return its root page number.
    pub fn create_table(&mut self, num_columns: usize) -> i32 {
        let prev_next_root_page = self.next_root_page;
        let root_page = prev_next_root_page;
        self.next_root_page += 1;
        self.tables.insert(root_page, MemTable::new(num_columns));
        self.push_undo(MemDbUndoOp::CreateTable {
            root_page,
            prev_next_root_page,
        });
        root_page
    }

    /// Create a table at a specific root page number.
    ///
    /// Used by the storage layer (5A.3) when the root page is allocated
    /// from the pager rather than auto-assigned.  Advances
    /// `next_root_page` past `root_page` if necessary so that future
    /// `create_table()` calls do not collide.
    pub fn create_table_at(&mut self, root_page: i32, num_columns: usize) {
        let prev_next_root_page = self.next_root_page;
        if root_page >= self.next_root_page {
            self.next_root_page = root_page + 1;
        }
        self.tables.insert(root_page, MemTable::new(num_columns));
        self.push_undo(MemDbUndoOp::CreateTable {
            root_page,
            prev_next_root_page,
        });
    }

    /// Get a reference to a table by root page.
    pub fn get_table(&self, root_page: i32) -> Option<&MemTable> {
        self.tables.get(&root_page)
    }

    /// Get a mutable reference to a table by root page.
    pub fn get_table_mut(&mut self, root_page: i32) -> Option<&mut MemTable> {
        self.tables.get_mut(&root_page)
    }

    fn push_undo(&mut self, op: MemDbUndoOp) {
        if self.undo_enabled {
            self.undo_log.push(op);
        }
    }

    /// Return the current undo-version token.
    ///
    /// This is the identity captured in snapshots for savepoints/transactions.
    pub fn undo_version(&self) -> MemDbVersionToken {
        MemDbVersionToken(self.undo_log.len())
    }

    /// Begin a new undo region (transaction start).
    pub fn begin_undo(&mut self) {
        self.undo_enabled = true;
        self.undo_log.clear();
    }

    /// End the undo region (transaction committed/finished).
    pub fn commit_undo(&mut self) {
        self.undo_enabled = false;
        self.undo_log.clear();
    }

    /// Restore the database to a previously captured undo-version token.
    pub fn rollback_to(&mut self, token: MemDbVersionToken) {
        while self.undo_log.len() > token.0 {
            if let Some(op) = self.undo_log.pop() {
                op.undo(self);
            }
        }
    }

    /// Drop a table by root page and record undo information.
    pub fn destroy_table(&mut self, root_page: i32) {
        if let Some(table) = self.tables.remove(&root_page) {
            self.push_undo(MemDbUndoOp::DestroyTable { root_page, table });
        }
    }

    fn clear_table(&mut self, root_page: i32) {
        let prev = self.tables.get(&root_page).cloned();
        if let Some(table) = prev {
            self.push_undo(MemDbUndoOp::ClearTable { root_page, table });
        }
        if let Some(table) = self.tables.get_mut(&root_page) {
            table.clear();
        }
    }

    fn alloc_rowid(&mut self, root_page: i32) -> i64 {
        if let Some(table) = self.tables.get_mut(&root_page) {
            let prev_next_rowid = table.next_rowid;
            let rowid = table.alloc_rowid();
            self.push_undo(MemDbUndoOp::BumpRowid {
                root_page,
                prev_next_rowid,
            });
            rowid
        } else {
            1
        }
    }

    /// Allocate a rowid for concurrent mode (`OP_NewRowid` with `p3 != 0`).
    ///
    /// Unlike the serialized path (counter only), this path derives the next
    /// candidate strictly from the visible table contents (`max(rowid) + 1`).
    /// This avoids relying on potentially stale local counter state.
    fn alloc_rowid_concurrent(&mut self, root_page: i32) -> i64 {
        if let Some(table) = self.tables.get_mut(&root_page) {
            let prev_next_rowid = table.next_rowid;
            let max_visible = table.max_visible_rowid().unwrap_or(0);
            let rowid = max_visible.saturating_add(1);
            table.next_rowid = rowid.saturating_add(1);
            self.push_undo(MemDbUndoOp::BumpRowid {
                root_page,
                prev_next_rowid,
            });
            rowid
        } else {
            1
        }
    }

    pub fn upsert_row<V>(&mut self, root_page: i32, rowid: i64, values: V)
    where
        V: Into<MemRowValues>,
    {
        let values = values.into();
        if let Some(table) = self.tables.get_mut(&root_page) {
            let prev_next_rowid = table.next_rowid;
            // Use binary search (O(log n)) instead of linear scan (O(n))
            // since rows are maintained in rowid-sorted order.
            let old_values = table
                .rows
                .binary_search_by_key(&rowid, |r| r.rowid)
                .ok()
                .map(|idx| table.rows[idx].values.clone());
            table.insert(rowid, values);
            self.push_undo(MemDbUndoOp::UpsertRow {
                root_page,
                rowid,
                prev_next_rowid,
                old_values,
            });
        }
    }

    /// Allocate an implicit rowid and insert the row, recording one undo entry
    /// for both the rowid counter advance and the row mutation. If
    /// `rowid_value_column` is present, that column is patched to the allocated
    /// rowid before insertion.
    #[must_use]
    pub fn insert_auto_row<V>(
        &mut self,
        root_page: i32,
        values: V,
        rowid_value_column: Option<usize>,
    ) -> Option<i64>
    where
        V: Into<MemRowValues>,
    {
        let mut values = values.into();
        if let Some(table) = self.tables.get_mut(&root_page) {
            let prev_next_rowid = table.next_rowid;
            let rowid = table.alloc_rowid();
            if let Some(column) = rowid_value_column
                && let Some(value) = values.get_mut(column)
            {
                *value = SqliteValue::Integer(rowid);
            }
            let old_values = table
                .rows
                .binary_search_by_key(&rowid, |r| r.rowid)
                .ok()
                .map(|idx| table.rows[idx].values.clone());
            table.insert(rowid, values);
            self.push_undo(MemDbUndoOp::UpsertRow {
                root_page,
                rowid,
                prev_next_rowid,
                old_values,
            });
            Some(rowid)
        } else {
            None
        }
    }

    /// Delete a row by rowid, recording undo information for rollback.
    pub fn delete_rowid(&mut self, root_page: i32, rowid: i64) -> bool {
        if let Some(table) = self.tables.get_mut(&root_page)
            && let Ok(index) = table.rows.binary_search_by_key(&rowid, |r| r.rowid)
        {
            let prev_next_rowid = table.next_rowid;
            let row = table.rows.remove(index);
            table.remove_unique_entries(row.rowid, &row.values);
            self.push_undo(MemDbUndoOp::DeleteRow {
                root_page,
                row,
                prev_next_rowid,
            });
            true
        } else {
            false
        }
    }

    #[allow(dead_code)]
    fn delete_at(&mut self, root_page: i32, index: usize) {
        if let Some(table) = self.tables.get_mut(&root_page) {
            if index < table.rows.len() {
                let prev_next_rowid = table.next_rowid;
                let row = table.rows.remove(index);
                table.remove_unique_entries(row.rowid, &row.values);
                self.push_undo(MemDbUndoOp::DeleteRow {
                    root_page,
                    row,
                    prev_next_rowid,
                });
            }
        }
    }
}

impl Default for MemDatabase {
    fn default() -> Self {
        Self::new()
    }
}

// NOTE: MemDatabase intentionally does NOT implement Clone.
// Snapshot reads use the lightweight `MemDbVersionToken` (undo-log index)
// rather than cloning the entire table state.  See bd-g6eo.

const VDBE_TRACE_ENV: &str = "FSQLITE_VDBE_TRACE_OPCODES";
const VDBE_TRACE_LOGGING_STANDARD: &str = "bd-1fpm";

/// Slow query threshold for INFO-level logging (100ms).
const SLOW_QUERY_THRESHOLD_MS: u128 = 100;

// ── VDBE execution metrics (bd-1rw.1) ──────────────────────────────────────

/// Total number of VDBE opcodes executed across all statements.
static FSQLITE_VDBE_OPCODES_EXECUTED_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of VDBE statements executed.
static FSQLITE_VDBE_STATEMENTS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Cumulative statement duration in microseconds (for histogram approximation).
static FSQLITE_VDBE_STATEMENT_DURATION_US_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Dynamic execution counts for each opcode, indexed by raw opcode byte.
static FSQLITE_VDBE_OPCODE_EXECUTION_TOTALS: LazyLock<Box<[AtomicU64]>> = LazyLock::new(|| {
    (0..=Opcode::COUNT)
        .map(|_| AtomicU64::new(0))
        .collect::<Vec<_>>()
        .into_boxed_slice()
});
/// Total number of type-coercion attempts in Cast/Affinity opcodes.
static FSQLITE_VDBE_TYPE_COERCIONS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of coercions that changed a value's storage class.
static FSQLITE_VDBE_TYPE_COERCION_CHANGES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of storage cursor column reads.
static FSQLITE_VDBE_COLUMN_READS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of record decode calls that materialized a full row vector.
static FSQLITE_VDBE_RECORD_DECODE_CALLS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of decode-cache hits across storage, sorter, and pseudo-row paths.
static FSQLITE_VDBE_DECODE_CACHE_HITS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of decode-cache misses across storage, sorter, and pseudo-row paths.
static FSQLITE_VDBE_DECODE_CACHE_MISSES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of decode-cache invalidations caused by row-position changes.
static FSQLITE_VDBE_DECODE_CACHE_INVALIDATIONS_POSITION_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of decode-cache invalidations caused by write-path mutations.
static FSQLITE_VDBE_DECODE_CACHE_INVALIDATIONS_WRITE_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of decode-cache invalidations caused by pseudo-row image changes.
static FSQLITE_VDBE_DECODE_CACHE_INVALIDATIONS_PSEUDO_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of values materialized while decoding records/columns.
static FSQLITE_VDBE_DECODED_VALUES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Estimated heap bytes materialized while decoding records/columns.
static FSQLITE_VDBE_DECODED_VALUE_HEAP_BYTES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of result rows emitted by the interpreter.
static FSQLITE_VDBE_RESULT_ROWS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of result values materialized for emitted rows.
static FSQLITE_VDBE_RESULT_VALUES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Estimated heap bytes materialized for emitted result rows.
static FSQLITE_VDBE_RESULT_VALUE_HEAP_BYTES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Cumulative nanoseconds spent materializing emitted result rows.
static FSQLITE_VDBE_RESULT_ROW_MATERIALIZATION_TIME_NS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of MakeRecord calls.
static FSQLITE_VDBE_MAKE_RECORD_CALLS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total bytes produced by MakeRecord blobs.
static FSQLITE_VDBE_MAKE_RECORD_BLOB_BYTES_TOTAL: AtomicU64 = AtomicU64::new(0);
#[cfg(test)]
thread_local! {
    /// Test-only per-thread count of sideband MakeRecord values that had to become Arc-backed blobs.
    static FSQLITE_VDBE_MAKE_RECORD_SIDEBAND_MATERIALIZATIONS_TOTAL: Cell<u64> = const { Cell::new(0) };
}
/// bd-7vkes: Count times the BTREE_APPEND fast path was taken (skip seek).
static FSQLITE_VDBE_INSERT_APPEND_COUNT: AtomicU64 = AtomicU64::new(0);
/// bd-7vkes: Count times a full B-tree seek was needed for INSERT.
static FSQLITE_VDBE_INSERT_SEEK_COUNT: AtomicU64 = AtomicU64::new(0);
/// bd-qayid: Count times a cached append hint was cleared conservatively.
static FSQLITE_VDBE_INSERT_APPEND_HINT_CLEAR_COUNT: AtomicU64 = AtomicU64::new(0);
/// Decoded NULL values.
static FSQLITE_VDBE_DECODED_NULLS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Decoded INTEGER values.
static FSQLITE_VDBE_DECODED_INTEGERS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Decoded REAL values.
static FSQLITE_VDBE_DECODED_REALS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Decoded TEXT values.
static FSQLITE_VDBE_DECODED_TEXTS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Decoded BLOB values.
static FSQLITE_VDBE_DECODED_BLOBS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Heap bytes for decoded TEXT values.
static FSQLITE_VDBE_DECODED_TEXT_BYTES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Heap bytes for decoded BLOB values.
static FSQLITE_VDBE_DECODED_BLOB_BYTES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Result-row NULL values.
static FSQLITE_VDBE_RESULT_NULLS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Result-row INTEGER values.
static FSQLITE_VDBE_RESULT_INTEGERS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Result-row REAL values.
static FSQLITE_VDBE_RESULT_REALS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Result-row TEXT values.
static FSQLITE_VDBE_RESULT_TEXTS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Result-row BLOB values.
static FSQLITE_VDBE_RESULT_BLOBS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Heap bytes for result-row TEXT values.
static FSQLITE_VDBE_RESULT_TEXT_BYTES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Heap bytes for result-row BLOB values.
static FSQLITE_VDBE_RESULT_BLOB_BYTES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Whether VDBE execution metrics should be collected on the hot path.
///
/// These counters are used only for diagnostics/tests today, so leave them
/// disabled by default to keep shared-state bookkeeping off ordinary execute().
static FSQLITE_VDBE_METRICS_ENABLED: AtomicBool = AtomicBool::new(false);
/// Monotonic program ID counter for tracing correlation.
static VDBE_PROGRAM_ID_SEQ: AtomicU64 = AtomicU64::new(1);

// ── JIT scaffolding metrics/state (bd-1rw.3) ───────────────────────────────

/// Total number of JIT compilation attempts that succeeded.
static FSQLITE_JIT_COMPILATIONS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of JIT compilation attempts that failed and fell back.
static FSQLITE_JIT_COMPILE_FAILURES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of hot-query trigger events.
static FSQLITE_JIT_TRIGGERS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of JIT code cache hits.
static FSQLITE_JIT_CACHE_HITS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total number of JIT code cache misses.
static FSQLITE_JIT_CACHE_MISSES_TOTAL: AtomicU64 = AtomicU64::new(0);

/// Global JIT enable flag.
///
/// The current scaffold only tracks hot-query metadata and always falls back to
/// the interpreter, so leaving it on by default adds synchronization and hashing
/// overhead on every statement with no execution-speed upside. Keep it opt-in
/// until a real compiled fast path exists.
static FSQLITE_JIT_ENABLED: std::sync::atomic::AtomicBool =
    std::sync::atomic::AtomicBool::new(false);
/// Hot-query threshold (`N` executions before JIT trigger).
static FSQLITE_JIT_HOT_THRESHOLD: AtomicU64 = AtomicU64::new(8);
/// Maximum cached JIT plan stubs.
static FSQLITE_JIT_CACHE_CAPACITY: AtomicU64 = AtomicU64::new(128);

/// In-memory JIT code-cache entry (scaffold).
#[derive(Debug, Clone)]
struct JitCacheEntry {
    code_size_bytes: u64,
    compiled_program: Arc<CompiledProgram>,
}

#[derive(Debug, Default)]
struct JitRuntimeState {
    executions_by_plan: HashMap<u64, u64>,
    cache: HashMap<u64, JitCacheEntry>,
    lru: VecDeque<u64>,
    unsupported_plans: HashSet<u64>,
    unsupported_lru: VecDeque<u64>,
}

impl JitRuntimeState {
    fn touch_lru(&mut self, plan_hash: u64) {
        self.lru.retain(|candidate| *candidate != plan_hash);
        self.lru.push_back(plan_hash);
    }

    fn insert_cache(
        &mut self,
        plan_hash: u64,
        entry: JitCacheEntry,
        cache_capacity: usize,
    ) -> Option<u64> {
        if cache_capacity == 0 {
            return None;
        }
        let mut evicted = None;
        if let hashbrown::hash_map::Entry::Occupied(mut occupied) = self.cache.entry(plan_hash) {
            occupied.insert(entry);
            self.touch_lru(plan_hash);
            return None;
        }
        if self.cache.len() >= cache_capacity
            && let Some(oldest) = self.lru.pop_front()
        {
            self.cache.remove(&oldest);
            evicted = Some(oldest);
        }
        self.cache.insert(plan_hash, entry);
        self.touch_lru(plan_hash);
        evicted
    }

    fn apply_capacity(&mut self, cache_capacity: usize) {
        if cache_capacity == 0 {
            self.cache.clear();
            self.lru.clear();
            self.unsupported_plans.clear();
            self.unsupported_lru.clear();
            return;
        }
        while self.cache.len() > cache_capacity {
            if let Some(oldest) = self.lru.pop_front() {
                self.cache.remove(&oldest);
            } else {
                break;
            }
        }
        while self.unsupported_plans.len() > cache_capacity {
            if let Some(oldest) = self.unsupported_lru.pop_front() {
                self.unsupported_plans.remove(&oldest);
            } else {
                break;
            }
        }
    }

    fn mark_unsupported_plan(&mut self, plan_hash: u64, cache_capacity: usize) {
        if cache_capacity == 0 {
            return;
        }
        if !self.unsupported_plans.insert(plan_hash) {
            self.unsupported_lru
                .retain(|candidate| *candidate != plan_hash);
            self.unsupported_lru.push_back(plan_hash);
            return;
        }
        if self.unsupported_plans.len() > cache_capacity
            && let Some(oldest) = self.unsupported_lru.pop_front()
        {
            self.unsupported_plans.remove(&oldest);
        }
        self.unsupported_lru.push_back(plan_hash);
    }

    fn is_unsupported_plan(&self, plan_hash: u64) -> bool {
        self.unsupported_plans.contains(&plan_hash)
    }
}

static VDBE_JIT_RUNTIME: std::sync::OnceLock<Mutex<JitRuntimeState>> = std::sync::OnceLock::new();

fn jit_runtime() -> &'static Mutex<JitRuntimeState> {
    VDBE_JIT_RUNTIME.get_or_init(|| Mutex::new(JitRuntimeState::default()))
}

fn lock_jit_runtime() -> std::sync::MutexGuard<'static, JitRuntimeState> {
    jit_runtime()
        .lock()
        .unwrap_or_else(std::sync::PoisonError::into_inner)
}

/// Snapshot of JIT scaffold metrics/configuration.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct VdbeJitMetricsSnapshot {
    /// Whether JIT triggering is enabled.
    pub enabled: bool,
    /// Hot-query threshold (`N` executions before a JIT trigger).
    pub hot_threshold: u64,
    /// Maximum number of cached JIT plan stubs.
    pub cache_capacity: usize,
    /// Current number of cached JIT plan stubs.
    pub cache_entries: usize,
    /// Total successful compilation attempts.
    pub jit_compilations_total: u64,
    /// Total failed compilation attempts.
    pub jit_compile_failures_total: u64,
    /// Total hot-query trigger events.
    pub jit_triggers_total: u64,
    /// Total cache hits.
    pub jit_cache_hits_total: u64,
    /// Total cache misses.
    pub jit_cache_misses_total: u64,
    /// Integer cache hit ratio (percent).
    pub jit_cache_hit_ratio_percent: u64,
}

/// Read a point-in-time snapshot of JIT scaffold metrics/configuration.
#[must_use]
pub fn vdbe_jit_metrics_snapshot() -> VdbeJitMetricsSnapshot {
    let runtime = lock_jit_runtime();
    let hits = FSQLITE_JIT_CACHE_HITS_TOTAL.load(AtomicOrdering::Relaxed);
    let misses = FSQLITE_JIT_CACHE_MISSES_TOTAL.load(AtomicOrdering::Relaxed);
    let denom = hits.saturating_add(misses);
    let ratio_percent = if denom == 0 {
        0
    } else {
        hits.saturating_mul(100).saturating_add(denom / 2) / denom
    };
    VdbeJitMetricsSnapshot {
        enabled: FSQLITE_JIT_ENABLED.load(AtomicOrdering::Relaxed),
        hot_threshold: FSQLITE_JIT_HOT_THRESHOLD.load(AtomicOrdering::Relaxed),
        cache_capacity: usize::try_from(FSQLITE_JIT_CACHE_CAPACITY.load(AtomicOrdering::Relaxed))
            .unwrap_or(usize::MAX),
        cache_entries: runtime.cache.len(),
        jit_compilations_total: FSQLITE_JIT_COMPILATIONS_TOTAL.load(AtomicOrdering::Relaxed),
        jit_compile_failures_total: FSQLITE_JIT_COMPILE_FAILURES_TOTAL
            .load(AtomicOrdering::Relaxed),
        jit_triggers_total: FSQLITE_JIT_TRIGGERS_TOTAL.load(AtomicOrdering::Relaxed),
        jit_cache_hits_total: hits,
        jit_cache_misses_total: misses,
        jit_cache_hit_ratio_percent: ratio_percent,
    }
}

/// Enable/disable JIT triggering.
pub fn set_vdbe_jit_enabled(enabled: bool) {
    FSQLITE_JIT_ENABLED.store(enabled, AtomicOrdering::Relaxed);
}

/// Current JIT trigger enable flag.
#[must_use]
pub fn vdbe_jit_enabled() -> bool {
    FSQLITE_JIT_ENABLED.load(AtomicOrdering::Relaxed)
}

/// Set hot-query threshold (`N` executions before a JIT trigger).
///
/// Values below 1 are clamped to 1.
#[must_use]
pub fn set_vdbe_jit_hot_threshold(threshold: u64) -> u64 {
    let clamped = threshold.max(1);
    FSQLITE_JIT_HOT_THRESHOLD.store(clamped, AtomicOrdering::Relaxed);
    clamped
}

/// Current hot-query threshold.
#[must_use]
pub fn vdbe_jit_hot_threshold() -> u64 {
    FSQLITE_JIT_HOT_THRESHOLD.load(AtomicOrdering::Relaxed)
}

/// Set JIT code cache capacity (number of plans).
///
/// Shrinks current cache immediately if needed.
#[must_use]
pub fn set_vdbe_jit_cache_capacity(capacity: usize) -> usize {
    let value_u64 = u64::try_from(capacity).unwrap_or(u64::MAX);
    FSQLITE_JIT_CACHE_CAPACITY.store(value_u64, AtomicOrdering::Relaxed);
    let mut runtime = lock_jit_runtime();
    runtime.apply_capacity(capacity);
    capacity
}

/// Current JIT code-cache capacity.
#[must_use]
pub fn vdbe_jit_cache_capacity() -> usize {
    usize::try_from(FSQLITE_JIT_CACHE_CAPACITY.load(AtomicOrdering::Relaxed)).unwrap_or(usize::MAX)
}

/// Reset JIT scaffold metrics and in-memory state.
pub fn reset_vdbe_jit_metrics() {
    FSQLITE_JIT_COMPILATIONS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_JIT_COMPILE_FAILURES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_JIT_TRIGGERS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_JIT_CACHE_HITS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_JIT_CACHE_MISSES_TOTAL.store(0, AtomicOrdering::Relaxed);
    let mut runtime = lock_jit_runtime();
    runtime.executions_by_plan.clear();
    runtime.cache.clear();
    runtime.lru.clear();
    runtime.unsupported_plans.clear();
    runtime.unsupported_lru.clear();
}

// ── Sort metrics (bd-1rw.4) ─────────────────────────────────────────────────

/// Total rows sorted across all sorter invocations.
static FSQLITE_SORT_ROWS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total pages spilled to disk by sorters.
static FSQLITE_SORT_SPILL_PAGES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total MVCC write-path executions that reused an already-owned page lock.
static FSQLITE_VDBE_MVCC_TIER0_ALREADY_OWNED_WRITES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total MVCC write-path executions that acquired a page lock on first touch.
static FSQLITE_VDBE_MVCC_TIER1_FIRST_TOUCH_WRITES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total MVCC write-path executions that crossed the commit-surface/page-one lane.
static FSQLITE_VDBE_MVCC_TIER2_COMMIT_SURFACE_WRITES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total page-lock wait episodes observed on the MVCC path.
static FSQLITE_VDBE_MVCC_PAGE_LOCK_WAITS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Cumulative nanoseconds spent waiting for page-lock ownership changes.
static FSQLITE_VDBE_MVCC_PAGE_LOCK_WAIT_TIME_NS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total BUSY retries on MVCC write paths after waiting for a page lock.
static FSQLITE_VDBE_MVCC_WRITE_BUSY_RETRIES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total BUSY timeouts on MVCC write paths after exhausting the wait budget.
static FSQLITE_VDBE_MVCC_WRITE_BUSY_TIMEOUTS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total MVCC write rejections caused by stale snapshots.
static FSQLITE_VDBE_MVCC_STALE_SNAPSHOT_REJECTS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total conflict-only page-one tracking acquisitions.
static FSQLITE_VDBE_MVCC_PAGE_ONE_CONFLICT_TRACKS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Cumulative nanoseconds spent recording conflict-only page-one tracking.
static FSQLITE_VDBE_MVCC_PAGE_ONE_CONFLICT_TRACK_TIME_NS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total stale synthetic pending-commit-surface clears.
static FSQLITE_VDBE_MVCC_PENDING_SURFACE_CLEARS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Cumulative nanoseconds spent clearing stale synthetic pending-commit surface state.
static FSQLITE_VDBE_MVCC_PENDING_SURFACE_CLEAR_TIME_NS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total borrowed `write_page(&[u8])` normalization calls.
static FSQLITE_VDBE_PAGE_DATA_BORROWED_NORMALIZATION_CALLS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total borrowed `write_page(&[u8])` calls that still copied a full page even though input was exact-size.
static FSQLITE_VDBE_PAGE_DATA_BORROWED_EXACT_SIZE_COPIES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total owned `write_page_data(PageData)` normalization calls.
static FSQLITE_VDBE_PAGE_DATA_OWNED_NORMALIZATION_CALLS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total owned `PageData` writes that passed through without resizing/copying.
static FSQLITE_VDBE_PAGE_DATA_OWNED_PASSTHROUGH_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total owned short-page writes that zero-extended the existing buffer in place.
static FSQLITE_VDBE_PAGE_DATA_OWNED_IN_PLACE_ZERO_EXTENDS_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total owned `PageData` writes that required allocating a resized page image.
static FSQLITE_VDBE_PAGE_DATA_OWNED_RESIZED_COPIES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total payload bytes copied while normalizing page data before writes.
static FSQLITE_VDBE_PAGE_DATA_NORMALIZED_PAYLOAD_BYTES_TOTAL: AtomicU64 = AtomicU64::new(0);
/// Total zero-fill bytes synthesized while normalizing short page writes.
static FSQLITE_VDBE_PAGE_DATA_NORMALIZED_ZERO_FILL_BYTES_TOTAL: AtomicU64 = AtomicU64::new(0);

/// Point-in-time breakdown of materialized value storage classes.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ValueTypeMetricsSnapshot {
    /// Total values observed in this lane.
    pub total_values: u64,
    /// NULL values observed.
    pub nulls: u64,
    /// INTEGER values observed.
    pub integers: u64,
    /// REAL values observed.
    pub reals: u64,
    /// TEXT values observed.
    pub texts: u64,
    /// BLOB values observed.
    pub blobs: u64,
    /// Heap bytes carried by TEXT values.
    pub text_bytes_total: u64,
    /// Heap bytes carried by BLOB values.
    pub blob_bytes_total: u64,
}

/// Point-in-time dynamic opcode execution total.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OpcodeExecutionCount {
    /// Stable opcode name.
    pub opcode: String,
    /// Total dynamic executions observed.
    pub total: u64,
}

/// Snapshot of MVCC write-path counters captured inside the VDBE write helpers.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct MvccWritePathMetricsSnapshot {
    /// Total tier-0 writes that reused an already-owned page lock.
    pub tier0_already_owned_writes_total: u64,
    /// Total tier-1 writes that acquired a page lock on first touch.
    pub tier1_first_touch_writes_total: u64,
    /// Total tier-2 writes that crossed the commit-surface/page-one lane.
    pub tier2_commit_surface_writes_total: u64,
    /// Total wait episodes on page-lock handoff.
    pub page_lock_waits_total: u64,
    /// Cumulative nanoseconds spent waiting for page-lock handoff.
    pub page_lock_wait_time_ns_total: u64,
    /// Total BUSY retries after a completed page-lock wait.
    pub write_busy_retries_total: u64,
    /// Total BUSY timeouts after exhausting the page-lock wait budget.
    pub write_busy_timeouts_total: u64,
    /// Total stale-snapshot rejections on MVCC writes.
    pub stale_snapshot_rejects_total: u64,
    /// Total conflict-only page-one tracking operations.
    pub page_one_conflict_tracks_total: u64,
    /// Cumulative nanoseconds spent in conflict-only page-one tracking.
    pub page_one_conflict_track_time_ns_total: u64,
    /// Total stale synthetic pending-surface clears.
    pub pending_commit_surface_clears_total: u64,
    /// Cumulative nanoseconds spent clearing stale synthetic pending-surface state.
    pub pending_commit_surface_clear_time_ns_total: u64,
}

/// Snapshot of page-data normalization and copy/motion counters on the write path.
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct PageDataMotionMetricsSnapshot {
    /// Total borrowed `write_page(&[u8])` normalization calls.
    pub borrowed_write_normalization_calls_total: u64,
    /// Total borrowed exact-size writes that still copied a full page image.
    pub borrowed_exact_size_copies_total: u64,
    /// Total owned `write_page_data(PageData)` normalization calls.
    pub owned_write_normalization_calls_total: u64,
    /// Total owned writes that passed through without resizing/copying.
    pub owned_passthrough_total: u64,
    /// Total owned short-page writes that zero-extended the existing buffer.
    pub owned_in_place_zero_extends_total: u64,
    /// Total owned writes that required allocating and copying into a new page image.
    pub owned_resized_copies_total: u64,
    /// Total payload bytes copied into normalized page images.
    pub normalized_payload_bytes_total: u64,
    /// Total zero-fill bytes synthesized while normalizing short writes.
    pub normalized_zero_fill_bytes_total: u64,
}

/// Snapshot of VDBE execution metrics.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct VdbeMetricsSnapshot {
    /// Total opcodes executed across all statements.
    pub opcodes_executed_total: u64,
    /// Total statements executed.
    pub statements_total: u64,
    /// Cumulative statement duration in microseconds.
    pub statement_duration_us_total: u64,
    /// Total rows sorted across all sorter invocations.
    pub sort_rows_total: u64,
    /// Total pages spilled to disk by sorters.
    pub sort_spill_pages_total: u64,
    /// Dynamic opcode execution counts observed while metrics were enabled.
    pub opcode_execution_totals: Vec<OpcodeExecutionCount>,
    /// Total type-coercion attempts.
    pub type_coercions_total: u64,
    /// Total type-coercion attempts that changed storage class.
    pub type_coercion_changes_total: u64,
    /// Total storage cursor column reads.
    pub column_reads_total: u64,
    /// Total full-record decode calls.
    pub record_decode_calls_total: u64,
    /// Total decode-cache hits across storage, sorter, and pseudo-row paths.
    pub decode_cache_hits_total: u64,
    /// Total decode-cache misses across storage, sorter, and pseudo-row paths.
    pub decode_cache_misses_total: u64,
    /// Total decode-cache invalidations caused by row-position changes.
    pub decode_cache_invalidations_position_total: u64,
    /// Total decode-cache invalidations caused by write-path mutations.
    pub decode_cache_invalidations_write_total: u64,
    /// Total decode-cache invalidations caused by pseudo-row image changes.
    pub decode_cache_invalidations_pseudo_total: u64,
    /// Total values materialized from record/column decode.
    pub decoded_values_total: u64,
    /// Estimated heap bytes materialized from record/column decode.
    pub decoded_value_heap_bytes_total: u64,
    /// Total emitted result rows.
    pub result_rows_total: u64,
    /// Total values materialized in emitted result rows.
    pub result_values_total: u64,
    /// Estimated heap bytes materialized in emitted result rows.
    pub result_value_heap_bytes_total: u64,
    /// Cumulative nanoseconds spent materializing emitted result rows.
    pub result_row_materialization_time_ns_total: u64,
    /// Total MakeRecord calls.
    pub make_record_calls_total: u64,
    /// Total bytes produced by MakeRecord blobs.
    pub make_record_blob_bytes_total: u64,
    /// Total INSERT executions that reused the append-eligible no-seek decision.
    pub insert_append_count: u64,
    /// Total INSERT executions that had to perform an existence seek.
    pub insert_seek_count: u64,
    /// Total times the cached append hint had to be cleared conservatively.
    pub insert_append_hint_clear_count: u64,
    /// Storage-class breakdown of decoded values.
    pub decoded_value_types: ValueTypeMetricsSnapshot,
    /// Storage-class breakdown of emitted result values.
    pub result_value_types: ValueTypeMetricsSnapshot,
    /// MVCC write-path timing and retry counters captured inside the VDBE layer.
    pub mvcc_write_path: MvccWritePathMetricsSnapshot,
    /// Page-data normalization/copy counters captured on write entry.
    pub page_data_motion: PageDataMotionMetricsSnapshot,
}

/// Enable/disable VDBE execution metrics collection.
pub fn set_vdbe_metrics_enabled(enabled: bool) {
    FSQLITE_VDBE_METRICS_ENABLED.store(enabled, AtomicOrdering::Relaxed);
}

/// Current VDBE metrics collection flag.
#[must_use]
pub fn vdbe_metrics_enabled() -> bool {
    FSQLITE_VDBE_METRICS_ENABLED.load(AtomicOrdering::Relaxed)
}

/// Read a point-in-time snapshot of VDBE execution metrics.
#[must_use]
pub fn vdbe_metrics_snapshot() -> VdbeMetricsSnapshot {
    let mut opcode_execution_totals: Vec<OpcodeExecutionCount> =
        FSQLITE_VDBE_OPCODE_EXECUTION_TOTALS
            .iter()
            .enumerate()
            .skip(1)
            .filter_map(|(idx, counter)| {
                let total = counter.load(AtomicOrdering::Relaxed);
                if total == 0 {
                    return None;
                }
                let raw = u8::try_from(idx).ok()?;
                let opcode = Opcode::from_byte(raw)?;
                Some(OpcodeExecutionCount {
                    opcode: opcode.name().to_owned(),
                    total,
                })
            })
            .collect();
    opcode_execution_totals.sort_by(|lhs, rhs| {
        rhs.total
            .cmp(&lhs.total)
            .then_with(|| lhs.opcode.cmp(&rhs.opcode))
    });
    VdbeMetricsSnapshot {
        opcodes_executed_total: FSQLITE_VDBE_OPCODES_EXECUTED_TOTAL.load(AtomicOrdering::Relaxed),
        statements_total: FSQLITE_VDBE_STATEMENTS_TOTAL.load(AtomicOrdering::Relaxed),
        statement_duration_us_total: FSQLITE_VDBE_STATEMENT_DURATION_US_TOTAL
            .load(AtomicOrdering::Relaxed),
        sort_rows_total: FSQLITE_SORT_ROWS_TOTAL.load(AtomicOrdering::Relaxed),
        sort_spill_pages_total: FSQLITE_SORT_SPILL_PAGES_TOTAL.load(AtomicOrdering::Relaxed),
        opcode_execution_totals,
        type_coercions_total: FSQLITE_VDBE_TYPE_COERCIONS_TOTAL.load(AtomicOrdering::Relaxed),
        type_coercion_changes_total: FSQLITE_VDBE_TYPE_COERCION_CHANGES_TOTAL
            .load(AtomicOrdering::Relaxed),
        column_reads_total: FSQLITE_VDBE_COLUMN_READS_TOTAL.load(AtomicOrdering::Relaxed),
        record_decode_calls_total: FSQLITE_VDBE_RECORD_DECODE_CALLS_TOTAL
            .load(AtomicOrdering::Relaxed),
        decode_cache_hits_total: FSQLITE_VDBE_DECODE_CACHE_HITS_TOTAL.load(AtomicOrdering::Relaxed),
        decode_cache_misses_total: FSQLITE_VDBE_DECODE_CACHE_MISSES_TOTAL
            .load(AtomicOrdering::Relaxed),
        decode_cache_invalidations_position_total:
            FSQLITE_VDBE_DECODE_CACHE_INVALIDATIONS_POSITION_TOTAL.load(AtomicOrdering::Relaxed),
        decode_cache_invalidations_write_total: FSQLITE_VDBE_DECODE_CACHE_INVALIDATIONS_WRITE_TOTAL
            .load(AtomicOrdering::Relaxed),
        decode_cache_invalidations_pseudo_total:
            FSQLITE_VDBE_DECODE_CACHE_INVALIDATIONS_PSEUDO_TOTAL.load(AtomicOrdering::Relaxed),
        decoded_values_total: FSQLITE_VDBE_DECODED_VALUES_TOTAL.load(AtomicOrdering::Relaxed),
        decoded_value_heap_bytes_total: FSQLITE_VDBE_DECODED_VALUE_HEAP_BYTES_TOTAL
            .load(AtomicOrdering::Relaxed),
        result_rows_total: FSQLITE_VDBE_RESULT_ROWS_TOTAL.load(AtomicOrdering::Relaxed),
        result_values_total: FSQLITE_VDBE_RESULT_VALUES_TOTAL.load(AtomicOrdering::Relaxed),
        result_value_heap_bytes_total: FSQLITE_VDBE_RESULT_VALUE_HEAP_BYTES_TOTAL
            .load(AtomicOrdering::Relaxed),
        result_row_materialization_time_ns_total:
            FSQLITE_VDBE_RESULT_ROW_MATERIALIZATION_TIME_NS_TOTAL.load(AtomicOrdering::Relaxed),
        make_record_calls_total: FSQLITE_VDBE_MAKE_RECORD_CALLS_TOTAL.load(AtomicOrdering::Relaxed),
        make_record_blob_bytes_total: FSQLITE_VDBE_MAKE_RECORD_BLOB_BYTES_TOTAL
            .load(AtomicOrdering::Relaxed),
        insert_append_count: FSQLITE_VDBE_INSERT_APPEND_COUNT.load(AtomicOrdering::Relaxed),
        insert_seek_count: FSQLITE_VDBE_INSERT_SEEK_COUNT.load(AtomicOrdering::Relaxed),
        insert_append_hint_clear_count: FSQLITE_VDBE_INSERT_APPEND_HINT_CLEAR_COUNT
            .load(AtomicOrdering::Relaxed),
        decoded_value_types: ValueTypeMetricsSnapshot {
            total_values: FSQLITE_VDBE_DECODED_VALUES_TOTAL.load(AtomicOrdering::Relaxed),
            nulls: FSQLITE_VDBE_DECODED_NULLS_TOTAL.load(AtomicOrdering::Relaxed),
            integers: FSQLITE_VDBE_DECODED_INTEGERS_TOTAL.load(AtomicOrdering::Relaxed),
            reals: FSQLITE_VDBE_DECODED_REALS_TOTAL.load(AtomicOrdering::Relaxed),
            texts: FSQLITE_VDBE_DECODED_TEXTS_TOTAL.load(AtomicOrdering::Relaxed),
            blobs: FSQLITE_VDBE_DECODED_BLOBS_TOTAL.load(AtomicOrdering::Relaxed),
            text_bytes_total: FSQLITE_VDBE_DECODED_TEXT_BYTES_TOTAL.load(AtomicOrdering::Relaxed),
            blob_bytes_total: FSQLITE_VDBE_DECODED_BLOB_BYTES_TOTAL.load(AtomicOrdering::Relaxed),
        },
        result_value_types: ValueTypeMetricsSnapshot {
            total_values: FSQLITE_VDBE_RESULT_VALUES_TOTAL.load(AtomicOrdering::Relaxed),
            nulls: FSQLITE_VDBE_RESULT_NULLS_TOTAL.load(AtomicOrdering::Relaxed),
            integers: FSQLITE_VDBE_RESULT_INTEGERS_TOTAL.load(AtomicOrdering::Relaxed),
            reals: FSQLITE_VDBE_RESULT_REALS_TOTAL.load(AtomicOrdering::Relaxed),
            texts: FSQLITE_VDBE_RESULT_TEXTS_TOTAL.load(AtomicOrdering::Relaxed),
            blobs: FSQLITE_VDBE_RESULT_BLOBS_TOTAL.load(AtomicOrdering::Relaxed),
            text_bytes_total: FSQLITE_VDBE_RESULT_TEXT_BYTES_TOTAL.load(AtomicOrdering::Relaxed),
            blob_bytes_total: FSQLITE_VDBE_RESULT_BLOB_BYTES_TOTAL.load(AtomicOrdering::Relaxed),
        },
        mvcc_write_path: MvccWritePathMetricsSnapshot {
            tier0_already_owned_writes_total: FSQLITE_VDBE_MVCC_TIER0_ALREADY_OWNED_WRITES_TOTAL
                .load(AtomicOrdering::Relaxed),
            tier1_first_touch_writes_total: FSQLITE_VDBE_MVCC_TIER1_FIRST_TOUCH_WRITES_TOTAL
                .load(AtomicOrdering::Relaxed),
            tier2_commit_surface_writes_total: FSQLITE_VDBE_MVCC_TIER2_COMMIT_SURFACE_WRITES_TOTAL
                .load(AtomicOrdering::Relaxed),
            page_lock_waits_total: FSQLITE_VDBE_MVCC_PAGE_LOCK_WAITS_TOTAL
                .load(AtomicOrdering::Relaxed),
            page_lock_wait_time_ns_total: FSQLITE_VDBE_MVCC_PAGE_LOCK_WAIT_TIME_NS_TOTAL
                .load(AtomicOrdering::Relaxed),
            write_busy_retries_total: FSQLITE_VDBE_MVCC_WRITE_BUSY_RETRIES_TOTAL
                .load(AtomicOrdering::Relaxed),
            write_busy_timeouts_total: FSQLITE_VDBE_MVCC_WRITE_BUSY_TIMEOUTS_TOTAL
                .load(AtomicOrdering::Relaxed),
            stale_snapshot_rejects_total: FSQLITE_VDBE_MVCC_STALE_SNAPSHOT_REJECTS_TOTAL
                .load(AtomicOrdering::Relaxed),
            page_one_conflict_tracks_total: FSQLITE_VDBE_MVCC_PAGE_ONE_CONFLICT_TRACKS_TOTAL
                .load(AtomicOrdering::Relaxed),
            page_one_conflict_track_time_ns_total:
                FSQLITE_VDBE_MVCC_PAGE_ONE_CONFLICT_TRACK_TIME_NS_TOTAL
                    .load(AtomicOrdering::Relaxed),
            pending_commit_surface_clears_total: FSQLITE_VDBE_MVCC_PENDING_SURFACE_CLEARS_TOTAL
                .load(AtomicOrdering::Relaxed),
            pending_commit_surface_clear_time_ns_total:
                FSQLITE_VDBE_MVCC_PENDING_SURFACE_CLEAR_TIME_NS_TOTAL.load(AtomicOrdering::Relaxed),
        },
        page_data_motion: PageDataMotionMetricsSnapshot {
            borrowed_write_normalization_calls_total:
                FSQLITE_VDBE_PAGE_DATA_BORROWED_NORMALIZATION_CALLS_TOTAL
                    .load(AtomicOrdering::Relaxed),
            borrowed_exact_size_copies_total:
                FSQLITE_VDBE_PAGE_DATA_BORROWED_EXACT_SIZE_COPIES_TOTAL
                    .load(AtomicOrdering::Relaxed),
            owned_write_normalization_calls_total:
                FSQLITE_VDBE_PAGE_DATA_OWNED_NORMALIZATION_CALLS_TOTAL.load(AtomicOrdering::Relaxed),
            owned_passthrough_total: FSQLITE_VDBE_PAGE_DATA_OWNED_PASSTHROUGH_TOTAL
                .load(AtomicOrdering::Relaxed),
            owned_in_place_zero_extends_total:
                FSQLITE_VDBE_PAGE_DATA_OWNED_IN_PLACE_ZERO_EXTENDS_TOTAL
                    .load(AtomicOrdering::Relaxed),
            owned_resized_copies_total: FSQLITE_VDBE_PAGE_DATA_OWNED_RESIZED_COPIES_TOTAL
                .load(AtomicOrdering::Relaxed),
            normalized_payload_bytes_total: FSQLITE_VDBE_PAGE_DATA_NORMALIZED_PAYLOAD_BYTES_TOTAL
                .load(AtomicOrdering::Relaxed),
            normalized_zero_fill_bytes_total:
                FSQLITE_VDBE_PAGE_DATA_NORMALIZED_ZERO_FILL_BYTES_TOTAL
                    .load(AtomicOrdering::Relaxed),
        },
    }
}

/// Reset VDBE metrics to zero (tests/diagnostics).
pub fn reset_vdbe_metrics() {
    FSQLITE_VDBE_OPCODES_EXECUTED_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_STATEMENTS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_STATEMENT_DURATION_US_TOTAL.store(0, AtomicOrdering::Relaxed);
    for counter in FSQLITE_VDBE_OPCODE_EXECUTION_TOTALS.iter() {
        counter.store(0, AtomicOrdering::Relaxed);
    }
    FSQLITE_VDBE_TYPE_COERCIONS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_TYPE_COERCION_CHANGES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_COLUMN_READS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_RECORD_DECODE_CALLS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_DECODE_CACHE_HITS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_DECODE_CACHE_MISSES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_DECODE_CACHE_INVALIDATIONS_POSITION_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_DECODE_CACHE_INVALIDATIONS_WRITE_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_DECODE_CACHE_INVALIDATIONS_PSEUDO_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_DECODED_VALUES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_DECODED_VALUE_HEAP_BYTES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_RESULT_ROWS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_RESULT_VALUES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_RESULT_VALUE_HEAP_BYTES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_RESULT_ROW_MATERIALIZATION_TIME_NS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_MAKE_RECORD_CALLS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_MAKE_RECORD_BLOB_BYTES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_INSERT_APPEND_COUNT.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_INSERT_SEEK_COUNT.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_INSERT_APPEND_HINT_CLEAR_COUNT.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_DECODED_NULLS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_DECODED_INTEGERS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_DECODED_REALS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_DECODED_TEXTS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_DECODED_BLOBS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_DECODED_TEXT_BYTES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_DECODED_BLOB_BYTES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_RESULT_NULLS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_RESULT_INTEGERS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_RESULT_REALS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_RESULT_TEXTS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_RESULT_BLOBS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_RESULT_TEXT_BYTES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_RESULT_BLOB_BYTES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_SORT_ROWS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_SORT_SPILL_PAGES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_MVCC_TIER0_ALREADY_OWNED_WRITES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_MVCC_TIER1_FIRST_TOUCH_WRITES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_MVCC_TIER2_COMMIT_SURFACE_WRITES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_MVCC_PAGE_LOCK_WAITS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_MVCC_PAGE_LOCK_WAIT_TIME_NS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_MVCC_WRITE_BUSY_RETRIES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_MVCC_WRITE_BUSY_TIMEOUTS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_MVCC_STALE_SNAPSHOT_REJECTS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_MVCC_PAGE_ONE_CONFLICT_TRACKS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_MVCC_PAGE_ONE_CONFLICT_TRACK_TIME_NS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_MVCC_PENDING_SURFACE_CLEARS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_MVCC_PENDING_SURFACE_CLEAR_TIME_NS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_PAGE_DATA_BORROWED_NORMALIZATION_CALLS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_PAGE_DATA_BORROWED_EXACT_SIZE_COPIES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_PAGE_DATA_OWNED_NORMALIZATION_CALLS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_PAGE_DATA_OWNED_PASSTHROUGH_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_PAGE_DATA_OWNED_IN_PLACE_ZERO_EXTENDS_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_PAGE_DATA_OWNED_RESIZED_COPIES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_PAGE_DATA_NORMALIZED_PAYLOAD_BYTES_TOTAL.store(0, AtomicOrdering::Relaxed);
    FSQLITE_VDBE_PAGE_DATA_NORMALIZED_ZERO_FILL_BYTES_TOTAL.store(0, AtomicOrdering::Relaxed);
    reset_vdbe_jit_metrics();
}

#[cfg(test)]
fn reset_vdbe_test_sideband_materialization_count() {
    FSQLITE_VDBE_MAKE_RECORD_SIDEBAND_MATERIALIZATIONS_TOTAL.with(|counter| counter.set(0));
}

#[cfg(test)]
fn vdbe_test_sideband_materialization_count_snapshot() -> u64 {
    FSQLITE_VDBE_MAKE_RECORD_SIDEBAND_MATERIALIZATIONS_TOTAL.with(Cell::get)
}

fn estimated_value_heap_bytes(value: &SqliteValue) -> u64 {
    match value {
        SqliteValue::Null => 0,
        SqliteValue::Integer(_) | SqliteValue::Float(_) => {
            u64::try_from(std::mem::size_of::<SqliteValue>()).unwrap_or(u64::MAX)
        }
        SqliteValue::Text(text) => {
            u64::try_from(std::mem::size_of::<SqliteValue>().saturating_add(text.len()))
                .unwrap_or(u64::MAX)
        }
        SqliteValue::Blob(blob) => {
            u64::try_from(std::mem::size_of::<SqliteValue>().saturating_add(blob.len()))
                .unwrap_or(u64::MAX)
        }
    }
}

struct ValueTypeMetricCounters<'a> {
    total: &'a AtomicU64,
    nulls: &'a AtomicU64,
    integers: &'a AtomicU64,
    reals: &'a AtomicU64,
    texts: &'a AtomicU64,
    blobs: &'a AtomicU64,
    text_bytes: &'a AtomicU64,
    blob_bytes: &'a AtomicU64,
}

fn record_value_type_metrics(value: &SqliteValue, counters: &ValueTypeMetricCounters<'_>) {
    counters.total.fetch_add(1, AtomicOrdering::Relaxed);
    match value {
        SqliteValue::Null => {
            counters.nulls.fetch_add(1, AtomicOrdering::Relaxed);
        }
        SqliteValue::Integer(_) => {
            counters.integers.fetch_add(1, AtomicOrdering::Relaxed);
        }
        SqliteValue::Float(_) => {
            counters.reals.fetch_add(1, AtomicOrdering::Relaxed);
        }
        SqliteValue::Text(text) => {
            counters.texts.fetch_add(1, AtomicOrdering::Relaxed);
            counters.text_bytes.fetch_add(
                u64::try_from(text.len()).unwrap_or(u64::MAX),
                AtomicOrdering::Relaxed,
            );
        }
        SqliteValue::Blob(blob) => {
            counters.blobs.fetch_add(1, AtomicOrdering::Relaxed);
            counters.blob_bytes.fetch_add(
                u64::try_from(blob.len()).unwrap_or(u64::MAX),
                AtomicOrdering::Relaxed,
            );
        }
    }
}

fn record_decoded_value_metrics(value: &SqliteValue) {
    FSQLITE_VDBE_DECODED_VALUE_HEAP_BYTES_TOTAL
        .fetch_add(estimated_value_heap_bytes(value), AtomicOrdering::Relaxed);
    let counters = ValueTypeMetricCounters {
        total: &FSQLITE_VDBE_DECODED_VALUES_TOTAL,
        nulls: &FSQLITE_VDBE_DECODED_NULLS_TOTAL,
        integers: &FSQLITE_VDBE_DECODED_INTEGERS_TOTAL,
        reals: &FSQLITE_VDBE_DECODED_REALS_TOTAL,
        texts: &FSQLITE_VDBE_DECODED_TEXTS_TOTAL,
        blobs: &FSQLITE_VDBE_DECODED_BLOBS_TOTAL,
        text_bytes: &FSQLITE_VDBE_DECODED_TEXT_BYTES_TOTAL,
        blob_bytes: &FSQLITE_VDBE_DECODED_BLOB_BYTES_TOTAL,
    };
    record_value_type_metrics(value, &counters);
}

fn note_decode_cache_hit(collect_vdbe_metrics: bool) {
    if collect_vdbe_metrics {
        FSQLITE_VDBE_DECODE_CACHE_HITS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
    }
}

fn note_decode_cache_miss(collect_vdbe_metrics: bool) {
    if collect_vdbe_metrics {
        FSQLITE_VDBE_DECODE_CACHE_MISSES_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
    }
}

fn note_decode_cache_invalidation(
    collect_vdbe_metrics: bool,
    reason: DecodeCacheInvalidationReason,
) {
    if !collect_vdbe_metrics {
        return;
    }
    match reason {
        DecodeCacheInvalidationReason::PositionChange => {
            FSQLITE_VDBE_DECODE_CACHE_INVALIDATIONS_POSITION_TOTAL
                .fetch_add(1, AtomicOrdering::Relaxed);
        }
        DecodeCacheInvalidationReason::WriteMutation => {
            FSQLITE_VDBE_DECODE_CACHE_INVALIDATIONS_WRITE_TOTAL
                .fetch_add(1, AtomicOrdering::Relaxed);
        }
        DecodeCacheInvalidationReason::PseudoRowChange => {
            FSQLITE_VDBE_DECODE_CACHE_INVALIDATIONS_PSEUDO_TOTAL
                .fetch_add(1, AtomicOrdering::Relaxed);
        }
    }
}

fn record_result_row_metrics(row: &[SqliteValue]) {
    FSQLITE_VDBE_RESULT_ROWS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
    let row_heap_bytes = row.iter().fold(0_u64, |acc, value| {
        acc.saturating_add(estimated_value_heap_bytes(value))
    });
    FSQLITE_VDBE_RESULT_VALUE_HEAP_BYTES_TOTAL.fetch_add(row_heap_bytes, AtomicOrdering::Relaxed);
    let counters = ValueTypeMetricCounters {
        total: &FSQLITE_VDBE_RESULT_VALUES_TOTAL,
        nulls: &FSQLITE_VDBE_RESULT_NULLS_TOTAL,
        integers: &FSQLITE_VDBE_RESULT_INTEGERS_TOTAL,
        reals: &FSQLITE_VDBE_RESULT_REALS_TOTAL,
        texts: &FSQLITE_VDBE_RESULT_TEXTS_TOTAL,
        blobs: &FSQLITE_VDBE_RESULT_BLOBS_TOTAL,
        text_bytes: &FSQLITE_VDBE_RESULT_TEXT_BYTES_TOTAL,
        blob_bytes: &FSQLITE_VDBE_RESULT_BLOB_BYTES_TOTAL,
    };
    for value in row {
        record_value_type_metrics(value, &counters);
    }
}

fn record_type_coercion(before: &SqliteValue, after: &SqliteValue) {
    FSQLITE_VDBE_TYPE_COERCIONS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
    if before.storage_class() != after.storage_class() {
        FSQLITE_VDBE_TYPE_COERCION_CHANGES_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
    }
}

#[derive(Debug, Clone)]
enum JitDecision {
    Disabled,
    Warming {
        plan_hash: u64,
        execution_count: u64,
    },
    CacheHit {
        plan_hash: u64,
        code_size_bytes: u64,
        compiled_program: Arc<CompiledProgram>,
    },
    UnsupportedCached {
        plan_hash: u64,
    },
    Compiled {
        plan_hash: u64,
        compile_time_us: u64,
        code_size_bytes: u64,
        evicted_plan_hash: Option<u64>,
        compiled_program: Arc<CompiledProgram>,
    },
    CompileFailed {
        plan_hash: u64,
        compile_time_us: u64,
        reason: &'static str,
    },
}

fn hash_program(program: &VdbeProgram) -> u64 {
    const FNV_OFFSET: u64 = 0xcbf29ce484222325;
    const FNV_PRIME: u64 = 0x100000001b3;

    fn mix(hash: &mut u64, bytes: &[u8]) {
        for byte in bytes {
            *hash ^= u64::from(*byte);
            *hash = hash.wrapping_mul(FNV_PRIME);
        }
    }

    fn mix_len(hash: &mut u64, len: usize) {
        mix(hash, &u64::try_from(len).unwrap_or(u64::MAX).to_le_bytes());
    }

    fn mix_p4(hash: &mut u64, p4: &P4) {
        match p4 {
            P4::None => mix(hash, &[0]),
            P4::Int(value) => {
                mix(hash, &[1]);
                mix(hash, &value.to_le_bytes());
            }
            P4::Int64(value) => {
                mix(hash, &[2]);
                mix(hash, &value.to_le_bytes());
            }
            P4::Real(value) => {
                mix(hash, &[3]);
                mix(hash, &value.to_bits().to_le_bytes());
            }
            P4::Str(value) => {
                mix(hash, &[4]);
                mix_len(hash, value.len());
                mix(hash, value.as_bytes());
            }
            P4::Blob(value) => {
                mix(hash, &[5]);
                mix_len(hash, value.len());
                mix(hash, value);
            }
            P4::Collation(value) => {
                mix(hash, &[6]);
                mix_len(hash, value.len());
                mix(hash, value.as_bytes());
            }
            P4::FuncName(value) => {
                mix(hash, &[7]);
                mix_len(hash, value.len());
                mix(hash, value.as_bytes());
            }
            P4::FuncNameCollated(value, coll) => {
                mix(hash, &[13]); // distinct from FuncName tag [7]
                mix_len(hash, value.len());
                mix(hash, value.as_bytes());
                mix_len(hash, coll.len());
                mix(hash, coll.as_bytes());
            }
            P4::Table(value) => {
                mix(hash, &[8]);
                mix_len(hash, value.len());
                mix(hash, value.as_bytes());
            }
            P4::Index(value) => {
                mix(hash, &[9]);
                mix_len(hash, value.len());
                mix(hash, value.as_bytes());
            }
            P4::Affinity(value) => {
                mix(hash, &[10]);
                mix_len(hash, value.len());
                mix(hash, value.as_bytes());
            }
            P4::TimeTravelCommitSeq(value) => {
                mix(hash, &[11]);
                mix(hash, &value.to_le_bytes());
            }
            P4::TimeTravelTimestamp(value) => {
                mix(hash, &[12]);
                mix_len(hash, value.len());
                mix(hash, value.as_bytes());
            }
            P4::PrecomputedHeader(value) => {
                mix(hash, &[14]);
                mix_len(hash, value.template.len());
                mix(hash, &value.template);
                mix_len(hash, value.slots.len());
                for slot in &value.slots {
                    let kind = match slot.kind {
                        PrecomputedSerialTypeKind::NullPlaceholder => 0_u8,
                        PrecomputedSerialTypeKind::AnyOneByteVarintOrNull => 1_u8,
                        PrecomputedSerialTypeKind::IntegerOrNull => 2_u8,
                        PrecomputedSerialTypeKind::RealOrNull => 3_u8,
                    };
                    mix(hash, &[kind]);
                    mix_len(hash, slot.header_offset);
                }
            }
        }
    }

    let mut hash = FNV_OFFSET;
    mix(&mut hash, &program.register_count().to_le_bytes());
    for op in program.ops() {
        mix(&mut hash, &[op.opcode as u8]);
        mix(&mut hash, &op.p1.to_le_bytes());
        mix(&mut hash, &op.p2.to_le_bytes());
        mix(&mut hash, &op.p3.to_le_bytes());
        mix_p4(&mut hash, &op.p4);
        mix(&mut hash, &op.p5.to_le_bytes());
    }
    hash
}

fn estimate_compiled_program_size(compiled_program: &CompiledProgram) -> u64 {
    match compiled_program {
        CompiledProgram::ConstantResultRow(template) => {
            let value_count = u64::try_from(template.values.len()).unwrap_or(u64::MAX);
            value_count.saturating_mul(24).saturating_add(64)
        }
        CompiledProgram::SimpleInsert(template) => {
            let value_count = u64::try_from(template.value_sources.len()).unwrap_or(u64::MAX);
            let builder_bytes = match &template.record_builder {
                CompiledRecordBuilder::Generic => 0,
                CompiledRecordBuilder::PrecomputedHeader(header) => {
                    u64::try_from(header.template.len() + header.slots.len() * 8)
                        .unwrap_or(u64::MAX)
                }
            };
            value_count
                .saturating_mul(32)
                .saturating_add(96)
                .saturating_add(builder_bytes)
        }
        CompiledProgram::RowidLookupSelect(template) => {
            let col_count = u64::try_from(template.column_indices.len()).unwrap_or(u64::MAX);
            col_count.saturating_mul(24).saturating_add(80)
        }
        CompiledProgram::FullScanSelect(template) => {
            let col_count = u64::try_from(template.column_indices.len()).unwrap_or(u64::MAX);
            col_count.saturating_mul(24).saturating_add(64)
        }
    }
}

fn compile_jit_program(
    program: &VdbeProgram,
) -> std::result::Result<(CompiledProgram, u64), &'static str> {
    let compiled_program =
        try_compile_program(program.ops()).ok_or("unsupported opcode in JIT scaffold compiler")?;
    let code_size_bytes = estimate_compiled_program_size(&compiled_program);
    Ok((compiled_program, code_size_bytes))
}

fn maybe_trigger_jit(program: &VdbeProgram) -> JitDecision {
    if !vdbe_jit_enabled() {
        return JitDecision::Disabled;
    }
    let plan_hash = hash_program(program);
    let hot_threshold = vdbe_jit_hot_threshold();
    let cache_capacity = vdbe_jit_cache_capacity();

    let mut runtime = lock_jit_runtime();
    let execution_count = {
        let count = runtime.executions_by_plan.entry(plan_hash).or_insert(0);
        *count = count.saturating_add(1);
        *count
    };
    if execution_count < hot_threshold {
        return JitDecision::Warming {
            plan_hash,
            execution_count,
        };
    }

    FSQLITE_JIT_TRIGGERS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
    if runtime.is_unsupported_plan(plan_hash) {
        return JitDecision::UnsupportedCached { plan_hash };
    }
    if let Some((code_size_bytes, compiled_program)) = runtime
        .cache
        .get(&plan_hash)
        .map(|entry| (entry.code_size_bytes, Arc::clone(&entry.compiled_program)))
    {
        FSQLITE_JIT_CACHE_HITS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
        runtime.touch_lru(plan_hash);
        return JitDecision::CacheHit {
            plan_hash,
            code_size_bytes,
            compiled_program,
        };
    }

    FSQLITE_JIT_CACHE_MISSES_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
    let compile_started = Instant::now();
    match compile_jit_program(program) {
        Ok((compiled_program, code_size_bytes)) => {
            FSQLITE_JIT_COMPILATIONS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
            let compile_time_us =
                u64::try_from(compile_started.elapsed().as_micros()).unwrap_or(u64::MAX);
            let compiled_program = Arc::new(compiled_program);
            let evicted_plan_hash = runtime.insert_cache(
                plan_hash,
                JitCacheEntry {
                    code_size_bytes,
                    compiled_program: Arc::clone(&compiled_program),
                },
                cache_capacity,
            );
            JitDecision::Compiled {
                plan_hash,
                compile_time_us,
                code_size_bytes,
                evicted_plan_hash,
                compiled_program,
            }
        }
        Err(reason) => {
            FSQLITE_JIT_COMPILE_FAILURES_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
            let compile_time_us =
                u64::try_from(compile_started.elapsed().as_micros()).unwrap_or(u64::MAX);
            runtime.mark_unsupported_plan(plan_hash, cache_capacity.max(1));
            JitDecision::CompileFailed {
                plan_hash,
                compile_time_us,
                reason,
            }
        }
    }
}

/// Outcome of a single engine execution.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExecOutcome {
    /// Program halted normally (Halt with p1=0).
    Done,
    /// Program halted with an error code and message.
    Error { code: i32, message: String },
}

/// Inline register storage for the 1-indexed VDBE register file.
///
/// The engine keeps `SqliteValue` cells directly in a `SmallVec` so the hot
/// opcode loop can borrow, materialize, or consume values without going
/// through an extra container abstraction.
type RegisterFile = smallvec::SmallVec<[SqliteValue; 32]>;

/// Bound parameter storage for `?1`, `?2`, ... lookups during execution.
type BindingStorage = smallvec::SmallVec<[SqliteValue; 8]>;

/// Buffered result-row storage retained by the engine when row collection is on.
type ResultRowStorage = Vec<smallvec::SmallVec<[SqliteValue; 16]>>;

#[derive(Debug, Default)]
struct MakeRecordStatementLookaside {
    buf: Vec<u8>,
    sideband_reg: i32,
}

impl MakeRecordStatementLookaside {
    fn prepare_for_statement(&mut self, estimated_capacity: usize) {
        if estimated_capacity > self.buf.capacity() {
            self.buf.reserve(estimated_capacity - self.buf.capacity());
        }
        self.reset();
    }

    fn reset(&mut self) {
        self.buf.clear();
        self.sideband_reg = 0;
    }

    fn clear_sideband_for_register(&mut self, register: i32) {
        if self.sideband_reg == register {
            self.reset();
        }
    }

    #[must_use]
    fn sideband_is_armed_for(&self, register: i32) -> bool {
        self.sideband_reg == register && !self.buf.is_empty()
    }

    #[must_use]
    fn armed_register(&self) -> Option<i32> {
        (self.sideband_reg != 0 && !self.buf.is_empty()).then_some(self.sideband_reg)
    }

    fn disarm(&mut self) {
        self.sideband_reg = 0;
    }

    fn replace_buf(&mut self, buf: Vec<u8>) {
        self.buf = buf;
    }

    fn replace_cleared_buf(&mut self, mut buf: Vec<u8>) {
        buf.clear();
        self.buf = buf;
    }

    fn arm_sideband(&mut self, register: i32) {
        self.sideband_reg = register;
    }

    fn take_buf(&mut self) -> Vec<u8> {
        std::mem::take(&mut self.buf)
    }

    #[cfg(test)]
    #[must_use]
    fn capacity(&self) -> usize {
        self.buf.capacity()
    }

    #[cfg(test)]
    #[must_use]
    fn is_empty(&self) -> bool {
        self.buf.is_empty()
    }

    #[cfg(test)]
    #[must_use]
    fn as_slice(&self) -> &[u8] {
        self.buf.as_slice()
    }
}

enum CompiledRecordWritePlan<'a> {
    PrecomputedHeader {
        values: &'a [SqliteValue],
        header: &'a fsqlite_types::record::PrecomputedRecordHeader,
        exact_size: usize,
    },
    Generic(Box<fsqlite_types::record::PlannedRecordSerialization<'a>>),
}

impl CompiledRecordWritePlan<'_> {
    #[must_use]
    fn exact_size(&self) -> usize {
        match self {
            Self::PrecomputedHeader { exact_size, .. } => *exact_size,
            Self::Generic(plan) => plan.exact_size(),
        }
    }

    #[allow(clippy::result_unit_err)]
    fn write_into_slice(self, dst: &mut [u8]) -> std::result::Result<(), ()> {
        match self {
            Self::PrecomputedHeader { values, header, .. } => {
                serialize_record_iter_with_precomputed_header_into_slice(values.iter(), header, dst)
            }
            Self::Generic(plan) => (*plan).write_into_slice(dst),
        }
    }
}

fn build_compiled_record_write_plan<'a>(
    values: &'a [SqliteValue],
    record_builder: &'a CompiledRecordBuilder,
) -> CompiledRecordWritePlan<'a> {
    if let CompiledRecordBuilder::PrecomputedHeader(header) = record_builder
        && let Some(exact_size) =
            record_iter_with_precomputed_header_exact_size(values.iter(), header)
    {
        return CompiledRecordWritePlan::PrecomputedHeader {
            values,
            header,
            exact_size,
        };
    }

    CompiledRecordWritePlan::Generic(Box::new(
        fsqlite_types::record::plan_record_iter_serialization(values.iter()),
    ))
}

fn serialize_compiled_record_into_vec(
    values: &[SqliteValue],
    record_builder: &CompiledRecordBuilder,
    buf: &mut Vec<u8>,
) {
    if let CompiledRecordBuilder::PrecomputedHeader(header) = record_builder
        && serialize_record_iter_with_precomputed_header_into(values.iter(), header, buf)
    {
        return;
    }

    fsqlite_types::record::serialize_record_iter_into(values.iter(), buf);
}

/// The VDBE bytecode interpreter.
///
/// Executes a program produced by the code generator, maintaining a register
/// file and collecting result rows. In Phase 4, cursor operations use an
/// in-memory table store (`MemDatabase`) rather than the full B-tree stack.
#[allow(clippy::struct_excessive_bools)]
pub struct VdbeEngine {
    /// Register file (1-indexed; index 0 is unused/sentinel).
    registers: RegisterFile,
    /// Bound SQL parameter values (`?1`, `?2`, ...).
    bindings: BindingStorage,
    /// Root capability context for execution-owned cursor and virtual-table work.
    execution_cx: Cx,
    /// Page size for this database (bd-zjisk.2).
    page_size: PageSize,
    /// Whether opcode-level tracing is enabled.
    trace_opcodes: bool,
    /// Execute-scoped metrics flag latched once per statement.
    collect_vdbe_metrics: bool,
    /// Result rows accumulated during execution.
    results: ResultRowStorage,
    /// Open cursors (keyed by cursor number, i.e. p1 of OpenRead/OpenWrite).
    /// bd-perf (V1.6): Flat array instead of HashMap — direct O(1) index.
    cursors: CursorSlots<MemCursor>,
    /// Open sorter cursors keyed by cursor number.
    sorters: CursorSlots<SorterCursor>,
    /// Open storage-backed cursors keyed by cursor number (read and write).
    storage_cursors: CursorSlots<StorageCursor>,
    /// Cursors that deleted the current row and should treat the next `Next`
    /// as a no-advance "consume successor" step.
    pending_next_after_delete: HashSet<i32>,
    /// Whether `OpenRead`/`OpenWrite` should route through storage-backed cursors.
    storage_cursors_enabled: bool,
    /// Shared pager transaction for storage cursors (Phase 5, bd-2a3y).
    /// When set, `open_storage_cursor` routes through the real pager/WAL
    /// stack instead of building transient `MemPageStore` snapshots.
    txn_page_io: Option<SharedTxnPageIo>,
    /// When true, `open_storage_cursor` will reject the MemPageStore fallback
    /// path and return false instead of silently routing through in-memory
    /// storage. Used in parity-certification mode (bd-2ttd8.1) to verify all
    /// cursor operations flow through the real Pager+BtreeCursor stack.
    reject_mem_fallback: bool,
    /// In-memory database backing cursor operations (shared with Connection).
    db: Option<MemDatabase>,
    /// Whether the attached `MemDatabase` contains a fully hydrated row mirror
    /// for ordinary storage-backed table reads.
    memdb_rows_loaded: bool,
    /// Whether storage-cursor `Count` may trust the attached `MemDatabase`
    /// row mirror for exact cardinality. VDBE write-through DML can leave the
    /// MemDatabase rows stale until the connection reloads from pager.
    storage_cursor_memdb_count_shortcuts_safe: bool,
    /// Scalar/aggregate/window function registry for Function/PureFunc opcodes.
    func_registry: Option<Arc<FunctionRegistry>>,
    /// Resolved scalar functions keyed by opcode address.
    scalar_function_cache: HashMap<usize, Arc<dyn fsqlite_func::ScalarFunction>>,
    /// Resolved aggregate functions keyed by opcode address.
    aggregate_function_cache: HashMap<usize, Arc<ErasedAggregateFunction>>,
    /// Collation registry for compare, sort, DISTINCT, and grouping semantics.
    collation_registry: Arc<Mutex<CollationRegistry>>,
    /// Lazily allocated feature state kept off the hot interpreter footprint.
    cold_state: Option<Box<ColdVdbeState>>,
    /// Schema cookie value provided by the Connection (bd-3mmj).
    /// Used by `ReadCookie` (p3=1) and `SetCookie` opcodes, and
    /// by `Transaction` for stale-schema detection.
    schema_cookie: u32,
    /// Result of the last `Opcode::Compare` operation.
    last_compare_result: Option<Ordering>,
    /// Number of rows modified (inserted, deleted, or updated) during execution.
    changes: usize,
    /// Rowid of the last INSERT operation (for `last_insert_rowid()` support).
    last_insert_rowid: i64,
    /// Whether this execution recorded a real last-insert rowid.
    last_insert_rowid_valid: bool,
    /// Cursor ID used by the last Insert opcode (for conflict resolution in
    /// `IdxInsert`: allows the index handler to undo or replace the table row).
    last_insert_cursor_id: Option<i32>,
    /// Foreign key constraint violation counter (deferred FK enforcement).
    fk_counter: i64,
    /// AUTOINCREMENT high-water marks keyed by root page number (bd-31j76).
    /// Populated from `sqlite_sequence` by the Connection before execution.
    autoincrement_seq_by_root_page: HashMap<i32, i64>,
    /// Shared allocator used for implicit rowid assignment in concurrent mode.
    concurrent_rowid_allocator: Option<Arc<ConcurrentRowIdAllocator>>,
    /// Schema epoch namespace for concurrent rowid reservations.
    concurrent_rowid_schema_epoch: SchemaEpoch,
    /// INTEGER PRIMARY KEY alias column positions keyed by root page number.
    /// Used to decode storage-cursor payload columns for rowid tables.
    rowid_alias_col_by_root_page: Arc<HashMap<i32, usize>>,
    /// Declared table column counts keyed by root page number.
    /// Used to distinguish canonical SQLite payloads from legacy short records.
    table_column_count_by_root_page: Arc<HashMap<i32, usize>>,
    /// Earliest non-IPK NOT NULL column keyed by root page number.
    first_not_null_non_ipk_col_by_root_page: Arc<HashMap<i32, usize>>,
    /// Column default values by root page number (for ALTER TABLE ADD COLUMN).
    /// When a row has fewer columns than the schema expects, defaults from this
    /// map are applied instead of returning NULL.
    column_defaults_by_root_page: Arc<HashMap<i32, Vec<Option<SqliteValue>>>>,
    /// Per-index descending flags keyed by index root page number.
    index_desc_flags_by_root_page: Arc<HashMap<i32, Vec<bool>>>,
    /// Per-index collation sequences keyed by index root page number.
    index_collations_by_root_page: Arc<HashMap<i32, Vec<Option<String>>>>,
    /// Mapping from cursor_id to root_page for default value lookup.
    cursor_root_pages: HashMap<i32, i32>,
    /// Virtual table instances keyed by cursor number (for transaction ops).
    vtab_instances: SwissIndex<i32, Box<dyn VtabInstance>>,
    /// Cursors with time-travel snapshots (SQL:2011 temporal queries).
    /// Keyed by cursor ID; the integration layer uses this to resolve
    /// historical page versions and enforce read-only semantics.
    time_travel_cursors: HashMap<i32, TimeTravelMarker>,
    /// MVCC version store for time-travel page resolution.
    /// Set by the connection layer before execution when time-travel
    /// queries may be present.
    version_store: Option<Arc<VersionStore>>,
    /// Commit log for time-travel timestamp resolution and commit validation.
    time_travel_commit_log: Option<Arc<Mutex<CommitLog>>>,
    /// GC horizon for time-travel snapshot validation.
    time_travel_gc_horizon: Option<CommitSeq>,
    /// Metadata mapping table cursor IDs to their associated index cursors
    /// and column indices. Used by `native_replace_row` to clean up secondary
    /// index entries during REPLACE conflict resolution.
    table_index_meta: Arc<TableIndexMetaMap>,
    /// Statement-local lookaside for `MakeRecord` serialization sideband.
    make_record_lookaside: MakeRecordStatementLookaside,
    /// Whether `OP_ResultRow` should materialize and retain result rows.
    /// DML-only execution lanes can disable this to avoid row-buffer work.
    collect_result_rows: bool,
    /// Optional cap on how many `OP_ResultRow` payloads are retained.
    ///
    /// Execution still continues to completion; rows past the cap are simply
    /// discarded while preserving register-clearing semantics.
    max_collected_result_rows: Option<usize>,
    /// Whether per-statement execution state is already in a fresh baseline.
    ///
    /// `reset_for_reuse()` establishes this for cached engines. The common
    /// prepared-statement path can then skip a second round of identical clears
    /// at the top of `execute()`, while plain repeated `execute()` calls on the
    /// same engine still preserve their existing reset semantics.
    statement_state_clean: bool,
    /// Tracks which cold subsystems were actually touched by the last statement
    /// so common-case reuse can skip blanket clears of unused collections.
    statement_cold_state: StatementColdState,
    /// Root pages of tables modified by storage-cursor DML since last sync.
    /// Lazy tracking: instead of syncing MemDB on every INSERT, we mark the
    /// table dirty and bulk-sync only when a compatibility-path query needs
    /// MemDB data. This eliminates the 0.10 µs/row sync overhead for fast-path
    /// storage-cursor-backed workloads.
    dirty_root_pages: HashSet<i32>,
}

type ResultRowCallback<'a> = dyn FnMut(smallvec::SmallVec<[SqliteValue; 16]>) -> Result<()> + 'a;

#[derive(Debug, PartialEq, Eq)]
pub enum ExactResultRowOutcome {
    NoRows,
    Row(Box<smallvec::SmallVec<[SqliteValue; 16]>>),
    MultipleRows,
}

/// Time-travel target marker stored on cursors opened with
/// `FOR SYSTEM_TIME AS OF ...`.
#[derive(Debug, Clone)]
pub enum TimeTravelMarker {
    /// Pinned to a specific commit sequence number.
    CommitSeq(u64),
    /// Resolved from an ISO-8601 timestamp string.
    Timestamp(String),
}

/// Type-erased virtual table instance for transaction and lifecycle ops.
///
/// Because `VirtualTable` has an associated `Cursor` type, we need a
/// type-erased wrapper to store heterogeneous vtab instances.
#[allow(dead_code)]
trait VtabInstance: Send + Sync {
    fn begin(&mut self, cx: &Cx) -> Result<()>;
    fn commit(&mut self, cx: &Cx) -> Result<()>;
    fn rollback(&mut self, cx: &Cx) -> Result<()>;
    fn destroy(&mut self, cx: &Cx) -> Result<()>;
    fn rename(&mut self, cx: &Cx, new_name: &str) -> Result<()>;
    fn open_cursor(&self) -> Result<Box<dyn ErasedVtabCursor>>;
    fn vtab_update(&mut self, cx: &Cx, args: &[SqliteValue]) -> Result<Option<i64>>;
}

/// A type-erased virtual table cursor.
///
/// Wraps a concrete `VirtualTableCursor` implementation behind dynamic
/// dispatch so the engine can store cursors from different vtab modules
/// in the same index.
pub trait ErasedVtabCursor: Send {
    fn filter(
        &mut self,
        cx: &Cx,
        idx_num: i32,
        idx_str: Option<&str>,
        args: &[SqliteValue],
    ) -> Result<()>;
    fn next(&mut self, cx: &Cx) -> Result<()>;
    fn eof(&self) -> bool;
    fn column(&self, ctx: &mut ColumnContext, col: i32) -> Result<()>;
    fn rowid(&self) -> Result<i64>;
}

/// Blanket implementation of `ErasedVtabCursor` for any concrete cursor type.
impl<C: fsqlite_func::vtab::VirtualTableCursor + 'static> ErasedVtabCursor for C {
    fn filter(
        &mut self,
        cx: &Cx,
        idx_num: i32,
        idx_str: Option<&str>,
        args: &[SqliteValue],
    ) -> Result<()> {
        fsqlite_func::vtab::VirtualTableCursor::filter(self, cx, idx_num, idx_str, args)
    }
    fn next(&mut self, cx: &Cx) -> Result<()> {
        fsqlite_func::vtab::VirtualTableCursor::next(self, cx)
    }
    fn eof(&self) -> bool {
        fsqlite_func::vtab::VirtualTableCursor::eof(self)
    }
    fn column(&self, ctx: &mut ColumnContext, col: i32) -> Result<()> {
        fsqlite_func::vtab::VirtualTableCursor::column(self, ctx, col)
    }
    fn rowid(&self) -> Result<i64> {
        fsqlite_func::vtab::VirtualTableCursor::rowid(self)
    }
}

/// Bridge from `fsqlite_func::vtab::ErasedVtabInstance` to engine's `VtabInstance`.
struct ErasedVtabBridge(Box<dyn fsqlite_func::vtab::ErasedVtabInstance>);

impl VtabInstance for ErasedVtabBridge {
    fn begin(&mut self, cx: &Cx) -> Result<()> {
        self.0.begin(cx)
    }
    fn commit(&mut self, cx: &Cx) -> Result<()> {
        self.0.commit(cx)
    }
    fn rollback(&mut self, cx: &Cx) -> Result<()> {
        self.0.rollback(cx)
    }
    fn destroy(&mut self, cx: &Cx) -> Result<()> {
        self.0.destroy(cx)
    }
    fn rename(&mut self, cx: &Cx, new_name: &str) -> Result<()> {
        self.0.rename(cx, new_name)
    }
    fn open_cursor(&self) -> Result<Box<dyn ErasedVtabCursor>> {
        let func_cursor = self.0.open_cursor()?;
        Ok(Box::new(FuncVtabCursorBridge(func_cursor)))
    }
    fn vtab_update(&mut self, cx: &Cx, args: &[SqliteValue]) -> Result<Option<i64>> {
        self.0.update(cx, args)
    }
}

/// Bridge from `fsqlite_func::vtab::ErasedVtabCursor` to engine's `ErasedVtabCursor`.
struct FuncVtabCursorBridge(Box<dyn fsqlite_func::vtab::ErasedVtabCursor>);

impl ErasedVtabCursor for FuncVtabCursorBridge {
    fn filter(
        &mut self,
        cx: &Cx,
        idx_num: i32,
        idx_str: Option<&str>,
        args: &[SqliteValue],
    ) -> Result<()> {
        self.0.erased_filter(cx, idx_num, idx_str, args)
    }
    fn next(&mut self, cx: &Cx) -> Result<()> {
        self.0.erased_next(cx)
    }
    fn eof(&self) -> bool {
        self.0.erased_eof()
    }
    fn column(&self, ctx: &mut ColumnContext, col: i32) -> Result<()> {
        self.0.erased_column(ctx, col)
    }
    fn rowid(&self) -> Result<i64> {
        self.0.erased_rowid()
    }
}

/// State for an open virtual table cursor in the VDBE engine.
struct VtabCursorState {
    /// The type-erased cursor.
    cursor: Box<dyn ErasedVtabCursor>,
    /// Synthetic NULL-row state forced by `OP_NullRow`.
    null_row: bool,
}

/// A set of rowids for RowSetAdd/RowSetRead/RowSetTest opcodes.
///
/// Used by OR-optimized queries and IN subquery evaluation.
/// SQLite implements this as a sorted unique set of i64 rowids.
struct RowSet {
    /// Sorted, deduplicated set of rowids.
    entries: Vec<i64>,
    /// Current read position for `RowSetRead`.
    read_pos: usize,
}

impl RowSet {
    fn new() -> Self {
        Self {
            entries: Vec::new(),
            read_pos: 0,
        }
    }

    /// Add a rowid to the set (maintains sorted order, deduplicates).
    fn add(&mut self, rowid: i64) {
        match self.entries.binary_search(&rowid) {
            Ok(_) => {} // Already present
            Err(pos) => self.entries.insert(pos, rowid),
        }
    }

    /// Read the next rowid. Returns `None` when exhausted.
    fn read_next(&mut self) -> Option<i64> {
        if self.read_pos < self.entries.len() {
            let val = self.entries[self.read_pos];
            self.read_pos += 1;
            Some(val)
        } else {
            None
        }
    }

    /// Test if a rowid exists in the set.
    fn contains(&self, rowid: i64) -> bool {
        self.entries.binary_search(&rowid).is_ok()
    }
}

/// Stack-backed byte buffer for distinct keys (avoids heap allocation for small keys).
type DistinctKeyBuf = smallvec::SmallVec<[u8; 64]>;

struct AggregateContext {
    func: Arc<ErasedAggregateFunction>,
    state: Box<dyn Any + Send>,
    /// When DISTINCT is active, tracks seen argument byte-keys to skip duplicates.
    distinct_seen: Option<std::collections::HashSet<DistinctKeyBuf>>,
}

struct AggStepCall<'a> {
    accum_reg: i32,
    is_distinct: bool,
    func: &'a Arc<ErasedAggregateFunction>,
    func_name: &'a str,
    agg_collation: Option<&'a str>,
    execution_cx: &'a Cx,
    args: &'a [SqliteValue],
}

/// Original-row state captured for UPDATE's delete+insert rewrite so the old
/// row can be restored if the replacement later hits a conflict.
#[derive(Debug, Clone)]
enum PendingUpdateRestore {
    Storage {
        cursor_id: i32,
        rowid: i64,
        payload: Vec<u8>,
    },
    Mem {
        root_page: i32,
        rowid: i64,
        values: MemRowValues,
    },
}

#[derive(Debug, Clone)]
struct PendingInsertRollback {
    cursor_id: i32,
    rowid: i64,
    previous_last_insert_rowid: i64,
    previous_last_insert_rowid_valid: bool,
    update_restore: Option<PendingUpdateRestore>,
}

/// Per-accumulator window function context (for `AggInverse` / `AggValue`).
///
/// Bead bd-bldc5.4.1 (T20): Window functions are not yet emitted by VDBE
/// codegen. `AggInverse`/`AggValue` opcodes exist and dispatch correctly
/// but are never generated — window functions route through the connection
/// fallback path (which works correctly, per 100+ conformance tests).
///
/// To enable VDBE window codegen:
/// 1. Emit `SorterOpen` for PARTITION BY + ORDER BY sort in codegen.rs
/// 2. Emit `AggStep`/`AggValue` per window function per row
/// 3. For running aggregates (frame specs), emit `AggInverse` for shrinkage
/// 4. Handle multiple window specs per query (Fix #85 established the
///    runtime path for per-function sorted indices)
///
/// The split between `self.aggregates` and `self.window_contexts` must be
/// reconciled — a function used as both aggregate and window needs shared
/// state, not duplicated across the two maps.
struct WindowContext {
    func: Arc<ErasedWindowFunction>,
    state: Box<dyn Any + Send>,
}

struct ColdVdbeState {
    /// Aggregate accumulators keyed by accumulator register.
    aggregates: SwissIndex<i32, AggregateContext>,
    /// Deleted-row state for UPDATE's delete+insert rewrite. When the
    /// replacement row later conflicts, we must restore the original row.
    pending_update_restore: Option<PendingUpdateRestore>,
    /// Provisional table insert metadata kept until later `IdxInsert`
    /// opcodes either succeed or roll the row back after a secondary-index
    /// conflict.
    pending_insert_rollback: Option<PendingInsertRollback>,
    /// When true, a UNIQUE conflict with IGNORE was detected during an
    /// `IdxInsert`, so remaining `IdxInsert` opcodes for this row should be
    /// skipped.
    conflict_skip_idx: bool,
    /// Index entries inserted for the current row (cursor_id, key_bytes).
    /// On secondary-index conflict rollback, these entries must be deleted to
    /// avoid phantom index entries blocking future inserts.
    pending_idx_entries: Vec<(i32, Vec<u8>)>,
    /// RowSet data structures for OR-optimized queries (keyed by register).
    rowsets: SwissIndex<i32, RowSet>,
    /// Per-cursor monotonic sequence counters for `Opcode::Sequence`.
    sequence_counters: HashMap<i32, i64>,
    /// Open virtual table cursors keyed by cursor number.
    vtab_cursors: SwissIndex<i32, VtabCursorState>,
    /// Window function accumulators keyed by accumulator register.
    window_contexts: SwissIndex<i32, WindowContext>,
    /// Register subtype tags (register index → subtype value).
    /// Used by JSON functions to distinguish JSON text (subtype 74/'J')
    /// from regular text. Cleared on each register write.
    register_subtypes: HashMap<i32, u32>,
    /// Fast check to avoid a hash probe on every register write.
    has_subtypes: bool,
    /// Bloom filters keyed by cursor/filter register.
    /// Each entry is a fixed-size bit array used for early rejection
    /// during index lookups.
    bloom_filters: HashMap<i32, Vec<u64>>,
}

impl ColdVdbeState {
    fn new() -> Self {
        Self {
            aggregates: SwissIndex::new(),
            pending_update_restore: None,
            pending_insert_rollback: None,
            conflict_skip_idx: false,
            pending_idx_entries: Vec::new(),
            rowsets: SwissIndex::new(),
            sequence_counters: HashMap::new(),
            vtab_cursors: SwissIndex::new(),
            window_contexts: SwissIndex::new(),
            register_subtypes: HashMap::new(),
            has_subtypes: false,
            bloom_filters: HashMap::new(),
        }
    }
}

/// Encode aggregate arguments into a canonical byte key for DISTINCT deduplication,
/// applying collation when specified.
///
/// Built-in text collations normalize into the same key space that the collation
/// compares over:
/// - `NOCASE` lowercases ASCII text so `'Alice'` and `'alice'` deduplicate
/// - `RTRIM` strips trailing ASCII spaces so `'abc'` and `'abc  '` deduplicate
fn distinct_key_collated(args: &[SqliteValue], collation: Option<&str>) -> DistinctKeyBuf {
    let is_nocase = collation.is_some_and(|c| c.eq_ignore_ascii_case("NOCASE"));
    let is_rtrim = collation.is_some_and(|c| c.eq_ignore_ascii_case("RTRIM"));
    let mut key = DistinctKeyBuf::new();
    for val in args {
        match val {
            SqliteValue::Null => key.push(0),
            SqliteValue::Integer(i) => {
                key.push(1);
                key.extend_from_slice(&i.to_le_bytes());
            }
            SqliteValue::Float(f) => {
                if (-9_223_372_036_854_775_808.0..9_223_372_036_854_775_808.0).contains(f) {
                    #[allow(clippy::cast_possible_truncation)]
                    let i = *f as i64;
                    #[allow(clippy::cast_precision_loss)]
                    if (i as f64) == *f {
                        key.push(1);
                        key.extend_from_slice(&i.to_le_bytes());
                        continue;
                    }
                }
                key.push(2);
                key.extend_from_slice(&f.to_bits().to_le_bytes());
            }
            SqliteValue::Text(s) => {
                key.push(3);
                if is_nocase {
                    let folded = s.to_ascii_lowercase();
                    #[allow(clippy::cast_possible_truncation)]
                    key.extend_from_slice(&(folded.len() as u64).to_le_bytes());
                    key.extend_from_slice(folded.as_bytes());
                } else if is_rtrim {
                    let trimmed = trim_rtrim_collation_text(s.as_bytes());
                    #[allow(clippy::cast_possible_truncation)]
                    key.extend_from_slice(&(trimmed.len() as u64).to_le_bytes());
                    key.extend_from_slice(trimmed);
                } else {
                    #[allow(clippy::cast_possible_truncation)]
                    key.extend_from_slice(&(s.len() as u64).to_le_bytes());
                    key.extend_from_slice(s.as_bytes());
                }
            }
            SqliteValue::Blob(b) => {
                key.push(4);
                #[allow(clippy::cast_possible_truncation)]
                key.extend_from_slice(&(b.len() as u64).to_le_bytes());
                key.extend_from_slice(b);
            }
        }
    }
    key
}

fn trim_rtrim_collation_text(text: &[u8]) -> &[u8] {
    let mut end = text.len();
    while end > 0 && text[end - 1] == b' ' {
        end -= 1;
    }
    &text[..end]
}

fn sqlite_substr_prefix_value(value: &SqliteValue, prefix_len: usize) -> SqliteValue {
    match value {
        SqliteValue::Null => SqliteValue::Null,
        SqliteValue::Blob(bytes) => {
            let end = prefix_len.min(bytes.len());
            SqliteValue::Blob(Arc::from(&bytes[..end]))
        }
        SqliteValue::Text(text) => {
            sqlite_substr_prefix_text(Cow::Borrowed(text.as_str()), prefix_len)
        }
        SqliteValue::Integer(_) | SqliteValue::Float(_) => {
            sqlite_substr_prefix_text(Cow::Owned(value.to_text()), prefix_len)
        }
    }
}

fn sqlite_substr_prefix_text(text: Cow<'_, str>, prefix_len: usize) -> SqliteValue {
    let text = text.as_ref();
    let end = if text.is_ascii() {
        prefix_len.min(text.len())
    } else if prefix_len == 0 {
        0
    } else {
        text.char_indices()
            .nth(prefix_len)
            .map_or(text.len(), |(idx, _)| idx)
    };
    SqliteValue::Text(SmallText::new(&text[..end]))
}

impl VdbeEngine {
    /// Create a new engine with enough registers for the given program.
    ///
    /// Test-only detached convenience constructor. Production execution paths
    /// should prefer [`Self::new_with_execution_cx`] so capability lineage is
    /// preserved end-to-end.
    #[cfg(test)]
    #[must_use]
    pub fn new(register_count: i32) -> Self {
        let detached_execution_cx = Cx::new();
        Self::new_with_execution_cx(register_count, &detached_execution_cx, PageSize::DEFAULT)
    }

    /// Create a new engine rooted in the caller's execution context.
    #[must_use]
    #[allow(clippy::cast_sign_loss)]
    pub fn new_with_execution_cx(
        register_count: i32,
        execution_cx: &Cx,
        page_size: PageSize,
    ) -> Self {
        // +1 because registers are 1-indexed (register 0 unused).
        let count = register_count.max(0) as u32 + 1;
        Self {
            registers: smallvec::smallvec![SqliteValue::Null; count as usize],
            bindings: smallvec::SmallVec::new(),
            // The caller already supplies a per-execution context; cloning it
            // keeps cancellation/tracing lineage while avoiding another child
            // allocation on every statement execution.
            execution_cx: execution_cx.clone(),
            page_size,
            trace_opcodes: opcode_trace_enabled(),
            collect_vdbe_metrics: false,
            results: Vec::with_capacity(64),
            cursors: CursorSlots::new(),
            sorters: CursorSlots::new(),
            storage_cursors: CursorSlots::new(),
            pending_next_after_delete: HashSet::new(),
            storage_cursors_enabled: true,
            txn_page_io: None,
            // bd-zjisk.1: Default to parity-cert mode — reject MemPageStore fallback.
            reject_mem_fallback: true,
            db: None,
            memdb_rows_loaded: false,
            storage_cursor_memdb_count_shortcuts_safe: false,
            func_registry: None,
            scalar_function_cache: HashMap::new(),
            aggregate_function_cache: HashMap::new(),
            collation_registry: Arc::new(Mutex::new(CollationRegistry::new())),
            cold_state: None,
            schema_cookie: 0,
            last_compare_result: None,
            changes: 0,
            last_insert_rowid: 0,
            last_insert_rowid_valid: false,
            last_insert_cursor_id: None,
            fk_counter: 0,
            autoincrement_seq_by_root_page: HashMap::new(),
            concurrent_rowid_allocator: None,
            concurrent_rowid_schema_epoch: SchemaEpoch::ZERO,
            rowid_alias_col_by_root_page: Arc::new(HashMap::new()),
            table_column_count_by_root_page: Arc::new(HashMap::new()),
            first_not_null_non_ipk_col_by_root_page: Arc::new(HashMap::new()),
            column_defaults_by_root_page: Arc::new(HashMap::new()),
            index_desc_flags_by_root_page: Arc::new(HashMap::new()),
            index_collations_by_root_page: Arc::new(HashMap::new()),
            cursor_root_pages: HashMap::new(),
            vtab_instances: SwissIndex::new(),
            time_travel_cursors: HashMap::new(),
            version_store: None,
            time_travel_commit_log: None,
            time_travel_gc_horizon: None,
            table_index_meta: Arc::new(HashMap::new()),
            make_record_lookaside: MakeRecordStatementLookaside::default(),
            collect_result_rows: true,
            max_collected_result_rows: None,
            statement_state_clean: true,
            statement_cold_state: StatementColdState::empty(),
            dirty_root_pages: HashSet::new(),
        }
    }

    #[inline]
    fn mark_statement_cold_state(&mut self, state: StatementColdState) {
        self.statement_cold_state.insert(state);
    }

    #[inline]
    fn cold_state(&self) -> Option<&ColdVdbeState> {
        self.cold_state.as_deref()
    }

    #[inline]
    fn cold_state_mut(&mut self) -> Option<&mut ColdVdbeState> {
        self.cold_state.as_deref_mut()
    }

    #[inline]
    fn ensure_cold_state(&mut self) -> &mut ColdVdbeState {
        self.cold_state
            .get_or_insert_with(|| Box::new(ColdVdbeState::new()))
            .as_mut()
    }

    #[inline]
    fn ensure_cold_state_for(&mut self, state: StatementColdState) -> &mut ColdVdbeState {
        self.mark_statement_cold_state(state);
        self.ensure_cold_state()
    }

    #[cfg(test)]
    #[inline]
    fn has_cold_state_allocated(&self) -> bool {
        self.cold_state.is_some()
    }

    #[inline]
    fn clear_register_subtype(&mut self, r: i32) {
        if let Some(cold_state) = self.cold_state_mut()
            && cold_state.has_subtypes
        {
            cold_state.register_subtypes.remove(&r);
            if cold_state.register_subtypes.is_empty() {
                cold_state.has_subtypes = false;
            }
        }
    }

    #[inline]
    fn register_subtype(&self, r: i32) -> Option<u32> {
        self.cold_state()
            .and_then(|cold_state| cold_state.register_subtypes.get(&r).copied())
    }

    #[inline]
    fn set_register_subtype(&mut self, r: i32, subtype: u32) {
        if subtype == 0 {
            self.clear_register_subtype(r);
            return;
        }
        let cold_state = self.ensure_cold_state_for(StatementColdState::REGISTER_SUBTYPES);
        cold_state.register_subtypes.insert(r, subtype);
        cold_state.has_subtypes = true;
    }

    #[inline]
    fn take_pending_update_restore(&mut self) -> Option<PendingUpdateRestore> {
        self.cold_state_mut()
            .and_then(|cold_state| cold_state.pending_update_restore.take())
    }

    #[inline]
    fn set_pending_update_restore(&mut self, restore: Option<PendingUpdateRestore>) {
        if let Some(restore) = restore {
            self.ensure_cold_state_for(StatementColdState::CONFLICT_TRACKING)
                .pending_update_restore = Some(restore);
        } else if let Some(cold_state) = self.cold_state_mut() {
            cold_state.pending_update_restore = None;
        }
    }

    #[inline]
    fn take_pending_insert_rollback(&mut self) -> Option<PendingInsertRollback> {
        self.cold_state_mut()
            .and_then(|cold_state| cold_state.pending_insert_rollback.take())
    }

    #[inline]
    fn set_pending_insert_rollback(&mut self, rollback: Option<PendingInsertRollback>) {
        if let Some(rollback) = rollback {
            self.ensure_cold_state_for(StatementColdState::CONFLICT_TRACKING)
                .pending_insert_rollback = Some(rollback);
        } else if let Some(cold_state) = self.cold_state_mut() {
            cold_state.pending_insert_rollback = None;
        }
    }

    #[inline]
    fn take_pending_idx_entries(&mut self) -> Vec<(i32, Vec<u8>)> {
        self.cold_state_mut().map_or_else(Vec::new, |cold_state| {
            std::mem::take(&mut cold_state.pending_idx_entries)
        })
    }

    #[inline]
    fn clear_pending_idx_entries(&mut self) {
        if let Some(cold_state) = self.cold_state_mut() {
            cold_state.pending_idx_entries.clear();
        }
    }

    #[inline]
    fn push_pending_idx_entry(&mut self, cursor_id: i32, key_bytes: Vec<u8>) {
        self.ensure_cold_state_for(StatementColdState::CONFLICT_TRACKING)
            .pending_idx_entries
            .push((cursor_id, key_bytes));
    }

    #[inline]
    fn conflict_skip_idx(&self) -> bool {
        self.cold_state()
            .is_some_and(|cold_state| cold_state.conflict_skip_idx)
    }

    #[inline]
    fn set_conflict_skip_idx(&mut self, skip: bool) {
        if skip {
            self.ensure_cold_state_for(StatementColdState::CONFLICT_TRACKING)
                .conflict_skip_idx = true;
        } else if let Some(cold_state) = self.cold_state_mut() {
            cold_state.conflict_skip_idx = false;
        }
    }

    fn clear_statement_cold_state(&mut self) {
        if self.statement_cold_state.is_empty() && self.cold_state.is_none() {
            return;
        }
        self.cold_state = None;
        self.statement_cold_state.clear();
    }

    fn reset_for_reuse_impl(
        &mut self,
        register_count: i32,
        execution_cx: &Cx,
        page_size: PageSize,
        retain_cursors: bool,
        preserve_runtime_setup: bool,
    ) {
        let count = register_count.max(0) as u32 + 1;
        // Clear + resize registers to reuse the SmallVec's inline/heap buffer.
        self.registers.clear();
        #[allow(clippy::cast_possible_truncation)]
        self.registers.resize(count as usize, SqliteValue::Null);
        self.bindings.clear();
        self.execution_cx = execution_cx.clone();
        self.page_size = page_size;
        self.trace_opcodes = opcode_trace_enabled();
        self.collect_vdbe_metrics = false;
        self.results.clear();
        if retain_cursors {
            // Keep cursors + cursor_root_pages + storage_cursors alive.
            // OP_OpenWrite will detect an existing cursor on the same root
            // page and reuse it instead of creating a new one.
            // Clear only transient per-statement cursor state.
            self.sorters.clear();
            self.pending_next_after_delete.clear();
        } else {
            self.cursors.clear();
            self.sorters.clear();
            self.storage_cursors.clear();
            self.pending_next_after_delete.clear();
            self.storage_cursors_enabled = true;
            self.txn_page_io = None;
        }
        if !retain_cursors {
            self.db = None;
            self.cursor_root_pages.clear();
            self.vtab_instances.clear();
            self.time_travel_cursors.clear();
            self.time_travel_commit_log = None;
            self.time_travel_gc_horizon = None;
        }
        if !preserve_runtime_setup {
            self.func_registry = None;
        }
        // Function caches are keyed by program counter (PC), so they are
        // only valid for a single program. Always clear them on reset
        // because the next execution may load a different program with
        // different functions at the same PCs. (The old code skipped this
        // when preserving runtime setup, but that caused SUM/AVG/TOTAL
        // cross-contamination when the cached engine ran different programs.)
        self.scalar_function_cache.clear();
        self.aggregate_function_cache.clear();
        // Keep the existing collation_registry Arc — don't allocate a new one.
        self.clear_statement_cold_state();
        if !preserve_runtime_setup {
            self.schema_cookie = 0;
        }
        self.last_compare_result = None;
        self.changes = 0;
        self.last_insert_rowid = 0;
        self.last_insert_rowid_valid = false;
        self.last_insert_cursor_id = None;
        self.fk_counter = 0;
        if !preserve_runtime_setup {
            self.autoincrement_seq_by_root_page.clear();
        }
        if !retain_cursors {
            self.concurrent_rowid_allocator = None;
            self.concurrent_rowid_schema_epoch = SchemaEpoch::ZERO;
        }
        // table_index_meta: kept as-is — execute() overwrites it from the
        // program at the start of each run (line ~4903).
        self.make_record_lookaside.reset();
        if !preserve_runtime_setup {
            self.reject_mem_fallback = true;
            self.memdb_rows_loaded = false;
            self.storage_cursor_memdb_count_shortcuts_safe = false;
            self.version_store = None;
            self.collect_result_rows = true;
            self.max_collected_result_rows = None;
        }
        self.statement_state_clean = true;
    }

    /// Reset the engine for reuse, clearing per-statement state but keeping
    /// allocated backing memory so subsequent executions avoid 21+ collection
    /// re-allocations.
    ///
    /// After `reset()` the engine is equivalent to a freshly constructed one
    /// with the same `register_count` — but all `Vec`/`HashMap`/`SmallVec`
    /// retain their heap capacity.
    /// Reset engine state for reuse from the cached-engine pool.
    ///
    /// When `retain_cursors` is true, storage cursors and their root-page
    /// mapping are kept alive so that repeated DML on the same table can
    /// skip `OP_OpenWrite` cursor creation and `OP_Last` seek on subsequent
    /// rows.  The caller is responsible for ensuring the transaction handle
    /// (`txn_page_io`) stays valid across calls when cursors are retained.
    pub fn reset_for_reuse_ex(
        &mut self,
        register_count: i32,
        execution_cx: &Cx,
        page_size: PageSize,
        retain_cursors: bool,
    ) {
        self.reset_for_reuse_impl(
            register_count,
            execution_cx,
            page_size,
            retain_cursors,
            false,
        );
    }

    /// Reset the engine while retaining runtime bindings that a caller may
    /// immediately revalidate and reuse on the next execution.
    ///
    /// This is used by prepared-statement reusable lanes so hot loops can skip
    /// rebinding identical function/schema/default metadata on every row.
    pub fn reset_for_reuse_preserving_runtime_setup(
        &mut self,
        register_count: i32,
        execution_cx: &Cx,
        page_size: PageSize,
        retain_cursors: bool,
    ) {
        self.reset_for_reuse_impl(
            register_count,
            execution_cx,
            page_size,
            retain_cursors,
            true,
        );
    }

    /// Convenience wrapper: reset without retaining cursors (legacy behavior).
    pub fn reset_for_reuse(&mut self, register_count: i32, execution_cx: &Cx, page_size: PageSize) {
        self.reset_for_reuse_ex(register_count, execution_cx, page_size, false);
    }

    pub fn apply_reusable_table_execution_state(
        &mut self,
        state: ReusableTableExecutionState,
    ) -> ReusableTableExecutionStateOutcome {
        let ReusableTableExecutionState {
            func_registry,
            collation_registry,
            schema_cookie,
            autoincrement_seq_by_root_page,
            rowid_alias_col_by_root_page,
            table_column_count_by_root_page,
            first_not_null_non_ipk_col_by_root_page,
            column_defaults_by_root_page,
            index_desc_flags_by_root_page,
            index_collations_by_root_page,
            reject_mem_fallback,
            memdb_rows_loaded,
            storage_cursor_memdb_count_shortcuts_safe,
            version_store,
            collect_result_rows,
            max_collected_result_rows,
        } = state;

        let mut outcome = ReusableTableExecutionStateOutcome::default();
        let mut note_rebind = |changed: bool| {
            if changed {
                outcome.metadata_rebind_count += 1;
            }
        };

        let func_registry_changed = self
            .func_registry
            .as_ref()
            .is_none_or(|current| !Arc::ptr_eq(current, &func_registry));
        if func_registry_changed {
            self.func_registry = Some(func_registry);
            // W1: Clear function caches when func_registry changes, since cached
            // function lookups are keyed by program counter and would reference
            // stale entries from the old registry.
            self.scalar_function_cache.clear();
            self.aggregate_function_cache.clear();
            outcome.function_cache_cleared = true;
        }
        note_rebind(func_registry_changed);

        let collation_registry_changed =
            !Arc::ptr_eq(&self.collation_registry, &collation_registry);
        if collation_registry_changed {
            self.set_collation_registry(collation_registry);
        }
        note_rebind(collation_registry_changed);

        let schema_cookie_changed = self.schema_cookie != schema_cookie;
        if schema_cookie_changed {
            self.schema_cookie = schema_cookie;
        }
        note_rebind(schema_cookie_changed);

        let autoincrement_seq_changed =
            self.autoincrement_seq_by_root_page != autoincrement_seq_by_root_page;
        if autoincrement_seq_changed {
            self.autoincrement_seq_by_root_page = autoincrement_seq_by_root_page;
        }
        note_rebind(autoincrement_seq_changed);

        let rowid_alias_changed = !Arc::ptr_eq(
            &self.rowid_alias_col_by_root_page,
            &rowid_alias_col_by_root_page,
        );
        if rowid_alias_changed {
            self.rowid_alias_col_by_root_page = rowid_alias_col_by_root_page;
        }
        note_rebind(rowid_alias_changed);

        let table_column_count_changed = !Arc::ptr_eq(
            &self.table_column_count_by_root_page,
            &table_column_count_by_root_page,
        );
        if table_column_count_changed {
            self.table_column_count_by_root_page = table_column_count_by_root_page;
        }
        note_rebind(table_column_count_changed);

        let first_not_null_changed = !Arc::ptr_eq(
            &self.first_not_null_non_ipk_col_by_root_page,
            &first_not_null_non_ipk_col_by_root_page,
        );
        if first_not_null_changed {
            self.first_not_null_non_ipk_col_by_root_page = first_not_null_non_ipk_col_by_root_page;
        }
        note_rebind(first_not_null_changed);

        let column_defaults_changed = !Arc::ptr_eq(
            &self.column_defaults_by_root_page,
            &column_defaults_by_root_page,
        );
        if column_defaults_changed {
            self.column_defaults_by_root_page = column_defaults_by_root_page;
        }
        note_rebind(column_defaults_changed);

        let index_desc_flags_changed = !Arc::ptr_eq(
            &self.index_desc_flags_by_root_page,
            &index_desc_flags_by_root_page,
        );
        if index_desc_flags_changed {
            self.index_desc_flags_by_root_page = index_desc_flags_by_root_page;
        }
        note_rebind(index_desc_flags_changed);

        let index_collations_changed = !Arc::ptr_eq(
            &self.index_collations_by_root_page,
            &index_collations_by_root_page,
        );
        if index_collations_changed {
            self.index_collations_by_root_page = index_collations_by_root_page;
        }
        note_rebind(index_collations_changed);

        let reject_mem_fallback_changed = self.reject_mem_fallback != reject_mem_fallback;
        if reject_mem_fallback_changed {
            self.reject_mem_fallback = reject_mem_fallback;
        }
        note_rebind(reject_mem_fallback_changed);

        let memdb_rows_loaded_changed = self.memdb_rows_loaded != memdb_rows_loaded;
        if memdb_rows_loaded_changed {
            self.memdb_rows_loaded = memdb_rows_loaded;
        }
        note_rebind(memdb_rows_loaded_changed);

        let storage_shortcuts_changed = self.storage_cursor_memdb_count_shortcuts_safe
            != storage_cursor_memdb_count_shortcuts_safe;
        if storage_shortcuts_changed {
            self.storage_cursor_memdb_count_shortcuts_safe =
                storage_cursor_memdb_count_shortcuts_safe;
        }
        note_rebind(storage_shortcuts_changed);

        let version_store_changed = match (&self.version_store, &version_store) {
            (Some(current), Some(next)) => !Arc::ptr_eq(current, next),
            (None, None) => false,
            _ => true,
        };
        if version_store_changed {
            self.version_store = version_store;
        }
        note_rebind(version_store_changed);

        let collect_result_rows_changed = self.collect_result_rows != collect_result_rows;
        if collect_result_rows_changed {
            self.collect_result_rows = collect_result_rows;
        }
        note_rebind(collect_result_rows_changed);

        let max_collected_result_rows_changed =
            self.max_collected_result_rows != max_collected_result_rows;
        if max_collected_result_rows_changed {
            self.max_collected_result_rows = max_collected_result_rows;
        }
        note_rebind(max_collected_result_rows_changed);

        outcome
    }

    /// Returns the number of rows modified (inserted, deleted, or updated).
    pub fn changes(&self) -> usize {
        self.changes
    }

    /// Returns the rowid of the last INSERT operation, if this execution
    /// performed a real INSERT that updated `last_insert_rowid()`.
    pub fn last_insert_rowid(&self) -> Option<i64> {
        self.last_insert_rowid_valid
            .then_some(self.last_insert_rowid)
    }

    /// Enable or disable `OP_ResultRow` materialization.
    pub fn set_collect_result_rows(&mut self, collect_result_rows: bool) {
        self.collect_result_rows = collect_result_rows;
    }

    /// Cap how many `OP_ResultRow` payloads are retained in `self.results`.
    pub fn set_max_collected_result_rows(&mut self, max_collected_result_rows: Option<usize>) {
        self.max_collected_result_rows = max_collected_result_rows;
    }

    /// Returns the time-travel marker for a cursor, if any.
    pub fn time_travel_marker(&self, cursor_id: i32) -> Option<&TimeTravelMarker> {
        self.time_travel_cursors.get(&cursor_id)
    }

    /// Returns all time-travel cursor mappings.
    pub fn time_travel_cursors(&self) -> &HashMap<i32, TimeTravelMarker> {
        &self.time_travel_cursors
    }

    /// Set the MVCC version store for time-travel page resolution.
    ///
    /// Must be called by the connection layer before executing programs that
    /// contain `SetSnapshot` opcodes so the engine can create
    /// `TimeTravelPageIo` cursors.
    pub fn set_version_store(&mut self, vs: Arc<VersionStore>) {
        self.version_store = Some(vs);
    }

    /// Set the commit log used for time-travel resolution and validation.
    pub fn set_time_travel_commit_log(&mut self, log: Arc<Mutex<CommitLog>>) {
        self.time_travel_commit_log = Some(log);
    }

    /// Set the GC horizon for time-travel snapshot validation.
    pub fn set_time_travel_gc_horizon(&mut self, horizon: CommitSeq) {
        self.time_travel_gc_horizon = Some(horizon);
    }

    /// Attach the root capability context for this execution.
    pub fn set_execution_cx(&mut self, cx: Cx) {
        self.execution_cx = cx;
    }

    fn derive_execution_cx(&self) -> Cx {
        self.execution_cx.clone()
    }

    #[allow(clippy::too_many_arguments)]
    fn log_open_storage_cursor_fallback_decision(
        trace_id: u64,
        certifying_mode: bool,
        cursor_id: i32,
        root_page: i32,
        writable: bool,
        backend_kind: &'static str,
        decision_reason: &'static str,
        decision_outcome: &'static str,
        detail: &str,
    ) {
        let rejected = decision_outcome == "vdbe_mempage_fallback_rejected";
        let trace_enabled = if rejected {
            tracing::enabled!(target: "fsqlite.fallback_decision", tracing::Level::WARN)
        } else {
            tracing::enabled!(target: "fsqlite.fallback_decision", tracing::Level::DEBUG)
        };
        if !trace_enabled {
            return;
        }

        let mode = if certifying_mode {
            "parity_cert"
        } else {
            "fallback_allowed"
        };
        let statement_fingerprint =
            format!("vdbe-open-storage-cursor:{cursor_id}:{root_page}:{decision_reason}");
        let statement_kind = "vdbe_open_cursor";
        let backend_identity = format!("{backend_kind}:{mode}");
        let fallback_boundary = format!("vdbe.open_cursor.{decision_reason}");
        let source_touchpoint = "VdbeEngine::open_storage_cursor";
        let first_failure_diag = format!(
            "statement_kind={statement_kind}; fallback_boundary={fallback_boundary}; \
             source_touchpoint={source_touchpoint}; decision_reason={decision_reason}; \
             cursor_id={cursor_id}; root_page={root_page}; writable={writable}; detail={detail}"
        );
        let run_id = "(none)";
        let scenario_id = "(none)";
        let strict_mode = false;
        if rejected {
            tracing::warn!(
                target: "fsqlite.fallback_decision",
                trace_id,
                run_id,
                scenario_id,
                statement_fingerprint = %statement_fingerprint,
                statement_kind,
                backend_identity = %backend_identity,
                fallback_boundary = %fallback_boundary,
                decision_reason,
                certifying_mode,
                strict_mode,
                decision_outcome,
                source_touchpoint,
                first_failure_diag = %first_failure_diag,
                "fallback decision telemetry"
            );
        } else {
            tracing::debug!(
                target: "fsqlite.fallback_decision",
                trace_id,
                run_id,
                scenario_id,
                statement_fingerprint = %statement_fingerprint,
                statement_kind,
                backend_identity = %backend_identity,
                fallback_boundary = %fallback_boundary,
                decision_reason,
                certifying_mode,
                strict_mode,
                decision_outcome,
                source_touchpoint,
                first_failure_diag = %first_failure_diag,
                "fallback decision telemetry"
            );
        }
    }

    fn index_desc_flags_for_root(&self, root_page: i32) -> Vec<bool> {
        self.index_desc_flags_by_root_page
            .get(&root_page)
            .cloned()
            .unwrap_or_default()
    }

    fn index_collations_for_root(&self, root_page: i32) -> Vec<Option<String>> {
        self.index_collations_by_root_page
            .get(&root_page)
            .cloned()
            .unwrap_or_default()
    }

    fn index_desc_flags_for_cursor(&self, cursor_id: i32) -> Vec<bool> {
        if let Some(cursor) = self.storage_cursors.get(&cursor_id) {
            return cursor.cursor.index_desc_flags().to_vec();
        }
        let root_page = self
            .cursor_root_pages
            .get(&cursor_id)
            .copied()
            .unwrap_or_default();
        self.index_desc_flags_for_root(root_page)
    }

    fn index_collations_for_cursor(&self, cursor_id: i32) -> Vec<Option<String>> {
        if let Some(cursor) = self.storage_cursors.get(&cursor_id) {
            return cursor.cursor.index_collations().to_vec();
        }
        let root_page = self
            .cursor_root_pages
            .get(&cursor_id)
            .copied()
            .unwrap_or_default();
        self.index_collations_for_root(root_page)
    }

    fn storage_cursor_find_exact_index_key(
        &mut self,
        cursor_id: i32,
        key_bytes: &[u8],
        missing_registry_detail: &'static str,
    ) -> Result<bool> {
        let index_desc_flags = self.index_desc_flags_for_cursor(cursor_id);
        let index_collations = self.index_collations_for_cursor(cursor_id);
        let uses_collated_probe = index_collations.iter().any(|collation| {
            collation
                .as_deref()
                .is_some_and(|name| !name.eq_ignore_ascii_case("BINARY"))
        });
        let collated_probe_registry = uses_collated_probe.then(|| {
            self.collation_registry
                .lock()
                .unwrap_or_else(|err| err.into_inner())
                .clone()
        });

        let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) else {
            return Ok(false);
        };

        if uses_collated_probe {
            let Some(collation_registry) = collated_probe_registry.as_ref() else {
                return Err(FrankenError::internal(missing_registry_detail));
            };
            storage_cursor_exact_match_collated(
                cursor_id,
                cursor,
                key_bytes,
                &index_desc_flags,
                &index_collations,
                collation_registry,
            )
        } else {
            cursor
                .cursor
                .index_move_to(&cursor.cx, key_bytes)
                .map(|seek| seek.is_found())
        }
    }

    fn table_root_page_for_cursor(&self, cursor_id: i32) -> Option<i32> {
        self.cursor_root_pages.get(&cursor_id).copied().or_else(|| {
            self.storage_cursors
                .get(&cursor_id)
                .map(|cursor| cursor.root_page)
        })
    }

    fn logical_table_payload_value(
        &self,
        table_root_page: Option<i32>,
        payload_values: &[SqliteValue],
        rowid: i64,
        column_index: usize,
    ) -> SqliteValue {
        let rowid_alias_col_idx = table_root_page
            .and_then(|root_page| self.rowid_alias_col_by_root_page.get(&root_page))
            .copied();
        if rowid_alias_col_idx == Some(column_index) {
            return SqliteValue::Integer(rowid);
        }

        let payload_includes_rowid_alias = rowid_alias_col_idx.is_some_and(|ipk_idx| {
            payload_includes_rowid_alias(
                payload_values,
                rowid,
                ipk_idx,
                table_root_page.and_then(|root_page| {
                    self.table_column_count_by_root_page
                        .get(&root_page)
                        .copied()
                }),
                table_root_page.and_then(|root_page| {
                    self.first_not_null_non_ipk_col_by_root_page
                        .get(&root_page)
                        .copied()
                }),
            )
        });

        let payload_index = if let Some(ipk_idx) = rowid_alias_col_idx
            && column_index > ipk_idx
            && !payload_includes_rowid_alias
        {
            column_index - 1
        } else {
            column_index
        };

        payload_values
            .get(payload_index)
            .cloned()
            .unwrap_or_else(|| {
                table_root_page
                    .and_then(|root_page| self.column_defaults_by_root_page.get(&root_page))
                    .and_then(|defaults| defaults.get(column_index))
                    .and_then(|default| default.as_ref())
                    .cloned()
                    .unwrap_or(SqliteValue::Null)
            })
    }

    fn index_key_values_from_table_payload(
        &self,
        table_root_page: Option<i32>,
        payload_values: &[SqliteValue],
        rowid: i64,
        column_indices: &[usize],
    ) -> Vec<SqliteValue> {
        let mut key_values = Vec::with_capacity(column_indices.len().saturating_add(1));
        for &col_idx in column_indices {
            key_values.push(self.logical_table_payload_value(
                table_root_page,
                payload_values,
                rowid,
                col_idx,
            ));
        }
        key_values.push(SqliteValue::Integer(rowid));
        key_values
    }

    /// Handles REPLACE conflict resolution natively (bd-2yqp6.x).
    /// Deletes the conflicting row from the table AND from all associated indexes.
    fn native_replace_row(&mut self, tbl_cursor_id: i32, conflict_rowid: i64) -> Result<()> {
        let old_payload = if let Some(tsc) = self.storage_cursors.get_mut(&tbl_cursor_id) {
            if tsc
                .cursor
                .table_move_to(&tsc.cx, conflict_rowid)?
                .is_found()
            {
                Some(tsc.cursor.payload(&tsc.cx)?)
            } else {
                None
            }
        } else {
            None
        };

        let Some(payload) = old_payload else {
            return Ok(());
        };

        // Parse old row to extract column values for index key construction.
        let old_row = parse_record(&payload).ok_or_else(|| {
            FrankenError::internal("delete_secondary_index_entries: malformed table record")
        })?;
        let table_root_page = self.table_root_page_for_cursor(tbl_cursor_id);

        // Delete secondary index entries for the old row using the metadata
        // registered by the codegen. For each index cursor, build the index
        // key from the old row's column values and delete it.
        let table_index_meta = Arc::clone(&self.table_index_meta);
        if let Some(index_metas) = table_index_meta.get(&tbl_cursor_id) {
            for meta in index_metas.iter() {
                let key_values = self.index_key_values_from_table_payload(
                    table_root_page,
                    &old_row,
                    conflict_rowid,
                    &meta.column_indices,
                );
                let key_bytes = encode_record(&key_values);

                // Seek to the key in the index cursor and delete it.
                let found =
                    self.storage_cursor_find_exact_index_key(
                        meta.cursor_id,
                        &key_bytes,
                        "delete_secondary_index_entries: missing collation registry for collated exact probe",
                    )?;
                if found
                    && let Some(sc) = self.storage_cursors.get_mut(&meta.cursor_id)
                    && sc.writable
                {
                    sc.cursor.delete(&sc.cx)?;
                    invalidate_storage_cursor_row_cache_with_reason(
                        sc,
                        self.collect_vdbe_metrics,
                        DecodeCacheInvalidationReason::WriteMutation,
                    );
                }
            }
        }

        // Delete the table row.
        if let Some(tsc) = self.storage_cursors.get_mut(&tbl_cursor_id) {
            tsc.cursor.table_move_to(&tsc.cx, conflict_rowid)?;
            tsc.cursor.delete(&tsc.cx)?;
            invalidate_storage_cursor_row_cache_with_reason(
                tsc,
                self.collect_vdbe_metrics,
                DecodeCacheInvalidationReason::WriteMutation,
            );
        }
        self.sync_storage_table_delete_into_memdb_mirror(tbl_cursor_id, conflict_rowid);

        Ok(())
    }

    fn rollback_pending_insert_after_index_conflict(&mut self) -> Result<()> {
        let entries = self.take_pending_idx_entries();
        for (idx_cid, idx_key) in entries {
            if self.storage_cursor_find_exact_index_key(
                idx_cid,
                &idx_key,
                "rollback_pending_insert_after_index_conflict: missing collation registry for collated exact probe",
            )? && let Some(isc) = self.storage_cursors.get_mut(&idx_cid)
            {
                isc.cursor.delete(&isc.cx)?;
                invalidate_storage_cursor_row_cache_with_reason(
                    isc,
                    self.collect_vdbe_metrics,
                    DecodeCacheInvalidationReason::WriteMutation,
                );
            }
        }

        let rollback = self.take_pending_insert_rollback().ok_or_else(|| {
            FrankenError::internal("secondary-index conflict without pending table insert")
        })?;
        let tsc = self
            .storage_cursors
            .get_mut(&rollback.cursor_id)
            .ok_or_else(|| {
                FrankenError::internal("table cursor missing during secondary-index rollback")
            })?;
        if !tsc
            .cursor
            .table_move_to(&tsc.cx, rollback.rowid)?
            .is_found()
        {
            return Err(FrankenError::internal(
                "failed to locate provisional table row during secondary-index rollback",
            ));
        }
        tsc.cursor.delete(&tsc.cx)?;
        invalidate_storage_cursor_row_cache_with_reason(
            tsc,
            self.collect_vdbe_metrics,
            DecodeCacheInvalidationReason::WriteMutation,
        );
        let rollback_cursor_id = rollback.cursor_id;
        let rollback_rowid = rollback.rowid;
        self.changes = self.changes.checked_sub(1).ok_or_else(|| {
            FrankenError::internal("secondary-index rollback underflowed change counter")
        })?;
        self.sync_storage_table_delete_into_memdb_mirror(rollback_cursor_id, rollback_rowid);
        if let Some(update_restore) = rollback.update_restore {
            self.restore_pending_update_after_conflict(update_restore)?;
        }
        self.last_insert_rowid = rollback.previous_last_insert_rowid;
        self.last_insert_rowid_valid = rollback.previous_last_insert_rowid_valid;
        self.last_insert_cursor_id = None;
        Ok(())
    }

    fn restore_pending_update_after_conflict(
        &mut self,
        restore: PendingUpdateRestore,
    ) -> Result<()> {
        match restore {
            PendingUpdateRestore::Storage {
                cursor_id,
                rowid,
                payload,
            } => {
                let tsc = self.storage_cursors.get_mut(&cursor_id).ok_or_else(|| {
                    FrankenError::internal("table cursor missing during UPDATE conflict restore")
                })?;
                tsc.cursor.table_insert(&tsc.cx, rowid, &payload)?;
                invalidate_storage_cursor_row_cache_with_reason(
                    tsc,
                    self.collect_vdbe_metrics,
                    DecodeCacheInvalidationReason::WriteMutation,
                );

                let table_index_meta = Arc::clone(&self.table_index_meta);
                let table_root_page = self.table_root_page_for_cursor(cursor_id);
                if let Some(index_metas) = table_index_meta.get(&cursor_id) {
                    let old_row = parse_record(&payload).ok_or_else(|| {
                        FrankenError::internal(
                            "UPDATE conflict restore could not decode original row payload",
                        )
                    })?;
                    for meta in index_metas.iter() {
                        let key_values = self.index_key_values_from_table_payload(
                            table_root_page,
                            &old_row,
                            rowid,
                            &meta.column_indices,
                        );
                        let key_bytes = encode_record(&key_values);
                        if let Some(sc) = self.storage_cursors.get_mut(&meta.cursor_id)
                            && sc.writable
                        {
                            sc.cursor.index_insert(&sc.cx, &key_bytes)?;
                            invalidate_storage_cursor_row_cache_with_reason(
                                sc,
                                self.collect_vdbe_metrics,
                                DecodeCacheInvalidationReason::WriteMutation,
                            );
                        }
                    }
                }
                self.sync_storage_table_restore_into_memdb_mirror(cursor_id, rowid, &payload);
            }
            PendingUpdateRestore::Mem {
                root_page,
                rowid,
                values,
            } => {
                let db = self.db.as_mut().ok_or_else(|| {
                    FrankenError::internal("MemDatabase missing during UPDATE conflict restore")
                })?;
                db.upsert_row(root_page, rowid, values);
            }
        }
        Ok(())
    }

    /// Attach an in-memory database for cursor operations.
    pub fn set_database(&mut self, mut db: MemDatabase) {
        db.set_collation_registry(Arc::clone(&self.collation_registry));
        self.db = Some(db);
    }

    /// Declare whether the attached `MemDatabase` is a complete row mirror for
    /// storage-backed table reads.
    pub fn set_memdb_rows_loaded(&mut self, loaded: bool) {
        self.memdb_rows_loaded = loaded;
    }

    /// Declare whether storage-cursor `Count` and row-mirror scan fast paths
    /// may trust the attached `MemDatabase` row mirror.
    pub fn set_storage_cursor_memdb_count_shortcuts_safe(&mut self, safe: bool) {
        self.storage_cursor_memdb_count_shortcuts_safe = safe;
    }

    /// Whether the attached `MemDatabase` row mirror still exactly matches
    /// storage-backed table contents for fast-path reads.
    pub fn storage_cursor_memdb_count_shortcuts_safe(&self) -> bool {
        self.storage_cursor_memdb_count_shortcuts_safe
    }

    /// Mark a root page as dirty for lazy MemDB sync.
    /// Instead of syncing MemDB on every DML operation, we just mark the table
    /// dirty and bulk-sync only when a compatibility-path query needs MemDB data.
    #[inline]
    fn mark_root_page_dirty(&mut self, root_page: i32) {
        self.dirty_root_pages.insert(root_page);
    }

    /// Mark a known storage-backed table root page dirty without another
    /// cursor-to-root lookup on the hot write path.
    #[inline]
    fn mark_storage_root_page_dirty(&mut self, root_page: i32) {
        self.mark_root_page_dirty(root_page);
        // Signal to Connection that MemDB is stale — queries should fall
        // back to pager-backed reads until a sync or reload occurs.
        self.storage_cursor_memdb_count_shortcuts_safe = false;
    }

    /// Mark the table identified by cursor_id as dirty for lazy MemDB sync
    /// when the caller does not already know the table root page.
    /// Also sets `storage_cursor_memdb_count_shortcuts_safe` to false so the
    /// Connection layer knows MemDB is stale and will fall back to pager reads.
    fn mark_storage_table_dirty(&mut self, cursor_id: i32) {
        if let Some(root_page) = self.cursor_root_pages.get(&cursor_id).copied() {
            self.mark_storage_root_page_dirty(root_page);
        }
    }

    /// Returns true if any tables have been modified and need MemDB sync.
    pub fn has_dirty_root_pages(&self) -> bool {
        !self.dirty_root_pages.is_empty()
    }

    /// Returns the set of dirty root pages for bulk sync.
    pub fn dirty_root_pages(&self) -> &HashSet<i32> {
        &self.dirty_root_pages
    }

    /// Clear dirty root pages after bulk sync completes.
    pub fn clear_dirty_root_pages(&mut self) {
        self.dirty_root_pages.clear();
    }

    fn sync_storage_table_delete_into_memdb_mirror(&mut self, cursor_id: i32, _rowid: i64) {
        // bd-g595c: Lazy MemDB dirty tracking — mark dirty instead of immediate sync.
        self.mark_storage_table_dirty(cursor_id);
    }

    fn sync_storage_table_restore_into_memdb_mirror(
        &mut self,
        cursor_id: i32,
        _rowid: i64,
        _payload: &[u8],
    ) {
        // bd-g595c: Lazy MemDB dirty tracking — mark dirty instead of immediate sync.
        self.mark_storage_table_dirty(cursor_id);
    }

    /// Take ownership of the in-memory database back from the engine.
    pub fn take_database(&mut self) -> Option<MemDatabase> {
        self.db.take()
    }

    /// Register a type-erased virtual table cursor for opcode execution.
    pub fn register_vtab_cursor(&mut self, cursor_id: i32, cursor: Box<dyn ErasedVtabCursor>) {
        self.ensure_cold_state_for(StatementColdState::VTAB_CURSORS)
            .vtab_cursors
            .insert(
                cursor_id,
                VtabCursorState {
                    cursor,
                    null_row: false,
                },
            );
    }

    /// Register a virtual table instance for lifecycle and cursor operations.
    pub fn register_vtab_instance(
        &mut self,
        cursor_id: i32,
        instance: Box<dyn fsqlite_func::vtab::ErasedVtabInstance>,
    ) {
        self.vtab_instances
            .insert(cursor_id, Box::new(ErasedVtabBridge(instance)));
    }

    /// Enable/disable storage-backed cursor execution for `OpenRead`/`OpenWrite`.
    pub fn enable_storage_cursors(&mut self, enabled: bool) {
        self.storage_cursors_enabled = enabled;
    }

    /// Backwards-compatible alias for [`Self::enable_storage_cursors`].
    pub fn enable_storage_read_cursors(&mut self, enabled: bool) {
        self.enable_storage_cursors(enabled);
    }

    /// Enable parity-certification mode (bd-2ttd8.1).
    ///
    /// When enabled, `open_storage_cursor` will refuse to fall back to the
    /// in-memory `MemPageStore` path and instead return an error. This
    /// verifies that all cursor operations route through the real
    /// Pager+BtreeCursor stack (`txn_page_io`).
    pub fn set_reject_mem_fallback(&mut self, reject: bool) {
        self.reject_mem_fallback = reject;
    }

    /// Returns `true` if all open storage cursors use the real pager backend
    /// (`CursorBackend::Txn`). Returns `true` vacuously when no cursors are open.
    ///
    /// Used by parity-certification (bd-2ttd8.4) to verify that no cursor
    /// accidentally routed through MemPageStore.
    #[must_use]
    pub fn all_cursors_are_txn_backed(&self) -> bool {
        self.storage_cursors.values().all(|sc| sc.cursor.is_txn())
    }

    /// Returns `true` if any open storage cursor uses the in-memory backend.
    #[must_use]
    pub fn has_mem_cursor(&self) -> bool {
        self.storage_cursors.values().any(|sc| sc.cursor.is_mem())
    }

    /// Validate the parity-certification invariant: if `reject_mem_fallback`
    /// is enabled, no storage cursor should be backed by MemPageStore.
    ///
    /// Returns `Ok(())` if the invariant holds, or `Err` with a diagnostic
    /// message listing the offending cursor IDs.
    pub fn validate_parity_cert_invariant(&self) -> std::result::Result<(), String> {
        if !self.reject_mem_fallback {
            return Ok(());
        }
        let mem_cursors: Vec<i32> = self
            .storage_cursors
            .iter()
            .filter(|(_, sc)| sc.cursor.is_mem())
            .map(|(id, _)| id)
            .collect();
        if mem_cursors.is_empty() {
            Ok(())
        } else {
            Err(format!(
                "bd-2ttd8.4: parity-cert violation — {} cursor(s) routed through MemPageStore: {:?}",
                mem_cursors.len(),
                mem_cursors
            ))
        }
    }

    fn attach_transaction_state(
        &mut self,
        txn: TransactionKind,
        concurrent: Option<ConcurrentContext>,
    ) {
        if let Some(txn_page_io) = self.txn_page_io.as_ref() {
            txn_page_io.refill(txn, concurrent);
        } else {
            self.txn_page_io = Some(SharedTxnPageIo::from_parts(txn, concurrent));
        }
        self.storage_cursors_enabled = true;
    }

    /// Lend a pager transaction to the engine for storage cursor I/O.
    ///
    /// When set, `open_storage_cursor` routes through the real pager/WAL
    /// stack (`SharedTxnPageIo`) instead of building transient `MemPageStore`
    /// snapshots. Also enables storage cursors automatically.
    pub fn set_transaction(&mut self, txn: impl Into<TransactionKind>) {
        self.attach_transaction_state(txn.into(), None);
    }

    /// Lend a pager transaction with MVCC concurrent context (bd-kivg / 5E.2).
    ///
    /// Like [`set_transaction`](Self::set_transaction), but also enables
    /// MVCC page-level locking for concurrent writers. When the concurrent
    /// context is present:
    /// - Write operations acquire page-level locks via [`concurrent_write_page`]
    /// - Written pages are recorded in the write set for FCW validation at commit
    pub fn set_transaction_concurrent(
        &mut self,
        txn: impl Into<TransactionKind>,
        session_id: u64,
        handle: SharedConcurrentHandle,
        lock_table: Arc<InProcessPageLockTable>,
        commit_index: Arc<CommitIndex>,
        busy_timeout_ms: u64,
    ) {
        self.attach_transaction_state(
            txn.into(),
            Some(ConcurrentContext::new(
                session_id,
                handle,
                lock_table,
                commit_index,
                busy_timeout_ms,
            )),
        );
    }

    /// Provide the shared concurrent rowid allocator for implicit inserts.
    pub fn set_concurrent_rowid_allocator(
        &mut self,
        allocator: Arc<ConcurrentRowIdAllocator>,
        schema_epoch: SchemaEpoch,
    ) {
        self.concurrent_rowid_allocator = Some(allocator);
        self.concurrent_rowid_schema_epoch = schema_epoch;
    }

    fn storage_cursor_runtime_meta(&self, root_page: i32) -> (RowIdMode, i64) {
        match self.autoincrement_seq_by_root_page.get(&root_page).copied() {
            Some(high_water) => (RowIdMode::AutoIncrement, high_water),
            None => (RowIdMode::Normal, 0),
        }
    }

    fn storage_cursor_visible_max_rowid(sc: &mut StorageCursor) -> Result<i64> {
        if sc.cursor.last(&sc.cx)? {
            sc.cursor.rowid(&sc.cx)
        } else {
            Ok(0)
        }
    }

    fn concurrent_rowid_key(schema_epoch: SchemaEpoch, root_page: i32) -> Result<AllocatorKey> {
        let root_page_u32 = u32::try_from(root_page).map_err(|_| {
            FrankenError::internal(format!(
                "invalid concurrent rowid root page {root_page} for allocator"
            ))
        })?;
        Ok(AllocatorKey {
            schema_epoch,
            table_id: TableId::new(root_page_u32),
        })
    }

    fn map_rowid_allocator_error(
        err: impl std::fmt::Display,
        detail: &'static str,
    ) -> FrankenError {
        FrankenError::VdbeExecutionError {
            detail: format!("{detail}: {err}"),
        }
    }

    fn allocate_serialized_storage_rowid(
        sc: &mut StorageCursor,
        autoinc_max: i64,
        overflow_detail: &'static str,
    ) -> Result<i64> {
        let base = if sc.last_alloc_rowid > 0 {
            sc.last_alloc_rowid.max(autoinc_max)
        } else {
            Self::storage_cursor_visible_max_rowid(sc)?.max(autoinc_max)
        };
        let rowid = base
            .checked_add(1)
            .ok_or_else(|| FrankenError::VdbeExecutionError {
                detail: overflow_detail.into(),
            })?;
        sc.last_alloc_rowid = rowid;
        Ok(rowid)
    }

    fn allocate_concurrent_storage_rowid(
        allocator: &ConcurrentRowIdAllocator,
        schema_epoch: SchemaEpoch,
        root_page: i32,
        mode: RowIdMode,
        autoinc_max: i64,
        sc: &mut StorageCursor,
        overflow_detail: &'static str,
    ) -> Result<i64> {
        let visible_max = if sc.last_alloc_rowid > 0 {
            sc.last_alloc_rowid.max(autoinc_max)
        } else {
            Self::storage_cursor_visible_max_rowid(sc)?.max(autoinc_max)
        };
        let key = Self::concurrent_rowid_key(schema_epoch, root_page)?;
        allocator.ensure_table_floor(
            key,
            (visible_max > 0).then(|| RowId::new(visible_max)),
            autoinc_max,
            mode,
        );
        let rowid = allocator
            .allocate_one(key)
            .map_err(|err| Self::map_rowid_allocator_error(err, overflow_detail))?
            .get();
        sc.last_alloc_rowid = rowid;
        Ok(rowid)
    }

    fn bump_concurrent_storage_rowid_floor(
        allocator: &ConcurrentRowIdAllocator,
        schema_epoch: SchemaEpoch,
        root_page: i32,
        mode: RowIdMode,
        autoinc_max: i64,
        sc: &mut StorageCursor,
        rowid: i64,
    ) -> Result<()> {
        let visible_max = Self::storage_cursor_visible_max_rowid(sc)?
            .max(autoinc_max)
            .max(rowid);
        let key = Self::concurrent_rowid_key(schema_epoch, root_page)?;
        allocator.ensure_table_floor(
            key,
            (visible_max > 0).then(|| RowId::new(visible_max)),
            autoinc_max,
            mode,
        );
        allocator
            .bump_explicit(key, RowId::new(rowid))
            .map_err(|err| Self::map_rowid_allocator_error(err, "explicit rowid bump failed"))?;
        Ok(())
    }

    /// Take back the pager transaction after execution.
    ///
    /// All storage cursors must be dropped first (cleared during execution
    /// cleanup).
    pub fn take_transaction(&mut self) -> Result<Option<TransactionKind>> {
        // Drop all storage cursors first to release Rc references.
        self.storage_cursors.clear();
        match self.txn_page_io.take() {
            Some(txn_page_io) => Ok(Some(txn_page_io.into_inner()?)),
            None => Ok(None),
        }
    }

    /// bd-perf: Swap the inner transaction without dropping the Rc.
    /// Retained StorageCursors hold Rc clones that remain valid.
    /// Returns the old transaction.
    pub fn refill_transaction(
        &mut self,
        txn: impl Into<TransactionKind>,
    ) -> Option<TransactionKind> {
        self.txn_page_io
            .as_ref()
            .map(|io| io.refill(txn.into(), None))
    }

    /// bd-perf: Extract the transaction while keeping the Rc alive for
    /// retained cursors. The Rc inner becomes TransactionKind::Drained
    /// until the next refill_transaction call.
    pub fn drain_transaction(&mut self) -> Option<TransactionKind> {
        self.txn_page_io.as_ref().map(|io| io.drain())
    }

    /// bd-perf: Check if a txn_page_io is present (for cursor reuse decisions).
    pub fn has_txn_page_io(&self) -> bool {
        self.txn_page_io.is_some()
    }

    /// bd-perf: Check if storage cursors are empty (for drain vs take decision).
    pub fn storage_cursors_empty(&self) -> bool {
        self.storage_cursors.is_empty()
    }

    /// Attach a function registry for `Function`/`PureFunc` opcode dispatch.
    pub fn set_function_registry(&mut self, registry: Arc<FunctionRegistry>) {
        self.func_registry = Some(registry);
    }

    /// Attach a shared collation registry for compare and sorting opcodes.
    pub fn set_collation_registry(&mut self, registry: Arc<Mutex<CollationRegistry>>) {
        self.collation_registry = Arc::clone(&registry);
        if let Some(db) = self.db.as_mut() {
            db.set_collation_registry(registry);
        }
    }

    /// Acquire a read-lock on the collation registry.  Callers should hold
    /// the guard for the duration of a comparison batch (e.g. an entire
    /// opcode or sort run) rather than re-acquiring per comparison.
    #[inline]
    fn lock_collation(&self) -> std::sync::MutexGuard<'_, CollationRegistry> {
        self.collation_registry
            .lock()
            .unwrap_or_else(|e| e.into_inner())
    }

    /// Replace the current set of bound SQL parameters.
    ///
    /// Values are 1-indexed at execution time (`?1` maps to `bindings[0]`).
    pub fn set_bindings(&mut self, bindings: Vec<SqliteValue>) {
        self.bindings = bindings.into_iter().collect();
    }

    /// Replace bindings from a slice while keeping small parameter sets inline.
    pub fn set_bindings_slice(&mut self, bindings: &[SqliteValue]) {
        self.bindings.clear();
        self.bindings.extend(bindings.iter().cloned());
    }

    /// Set the schema cookie that `ReadCookie` will return and
    /// `Transaction` will use for stale-schema detection (bd-3mmj).
    pub fn set_schema_cookie(&mut self, cookie: u32) {
        self.schema_cookie = cookie;
    }

    /// Read the current schema cookie value (possibly updated by `SetCookie`).
    pub fn schema_cookie(&self) -> u32 {
        self.schema_cookie
    }

    /// Provide AUTOINCREMENT high-water marks keyed by root page (bd-31j76).
    /// The engine uses these to guarantee monotonically increasing rowids
    /// for tables declared with `AUTOINCREMENT`.
    pub fn set_autoincrement_sequence_by_root_page(&mut self, map: HashMap<i32, i64>) {
        self.autoincrement_seq_by_root_page = map;
    }

    /// Provide INTEGER PRIMARY KEY alias column positions keyed by root page.
    pub fn set_rowid_alias_column_by_root_page(&mut self, map: HashMap<i32, usize>) {
        self.rowid_alias_col_by_root_page = Arc::new(map);
    }

    /// Reuse shared INTEGER PRIMARY KEY alias column positions keyed by root page.
    pub fn set_shared_rowid_alias_column_by_root_page(&mut self, map: Arc<HashMap<i32, usize>>) {
        self.rowid_alias_col_by_root_page = map;
    }

    /// Provide declared table column counts keyed by root page.
    pub fn set_table_column_count_by_root_page(&mut self, map: HashMap<i32, usize>) {
        self.table_column_count_by_root_page = Arc::new(map);
    }

    /// Reuse shared declared table column counts keyed by root page.
    pub fn set_shared_table_column_count_by_root_page(&mut self, map: Arc<HashMap<i32, usize>>) {
        self.table_column_count_by_root_page = map;
    }

    /// Provide earliest non-IPK NOT NULL columns keyed by root page.
    pub fn set_first_not_null_non_ipk_col_by_root_page(&mut self, map: HashMap<i32, usize>) {
        self.first_not_null_non_ipk_col_by_root_page = Arc::new(map);
    }

    /// Reuse shared earliest non-IPK NOT NULL columns keyed by root page.
    pub fn set_shared_first_not_null_non_ipk_col_by_root_page(
        &mut self,
        map: Arc<HashMap<i32, usize>>,
    ) {
        self.first_not_null_non_ipk_col_by_root_page = map;
    }

    /// Set column default values by root page, used for ALTER TABLE ADD COLUMN.
    /// Each entry maps a root page to a list of per-column defaults (None = no default).
    pub fn set_column_defaults_by_root_page(
        &mut self,
        map: HashMap<i32, Vec<Option<SqliteValue>>>,
    ) {
        self.column_defaults_by_root_page = Arc::new(map);
    }

    /// Reuse shared column default values by root page.
    pub fn set_shared_column_defaults_by_root_page(
        &mut self,
        map: Arc<HashMap<i32, Vec<Option<SqliteValue>>>>,
    ) {
        self.column_defaults_by_root_page = map;
    }

    /// Provide per-index descending flags keyed by index root page.
    pub fn set_index_desc_flags_by_root_page(&mut self, map: HashMap<i32, Vec<bool>>) {
        self.index_desc_flags_by_root_page = Arc::new(map);
    }

    /// Reuse shared per-index descending flags keyed by index root page.
    pub fn set_shared_index_desc_flags_by_root_page(&mut self, map: Arc<HashMap<i32, Vec<bool>>>) {
        self.index_desc_flags_by_root_page = map;
    }

    /// Provide per-index collation sequences keyed by index root page.
    pub fn set_index_collations_by_root_page(&mut self, map: HashMap<i32, Vec<Option<String>>>) {
        self.index_collations_by_root_page = Arc::new(map);
    }

    /// Reuse shared per-index collation sequences keyed by index root page.
    pub fn set_shared_index_collations_by_root_page(
        &mut self,
        map: Arc<HashMap<i32, Vec<Option<String>>>>,
    ) {
        self.index_collations_by_root_page = map;
    }

    /// Execute a VDBE program to completion.
    ///
    /// Returns `Ok(ExecOutcome::Done)` on normal halt, or an error if the
    /// program encounters a fatal condition.
    #[allow(
        clippy::too_many_lines,
        clippy::match_same_arms,
        clippy::cast_sign_loss,
        clippy::cast_possible_truncation,
        clippy::cast_possible_wrap
    )]
    pub fn execute(&mut self, program: &VdbeProgram) -> Result<ExecOutcome> {
        self.execute_with_borrowed_bindings_internal(program, None, None)
    }

    /// Execute a program using a borrowed binding slice for this run only.
    ///
    /// This avoids cloning bound parameters into the engine when the caller
    /// already owns a short-lived binding slice for a single execution.
    #[allow(
        clippy::too_many_lines,
        clippy::match_same_arms,
        clippy::cast_sign_loss,
        clippy::cast_possible_truncation,
        clippy::cast_possible_wrap
    )]
    pub fn execute_with_borrowed_bindings(
        &mut self,
        program: &VdbeProgram,
        borrowed_bindings: Option<&[SqliteValue]>,
    ) -> Result<ExecOutcome> {
        self.execute_with_borrowed_bindings_internal(program, borrowed_bindings, None)
    }

    /// Execute a program using a borrowed binding slice and a per-row callback.
    ///
    /// The callback is invoked for each `OP_ResultRow` payload as soon as the
    /// row is materialized, allowing callers to process large result sets
    /// without retaining them in `self.results`.
    pub fn execute_with_borrowed_bindings_and_row_handler(
        &mut self,
        program: &VdbeProgram,
        borrowed_bindings: Option<&[SqliteValue]>,
        row_handler: &mut ResultRowCallback<'_>,
    ) -> Result<ExecOutcome> {
        self.execute_with_borrowed_bindings_internal(program, borrowed_bindings, Some(row_handler))
    }

    fn execute_with_borrowed_bindings_internal(
        &mut self,
        program: &VdbeProgram,
        borrowed_bindings: Option<&[SqliteValue]>,
        mut row_handler: Option<&mut ResultRowCallback<'_>>,
    ) -> Result<ExecOutcome> {
        let _record_profile_scope = enter_record_profile_scope(RecordProfileScope::VdbeEngine);
        let _pipeline_profile_stage = enter_vdbe_execute_profile_stage();
        let estimated_make_record_capacity =
            estimate_make_record_buffer_capacity(program, self.page_size);
        self.make_record_lookaside
            .prepare_for_statement(estimated_make_record_capacity);
        if !self.statement_state_clean {
            self.clear_statement_cold_state();
            self.results.clear();
            self.last_compare_result = None;
            self.changes = 0;
            self.last_insert_rowid = 0;
            self.last_insert_rowid_valid = false;
            self.last_insert_cursor_id = None;
            self.fk_counter = 0;
            self.cursor_root_pages.clear();
        }
        self.statement_state_clean = false;
        self.table_index_meta = Arc::clone(program.shared_table_index_meta());

        let ops = program.ops();
        if ops.is_empty() {
            return Ok(ExecOutcome::Done);
        }

        // Pre-size the register file to the program's declared register count
        // so that per-opcode register writes never need bounds-check + resize.
        // This eliminates a branch from every set_reg/set_reg_fast in the hot loop.
        let reg_count = usize::try_from(program.register_count()).unwrap_or(0);
        if self.registers.len() < reg_count {
            self.registers.resize(reg_count, SqliteValue::Null);
        }

        let statement_debug_enabled =
            tracing::enabled!(target: "fsqlite_vdbe::statement", tracing::Level::DEBUG);
        let jit_enabled = vdbe_jit_enabled();
        let jit_debug_enabled =
            jit_enabled && tracing::enabled!(target: "fsqlite_vdbe::jit", tracing::Level::DEBUG);
        let jit_info_enabled =
            jit_enabled && tracing::enabled!(target: "fsqlite_vdbe::jit", tracing::Level::INFO);
        let jit_warn_enabled =
            jit_enabled && tracing::enabled!(target: "fsqlite_vdbe::jit", tracing::Level::WARN);
        let exec_info_enabled = tracing::enabled!(target: "fsqlite_vdbe", tracing::Level::INFO);
        let slow_query_info_enabled =
            tracing::enabled!(target: "fsqlite_vdbe::slow_query", tracing::Level::INFO);
        let collect_vdbe_metrics = vdbe_metrics_enabled();
        self.collect_vdbe_metrics = collect_vdbe_metrics;
        let needs_statement_timing = collect_vdbe_metrics
            || statement_debug_enabled
            || exec_info_enabled
            || slow_query_info_enabled;
        let program_id = if statement_debug_enabled
            || jit_debug_enabled
            || jit_info_enabled
            || jit_warn_enabled
            || exec_info_enabled
            || slow_query_info_enabled
        {
            VDBE_PROGRAM_ID_SEQ.fetch_add(1, AtomicOrdering::Relaxed)
        } else {
            0
        };
        let start_time = needs_statement_timing.then(Instant::now);
        let mut opcode_count: u64 = 0;
        let mut local_opcode_execution_totals =
            collect_vdbe_metrics.then(|| vec![0_u64; Opcode::COUNT + 1].into_boxed_slice());

        if statement_debug_enabled {
            tracing::debug!(
                target: "fsqlite_vdbe::statement",
                program_id,
                num_ops = ops.len(),
                "vdbe statement begin",
            );
        }

        let mut compiled_program = None;
        if jit_enabled {
            match maybe_trigger_jit(program) {
                JitDecision::Disabled => {}
                JitDecision::Warming {
                    plan_hash,
                    execution_count,
                } => {
                    if jit_debug_enabled {
                        tracing::debug!(
                            target: "fsqlite_vdbe::jit",
                            program_id,
                            plan_hash = format_args!("{plan_hash:016x}"),
                            execution_count,
                            hot_threshold = vdbe_jit_hot_threshold(),
                            "jit warmup (interpreter tier)"
                        );
                    }
                }
                JitDecision::CacheHit {
                    plan_hash,
                    code_size_bytes,
                    compiled_program: cached_program,
                } => {
                    if jit_info_enabled {
                        tracing::info!(
                            target: "fsqlite_vdbe::jit",
                            program_id,
                            plan_hash = format_args!("{plan_hash:016x}"),
                            code_size_bytes,
                            "jit trigger cache hit"
                        );
                    }
                    compiled_program = Some(cached_program);
                }
                JitDecision::UnsupportedCached { plan_hash } => {
                    if jit_debug_enabled {
                        tracing::debug!(
                            target: "fsqlite_vdbe::jit",
                            program_id,
                            plan_hash = format_args!("{plan_hash:016x}"),
                            "jit unsupported-plan cache hit (skipping recompilation)"
                        );
                    }
                }
                JitDecision::Compiled {
                    plan_hash,
                    compile_time_us,
                    code_size_bytes,
                    evicted_plan_hash,
                    compiled_program: newly_compiled_program,
                } => {
                    if jit_info_enabled {
                        let plan_hash_hex = format!("{plan_hash:016x}");
                        let span = tracing::info_span!(
                            target: "fsqlite_vdbe::jit",
                            "jit_compile",
                            plan_hash = %plan_hash_hex,
                            compile_time_us,
                            code_size_bytes,
                        );
                        let _compile_guard = span.enter();
                        tracing::info!(
                            target: "fsqlite_vdbe::jit",
                            program_id,
                            plan_hash = %plan_hash_hex,
                            compile_time_us,
                            code_size_bytes,
                            evicted_plan_hash = evicted_plan_hash.map(|value| format!("{value:016x}")),
                            "jit trigger compile completed"
                        );
                    }
                    compiled_program = Some(newly_compiled_program);
                }
                JitDecision::CompileFailed {
                    plan_hash,
                    compile_time_us,
                    reason,
                } => {
                    if jit_warn_enabled {
                        let plan_hash_hex = format!("{plan_hash:016x}");
                        let span = tracing::info_span!(
                            target: "fsqlite_vdbe::jit",
                            "jit_compile",
                            plan_hash = %plan_hash_hex,
                            compile_time_us,
                            code_size_bytes = 0_u64,
                        );
                        let _compile_guard = span.enter();
                        tracing::warn!(
                            target: "fsqlite_vdbe::jit",
                            program_id,
                            plan_hash = %plan_hash_hex,
                            compile_time_us,
                            reason,
                            "jit compilation failed; falling back to interpreter"
                        );
                    }
                }
            }
        }

        if let Some(compiled_program) = compiled_program {
            let outcome = self.execute_compiled_program(
                compiled_program.as_ref(),
                borrowed_bindings,
                collect_vdbe_metrics,
                &mut row_handler,
            )?;

            if !needs_statement_timing {
                return Ok(outcome);
            }

            let elapsed = start_time
                .expect("statement timing state exists when post-execution bookkeeping is enabled")
                .elapsed();
            let elapsed_us = elapsed.as_micros();
            let result_rows = self.results.len();

            if collect_vdbe_metrics {
                FSQLITE_VDBE_OPCODES_EXECUTED_TOTAL.fetch_add(0, AtomicOrdering::Relaxed);
                FSQLITE_VDBE_STATEMENTS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
                #[allow(clippy::cast_possible_truncation)]
                FSQLITE_VDBE_STATEMENT_DURATION_US_TOTAL
                    .fetch_add(elapsed_us as u64, AtomicOrdering::Relaxed);
            }

            let log_statement_done = || {
                if statement_debug_enabled {
                    tracing::debug!(
                        target: "fsqlite_vdbe::statement",
                        program_id,
                        opcode_count = 0_u64,
                        result_rows,
                        elapsed_us = elapsed_us as u64,
                        outcome = ?outcome,
                        "vdbe statement done",
                    );
                }

                if slow_query_info_enabled && elapsed.as_millis() >= SLOW_QUERY_THRESHOLD_MS {
                    #[allow(clippy::cast_possible_truncation)]
                    let millis = elapsed.as_millis() as u64;
                    tracing::info!(
                        target: "fsqlite_vdbe::slow_query",
                        program_id,
                        opcode_count = 0_u64,
                        result_rows,
                        elapsed_ms = millis,
                        "slow vdbe statement",
                    );
                }
            };

            if exec_info_enabled {
                let span = tracing::info_span!(
                    target: "fsqlite_vdbe",
                    "vdbe_exec",
                    opcode_count = 0_u64,
                    program_id,
                    result_rows,
                    elapsed_us = elapsed_us as u64,
                );
                let _guard = span.enter();
                log_statement_done();
            } else {
                log_statement_done();
            }

            return Ok(outcome);
        }

        let mut pc: usize = 0;
        // "once" flags: one bit per instruction address (stack-backed for small programs).
        let n_ops = ops.len();
        let mut once_stack = [0u64; 4]; // covers up to 256 opcodes on the stack
        let mut once_heap: Vec<u64> = if n_ops > 256 {
            vec![0u64; n_ops.div_ceil(64)]
        } else {
            Vec::new()
        };
        let once_bits: &mut [u64] = if n_ops > 256 {
            &mut once_heap
        } else {
            &mut once_stack
        };

        // bd-perf (V2.3): Bounds check eliminated — all programs terminate via
        // OP_Halt which breaks the loop. The sentinel is guaranteed by codegen.
        // This saves ~1ns per opcode on the hot path.
        let outcome = loop {
            // Safety: pc is always in-bounds because:
            // 1. Every program ends with Halt (codegen guarantee)
            // 2. Goto/If targets are validated at compile time
            // 3. pc is only modified by opcode handlers to valid targets
            debug_assert!(
                pc < ops.len(),
                "VDBE pc={pc} out of bounds (len={})",
                ops.len()
            );
            if opcode_count & (VDBE_EXECUTION_CHECKPOINT_INTERVAL - 1) == 0 {
                observe_execution_cancellation(&self.execution_cx)?;
            }

            let op = &ops[pc];
            opcode_count += 1;
            if collect_vdbe_metrics {
                if let Some(local_opcode_execution_totals) = local_opcode_execution_totals.as_mut()
                {
                    let opcode_idx = usize::from(op.opcode as u8);
                    local_opcode_execution_totals[opcode_idx] =
                        local_opcode_execution_totals[opcode_idx].saturating_add(1);
                }
            }
            if self.trace_opcodes {
                self.trace_opcode(pc, op);
            }
            if self.try_execute_hot_opcode(
                op,
                &mut pc,
                collect_vdbe_metrics,
                &mut row_handler,
                borrowed_bindings,
            )? {
                continue;
            }
            #[allow(unreachable_patterns)]
            match op.opcode {
                // ── Control Flow ────────────────────────────────────────
                Opcode::Init => {
                    // Jump to p2 if it points to a valid instruction.
                    // In the standard SQLite pattern, p2 points to a Goto
                    // at the end that bounces back. If p2 points past the
                    // end (our codegen pattern), fall through.
                    let target = op.p2 as usize;
                    if op.p2 > 0 && target < ops.len() {
                        pc = target;
                        continue;
                    }
                    pc += 1;
                }

                Opcode::Goto => {
                    pc = op.p2 as usize;
                }

                Opcode::Halt => {
                    if op.p1 != 0 {
                        let msg = match &op.p4 {
                            P4::Str(s) => s.clone(),
                            _ => format!("halt with error code {}", op.p1),
                        };
                        break ExecOutcome::Error {
                            code: op.p1,
                            message: msg,
                        };
                    }
                    break ExecOutcome::Done;
                }

                Opcode::Noop => {
                    pc += 1;
                }

                Opcode::SetSnapshot => {
                    self.execute_set_snapshot_cold(op)?;
                    pc += 1;
                }

                // ── Constants ───────────────────────────────────────────
                Opcode::Integer => {
                    // Set register p2 to integer value p1.
                    self.set_reg_int(op.p2, i64::from(op.p1));
                    pc += 1;
                }

                Opcode::Int64 => {
                    let val = match &op.p4 {
                        P4::Int64(v) => *v,
                        _ => 0,
                    };
                    self.set_reg_int(op.p2, val);
                    pc += 1;
                }

                Opcode::Real => {
                    let val = match &op.p4 {
                        P4::Real(v) => *v,
                        _ => 0.0,
                    };
                    self.set_reg_fast(op.p2, SqliteValue::Float(val));
                    pc += 1;
                }

                Opcode::String8 => {
                    // Write text constant to register, reusing the
                    // register's existing String buffer when possible.
                    match &op.p4 {
                        P4::Str(s) => self.write_text_to_reg(op.p2, s),
                        _ => self.set_reg_fast(op.p2, SqliteValue::Text(SmallText::new(""))),
                    }
                    pc += 1;
                }

                Opcode::String => {
                    // p1 = length, p4 = string data. Same as String8 for us.
                    match &op.p4 {
                        P4::Str(s) => self.write_text_to_reg(op.p2, s),
                        _ => self.set_reg_fast(op.p2, SqliteValue::Text(SmallText::new(""))),
                    }
                    pc += 1;
                }

                Opcode::Null => {
                    // Set registers p2..p3 to NULL.  When p3 == 0 only p2 is
                    // set.  p3 is an absolute register number (matching C
                    // SQLite where cnt = p3 - p2).
                    let start = op.p2;
                    let end = if op.p3 > 0 { op.p3 } else { start };
                    for r in start..=end {
                        self.set_reg_fast(r, SqliteValue::Null);
                    }
                    pc += 1;
                }

                Opcode::SoftNull => {
                    self.set_reg_fast(op.p1, SqliteValue::Null);
                    pc += 1;
                }

                Opcode::Blob => {
                    // Write blob constant to register, reusing the
                    // register's existing Vec<u8> buffer when possible.
                    match &op.p4 {
                        P4::Blob(b) => self.write_blob_to_reg(op.p2, b),
                        _ => self.set_reg(op.p2, SqliteValue::Blob(Arc::from([] as [u8; 0]))),
                    }
                    pc += 1;
                }

                // ── Register Operations ─────────────────────────────────
                Opcode::Move => {
                    let count = usize::try_from(op.p3).unwrap_or(0);
                    self.move_reg_range(op.p1, op.p2, count);
                    pc += 1;
                }

                Opcode::Copy => {
                    // Copy registers p1..p1+p3 to p2..p2+p3 (deep copy).
                    // p3 = number of ADDITIONAL registers to copy (0 = just one).
                    self.copy_reg_range(op.p1, op.p2, op.p3);
                    pc += 1;
                }

                Opcode::SCopy => {
                    // Shallow copy register p1 to p2.
                    let val = self.clone_reg_materialized(op.p1);
                    self.set_reg_fast(op.p2, val);
                    pc += 1;
                }

                Opcode::IntCopy => {
                    let val = self.get_reg(op.p1).to_integer();
                    self.set_reg_int(op.p2, val);
                    pc += 1;
                }

                // ── Result Row ──────────────────────────────────────────
                Opcode::ResultRow => {
                    self.execute_result_row_hot(op, collect_vdbe_metrics, &mut row_handler)?;
                    pc += 1;
                }

                // ── Arithmetic ──────────────────────────────────────────
                Opcode::Add => {
                    // p3 = p2 + p1
                    let a = self.get_reg(op.p2);
                    let b = self.get_reg(op.p1);
                    let result = a.sql_add(b);
                    self.set_reg_fast(op.p3, result);
                    pc += 1;
                }

                Opcode::Subtract => {
                    // p3 = p2 - p1
                    let a = self.get_reg(op.p2);
                    let b = self.get_reg(op.p1);
                    let result = a.sql_sub(b);
                    self.set_reg_fast(op.p3, result);
                    pc += 1;
                }

                Opcode::Multiply => {
                    // p3 = p2 * p1
                    let a = self.get_reg(op.p2);
                    let b = self.get_reg(op.p1);
                    let result = a.sql_mul(b);
                    self.set_reg_fast(op.p3, result);
                    pc += 1;
                }

                Opcode::Divide => {
                    // p3 = p2 / p1
                    let divisor = self.get_reg(op.p1);
                    let dividend = self.get_reg(op.p2);
                    let result = sql_div(dividend, divisor);
                    self.set_reg_fast(op.p3, result);
                    pc += 1;
                }

                Opcode::Remainder => {
                    // p3 = p2 % p1
                    let divisor = self.get_reg(op.p1);
                    let dividend = self.get_reg(op.p2);
                    let result = sql_rem(dividend, divisor);
                    self.set_reg_fast(op.p3, result);
                    pc += 1;
                }

                // ── String Concatenation ────────────────────────────────
                Opcode::Concat => {
                    // Concatenate p1 and p2 into p3.
                    // SQLite concat order: result = b || a  (p2 first, then p1).
                    let a = self.get_reg(op.p1);
                    let b = self.get_reg(op.p2);
                    let result = if a.is_null() || b.is_null() {
                        SqliteValue::Null
                    } else {
                        // Fast path: when both are already Text, avoid the
                        // to_text() clone+allocation on the second operand.
                        match (b, a) {
                            (SqliteValue::Text(bs), SqliteValue::Text(as_)) => {
                                let mut s = String::with_capacity(bs.len() + as_.len());
                                s.push_str(bs);
                                s.push_str(as_);
                                SqliteValue::Text(s.into())
                            }
                            (SqliteValue::Text(bs), a_val) => {
                                let a_text = a_val.to_text();
                                let mut s = String::with_capacity(bs.len() + a_text.len());
                                s.push_str(bs);
                                s.push_str(&a_text);
                                SqliteValue::Text(s.into())
                            }
                            (b_val, SqliteValue::Text(as_)) => {
                                let mut s = b_val.to_text();
                                s.push_str(as_);
                                SqliteValue::Text(s.into())
                            }
                            _ => {
                                let mut s = b.to_text();
                                s.push_str(&a.to_text());
                                SqliteValue::Text(s.into())
                            }
                        }
                    };
                    self.set_reg_fast(op.p3, result);
                    pc += 1;
                }

                // ── Bitwise ─────────────────────────────────────────────
                Opcode::BitAnd => {
                    let a = self.get_reg(op.p1);
                    let b = self.get_reg(op.p2);
                    if a.is_null() || b.is_null() {
                        self.set_reg_fast(op.p3, SqliteValue::Null);
                    } else {
                        self.set_reg_int(op.p3, a.to_integer() & b.to_integer());
                    }
                    pc += 1;
                }

                Opcode::BitOr => {
                    let a = self.get_reg(op.p1);
                    let b = self.get_reg(op.p2);
                    if a.is_null() || b.is_null() {
                        self.set_reg_fast(op.p3, SqliteValue::Null);
                    } else {
                        self.set_reg_int(op.p3, a.to_integer() | b.to_integer());
                    }
                    pc += 1;
                }

                Opcode::ShiftLeft => {
                    let a = self.get_reg(op.p1);
                    let b = self.get_reg(op.p2);
                    if a.is_null() || b.is_null() {
                        self.set_reg_fast(op.p3, SqliteValue::Null);
                    } else {
                        let result = sql_shift_left(b.to_integer(), a.to_integer());
                        self.set_reg_fast(op.p3, result);
                    }
                    pc += 1;
                }

                Opcode::ShiftRight => {
                    let a = self.get_reg(op.p1);
                    let b = self.get_reg(op.p2);
                    if a.is_null() || b.is_null() {
                        self.set_reg_fast(op.p3, SqliteValue::Null);
                    } else {
                        let result = sql_shift_right(b.to_integer(), a.to_integer());
                        self.set_reg_fast(op.p3, result);
                    }
                    pc += 1;
                }

                Opcode::BitNot => {
                    // p2 = ~p1
                    let a = self.get_reg(op.p1);
                    if a.is_null() {
                        self.set_reg_fast(op.p2, SqliteValue::Null);
                    } else {
                        self.set_reg_int(op.p2, !a.to_integer());
                    }
                    pc += 1;
                }

                // ── Type Conversion ─────────────────────────────────────
                Opcode::AddImm => {
                    // Add integer p2 to register p1.
                    let val = self
                        .get_reg(op.p1)
                        .to_integer()
                        .wrapping_add(i64::from(op.p2));
                    self.set_reg_int(op.p1, val);
                    pc += 1;
                }

                Opcode::Cast => {
                    // Cast register p1 to type indicated by p2.
                    let val = self.take_reg(op.p1);
                    if collect_vdbe_metrics {
                        let casted = sql_cast(val.clone(), op.p2);
                        record_type_coercion(&val, &casted);
                        self.set_reg_fast(op.p1, casted);
                    } else {
                        let casted = sql_cast(val, op.p2);
                        self.set_reg_fast(op.p1, casted);
                    }
                    pc += 1;
                }

                Opcode::MustBeInt => {
                    let val = self.take_reg(op.p1);
                    let coerced = val.apply_affinity(fsqlite_types::TypeAffinity::Integer);
                    let is_int = coerced.as_integer().is_some();
                    self.set_reg_fast(op.p1, coerced);
                    if is_int {
                        pc += 1;
                    } else {
                        if op.p2 > 0 {
                            pc = op.p2 as usize;
                            continue;
                        }
                        return Err(FrankenError::TypeMismatch {
                            expected: "integer".to_owned(),
                            actual: self.get_reg(op.p1).typeof_str().to_owned(),
                        });
                    }
                }

                #[allow(clippy::cast_precision_loss)]
                Opcode::RealAffinity => {
                    if let SqliteValue::Integer(i) = self.get_reg(op.p1) {
                        let i_val = *i;
                        let f = i_val as f64;
                        if collect_vdbe_metrics {
                            record_type_coercion(
                                &SqliteValue::Integer(i_val),
                                &SqliteValue::Float(f),
                            );
                        }
                        self.set_reg_fast(op.p1, SqliteValue::Float(f));
                    }
                    pc += 1;
                }

                // ── Comparison Jumps ────────────────────────────────────
                Opcode::Eq | Opcode::Ne | Opcode::Lt | Opcode::Le | Opcode::Gt | Opcode::Ge => {
                    let lhs = self.get_reg(op.p3);
                    let rhs = self.get_reg(op.p1);
                    let store_p2 = (op.p5 & 0x20) != 0; // SQLITE_STOREP2

                    if lhs.is_null() || rhs.is_null() {
                        let null_eq = (op.p5 & 0x80) != 0;
                        if null_eq {
                            // IS / IS NOT semantics: NULL == NULL is true.
                            let both_null = lhs.is_null() && rhs.is_null();
                            let should_jump = match op.opcode {
                                Opcode::Eq => both_null,
                                Opcode::Ne => !both_null,
                                _ => false,
                            };
                            if store_p2 {
                                self.set_reg_int(op.p2, i64::from(should_jump));
                                pc += 1;
                            } else if should_jump {
                                pc = op.p2 as usize;
                            } else {
                                pc += 1;
                            }
                        } else if store_p2 {
                            // STOREP2 with NULL: store NULL in P2.
                            self.set_reg_fast(op.p2, SqliteValue::Null);
                            pc += 1;
                        } else {
                            // JUMPIFNULL (0x10): jump to P2 when either is NULL.
                            let jump_if_null = (op.p5 & 0x10) != 0;
                            if jump_if_null {
                                pc = op.p2 as usize;
                            } else {
                                pc += 1;
                            }
                        }
                    } else {
                        // Fast path: Integer vs Integer with no collation.
                        // This is the dominant comparison case (rowid checks,
                        // WHERE filters on int columns, index probes). Avoids
                        // coerce_for_comparison Cow allocation and collation lookup.
                        let cmp = if let (SqliteValue::Integer(a), SqliteValue::Integer(b)) =
                            (lhs, rhs)
                        {
                            if !matches!(op.p4, P4::Collation(_)) {
                                Some(a.cmp(b))
                            } else {
                                lhs.partial_cmp(rhs)
                            }
                        } else {
                            // General path: affinity coercion + collation.
                            let (cmp_lhs, cmp_rhs) = coerce_for_comparison(lhs, rhs, op.p5);
                            if let P4::Collation(ref coll_name) = op.p4 {
                                let coll = self.lock_collation();
                                collate_compare(&cmp_lhs, &cmp_rhs, coll_name, &coll)
                            } else {
                                cmp_lhs.partial_cmp(&cmp_rhs)
                            }
                        };
                        let should_jump = matches!(
                            (op.opcode, cmp),
                            (Opcode::Eq, Some(std::cmp::Ordering::Equal))
                                | (Opcode::Lt, Some(std::cmp::Ordering::Less))
                                | (
                                    Opcode::Le,
                                    Some(std::cmp::Ordering::Less | std::cmp::Ordering::Equal)
                                )
                                | (Opcode::Gt, Some(std::cmp::Ordering::Greater))
                                | (
                                    Opcode::Ge,
                                    Some(std::cmp::Ordering::Greater | std::cmp::Ordering::Equal)
                                )
                        ) || matches!(
                            (op.opcode, cmp),
                            (Opcode::Ne, Some(ord)) if ord != std::cmp::Ordering::Equal
                        );

                        if store_p2 {
                            if cmp.is_none() {
                                // Indeterminate comparison (e.g., NaN): store NULL, not 0.
                                self.set_reg_fast(op.p2, SqliteValue::Null);
                            } else {
                                self.set_reg_int(op.p2, i64::from(should_jump));
                            }
                            pc += 1;
                        } else if should_jump {
                            pc = op.p2 as usize;
                        } else {
                            pc += 1;
                        }
                    }
                }

                // ── Boolean Logic ───────────────────────────────────────
                Opcode::And => {
                    // Three-valued AND: p3 = p1 AND p2
                    let a = self.get_reg(op.p1);
                    let b = self.get_reg(op.p2);
                    let result = sql_and(a, b);
                    self.set_reg_fast(op.p3, result);
                    pc += 1;
                }

                Opcode::Or => {
                    // Three-valued OR: p3 = p1 OR p2
                    let a = self.get_reg(op.p1);
                    let b = self.get_reg(op.p2);
                    let result = sql_or(a, b);
                    self.set_reg_fast(op.p3, result);
                    pc += 1;
                }

                Opcode::Not => {
                    // p2 = NOT p1
                    let a = self.get_reg(op.p1);
                    if a.is_null() {
                        self.set_reg_fast(op.p2, SqliteValue::Null);
                    } else {
                        self.set_reg_int(op.p2, i64::from(!vdbe_real_is_truthy(a)));
                    }
                    pc += 1;
                }

                // ── Conditional Jumps ───────────────────────────────────
                Opcode::If => {
                    // Jump to p2 if p1 is true.
                    // If p1 is NULL, jump iff p3 != 0 (SQLite semantics).
                    let val = self.get_reg(op.p1);
                    let should_jump = if val.is_null() {
                        op.p3 != 0
                    } else {
                        vdbe_real_is_truthy(val)
                    };
                    if should_jump {
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                Opcode::IfNot => {
                    // Jump to p2 if p1 is false (zero).
                    // If p1 is NULL, jump iff p3 != 0 (SQLite semantics).
                    let val = self.get_reg(op.p1);
                    let should_jump = if val.is_null() {
                        op.p3 != 0
                    } else {
                        !vdbe_real_is_truthy(val)
                    };
                    if should_jump {
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                Opcode::IsNull => {
                    // Jump to p2 if p1 is NULL.
                    if self.get_reg(op.p1).is_null() {
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                Opcode::NotNull => {
                    // Jump to p2 if p1 is NOT NULL.
                    if self.get_reg(op.p1).is_null() {
                        pc += 1;
                    } else {
                        pc = op.p2 as usize;
                    }
                }

                Opcode::Once => {
                    // Fall through on first execution (run the body), jump to
                    // p2 on subsequent executions (skip the body).  This
                    // matches C SQLite's OP_Once semantics.
                    let word = pc / 64;
                    let bit = 1u64 << (pc % 64);
                    if once_bits[word] & bit != 0 {
                        // Already fired — skip the body.
                        pc = op.p2 as usize;
                    } else {
                        // First time — mark as fired, fall through.
                        once_bits[word] |= bit;
                        pc += 1;
                    }
                }

                // ── Gosub / Return ──────────────────────────────────────
                Opcode::Gosub => {
                    // Store return address in p1, jump to p2.
                    let return_addr = (pc + 1) as i32;
                    self.set_reg(op.p1, SqliteValue::Integer(i64::from(return_addr)));
                    pc = op.p2 as usize;
                }

                Opcode::Return => {
                    // Jump to address stored in p1.
                    let addr = self.get_reg(op.p1).to_integer();
                    if addr < 0 || addr as usize >= ops.len() {
                        return Err(FrankenError::Internal(format!(
                            "Return address {} out of bounds",
                            addr
                        )));
                    }
                    pc = addr as usize;
                }

                // ── Transaction / AutoCommit / TableLock ────────────────
                //
                // In C SQLite, Transaction opens a read or write transaction
                // via the pager, AutoCommit commits/rollbacks, and TableLock
                // acquires table-level schema locks. In FrankenSQLite, the
                // Connection layer manages transaction lifecycle externally
                // (wrapping VDBE execution in BEGIN/COMMIT), so these opcodes
                // validate intent rather than performing the lifecycle step.
                //
                // Transaction: p1=db number, p2=1 for write txn, p3=schema version
                // AutoCommit: p1=1 for rollback/0 for commit, p2=auto-commit flag
                // TableLock: p1=db, p2=root page, p3=1 for write, p4=table name
                Opcode::Transaction => {
                    let _db_number = op.p1;
                    let is_write = op.p2 != 0;
                    if is_write && self.txn_page_io.is_some() {
                        // Record that this program requires write access.
                        // The Connection layer should have already opened a
                        // write transaction; if not, downstream write opcodes
                        // (Insert, Delete) will fail with appropriate errors.
                        tracing::trace!(
                            target: "fsqlite.vdbe",
                            opcode = "Transaction",
                            db = _db_number,
                            write = is_write,
                            "write transaction intent acknowledged"
                        );
                    }
                    pc += 1;
                }
                Opcode::AutoCommit => {
                    // AutoCommit is handled by the Connection layer after
                    // VDBE execution completes. The opcode is a no-op here
                    // because the Connection checks the final program counter
                    // and transaction state to determine commit/rollback.
                    pc += 1;
                }
                Opcode::TableLock => {
                    // Table-level locks are subsumed by MVCC page-level
                    // locking. Schema stability is guaranteed by the schema
                    // cookie check (ReadCookie/SetCookie opcodes). No
                    // additional locking is needed here.
                    pc += 1;
                }

                // ── Cookie operations (bd-3mmj) ────────────────────────
                //
                // ReadCookie: P1=db, P2=dest register, P3=cookie number
                //   cookie 1 = schema_cookie (offset 40 in header)
                // SetCookie: P1=db, P2=cookie number, P3=new value
                Opcode::ReadCookie => {
                    let dest_reg = op.p2;
                    let cookie_num = op.p3;
                    let value = match cookie_num {
                        // Cookie 1 = BTREE_SCHEMA_VERSION (schema cookie)
                        1 => i64::from(self.schema_cookie),
                        // Other cookies return 0 for now.
                        _ => 0,
                    };
                    self.set_reg(dest_reg, SqliteValue::Integer(value));
                    pc += 1;
                }
                Opcode::SetCookie => {
                    let cookie_num = op.p2;
                    let new_value = op.p3;
                    if cookie_num == 1 {
                        #[allow(clippy::cast_sign_loss)]
                        {
                            self.schema_cookie = new_value as u32;
                        }
                    }
                    // Other cookie numbers are silently ignored for now.
                    pc += 1;
                }

                // ── Cursor operations ─────────────────────────────────
                Opcode::OpenRead => {
                    // bd-1xrs: StorageCursor is now the ONLY cursor path.
                    // No MemCursor fallback - open_storage_cursor must succeed.
                    if op.p3 > 1 {
                        // Bead bd-bldc5.1.7 (T7): ATTACH DATABASE support.
                        // p3=0 is main db, p3=1 is temp db. p3>1 means an
                        // ATTACHed database, which requires multi-database pager
                        // management (Connection maintains a map of name→pager,
                        // VDBE looks up pager by db number). ATTACH is used for
                        // cross-database queries, migration, and backup.
                        return Err(FrankenError::NotImplemented(format!(
                            "ATTACH DATABASE (db number {}) not yet supported — \
                                 use the main database or :memory:",
                            op.p3
                        )));
                    }
                    let cursor_id = op.p1;
                    let root_page = op.p2;
                    self.pending_next_after_delete.remove(&cursor_id);
                    if !self.open_storage_cursor(cursor_id, root_page, false) {
                        return Err(FrankenError::Internal(format!(
                            "OpenRead failed: could not open storage cursor on root page {root_page}"
                        )));
                    }
                    self.cursor_root_pages.insert(cursor_id, root_page);
                    self.cursors.remove(&cursor_id);
                    pc += 1;
                }
                Opcode::OpenWrite => {
                    // bd-1xrs: StorageCursor is now the ONLY cursor path.
                    // No MemCursor fallback - open_storage_cursor must succeed.
                    if op.p3 > 1 {
                        // Bead bd-bldc5.1.7 (T7): ATTACH DATABASE support.
                        // p3=0 is main db, p3=1 is temp db. p3>1 means an
                        // ATTACHed database, which requires multi-database pager
                        // management (Connection maintains a map of name→pager,
                        // VDBE looks up pager by db number). ATTACH is used for
                        // cross-database queries, migration, and backup.
                        return Err(FrankenError::NotImplemented(format!(
                            "ATTACH DATABASE (db number {}) not yet supported — \
                                 use the main database or :memory:",
                            op.p3
                        )));
                    }
                    let cursor_id = op.p1;
                    let root_page = op.p2;
                    self.pending_next_after_delete.remove(&cursor_id);
                    if !self.open_storage_cursor(cursor_id, root_page, true) {
                        return Err(FrankenError::Internal(format!(
                            "OpenWrite failed: could not open storage cursor on root page {root_page}"
                        )));
                    }
                    self.cursor_root_pages.insert(cursor_id, root_page);
                    self.cursors.remove(&cursor_id);
                    pc += 1;
                }

                Opcode::OpenEphemeral => {
                    // Ephemeral table: create an in-memory table on-the-fly.
                    let cursor_id = op.p1;
                    self.pending_next_after_delete.remove(&cursor_id);
                    let num_cols = op.p2.max(1);
                    if let Some(db) = self.db.as_mut() {
                        let root_page = db.create_table(num_cols as usize);
                        self.storage_cursors.remove(&cursor_id);
                        self.cursors
                            .insert(cursor_id, MemCursor::new(root_page, true));
                    }
                    pc += 1;
                }

                Opcode::OpenAutoindex => {
                    // Autoindex: create an ephemeral INDEX B-tree.
                    // Unlike OpenEphemeral (table B-tree), autoindexes use
                    // index B-tree semantics (no rowid, key-only cells).
                    // We create a StorageCursor backed by MemPageStore with
                    // is_table=false so IdxInsert/IdxGE etc. work correctly.
                    let autoindex_page_size = self.page_size.get();
                    let cursor_id = op.p1;
                    self.pending_next_after_delete.remove(&cursor_id);
                    let root_pgno = if let Some(db) = self.db.as_mut() {
                        let rp = db.allocate_root_page();
                        PageNumber::new(rp as u32)
                    } else {
                        PageNumber::new(2)
                    };
                    if let Some(root_pgno) = root_pgno {
                        let store = MemPageStore::with_empty_index(root_pgno, autoindex_page_size);
                        let cx = self.derive_execution_cx();
                        let mut cursor =
                            BtCursor::new(store, root_pgno, autoindex_page_size, false);
                        let autoindex_collations = extract_collation_names_owned(&op.p4);
                        if !autoindex_collations.is_empty() {
                            cursor.set_index_collation_context(
                                autoindex_collations,
                                Arc::clone(&self.collation_registry),
                            );
                        }
                        self.cursor_root_pages
                            .insert(cursor_id, root_pgno.get() as i32);
                        self.cursors.remove(&cursor_id);
                        self.storage_cursors.insert(
                            cursor_id,
                            StorageCursor {
                                cursor: CursorBackend::Mem(cursor),
                                cx,
                                writable: true,
                                root_page: root_pgno.get() as i32,
                                rowid_mode: RowIdMode::Normal,
                                autoincrement_high_water: 0,
                                last_alloc_rowid: 0,
                                payload_buf: Vec::new(),
                                target_vals_buf: Vec::new(),
                                cur_vals_buf: Vec::new(),
                                row_decode: RowDecodeScratch::default(),
                                last_position_stamp: None,
                                last_successful_insert_rowid: None,
                                last_rightmost_unique_index_prefix: None,
                                last_rightmost_unique_index_position: None,
                                cached_rowid: None,
                                payload_includes_rowid_alias: None,
                                ipk_col_idx: None,
                                table_column_count: None,
                                first_not_null_non_ipk_col: None,
                            },
                        );
                    }
                    pc += 1;
                }

                Opcode::OpenPseudo => {
                    let cursor_id = op.p1;
                    self.pending_next_after_delete.remove(&cursor_id);
                    self.storage_cursors.remove(&cursor_id);
                    self.cursors.insert(cursor_id, MemCursor::new_pseudo(op.p2));
                    pc += 1;
                }

                Opcode::OpenDup | Opcode::ReopenIdx => {
                    // Reopen: reuse existing cursor configuration.
                    pc += 1;
                }

                Opcode::SorterOpen => {
                    let cursor_id = op.p1;
                    self.pending_next_after_delete.remove(&cursor_id);
                    let key_columns = usize::try_from(op.p2.max(1)).unwrap_or(1);
                    let top_n_limit = usize::try_from(op.p3).ok().filter(|limit| *limit > 0);
                    // P4::Str format: ORDER_CHARS or ORDER_CHARS|COLL1,COLL2,...
                    // where ORDER_CHARS are '+'/'-' per key column,
                    // and COLL values are collation names (empty = BINARY).
                    let (order_str, collation_str) = match &op.p4 {
                        P4::Str(s) => {
                            if let Some((orders, colls)) = s.split_once('|') {
                                (orders.to_owned(), Some(colls.to_owned()))
                            } else {
                                (s.clone(), None)
                            }
                        }
                        _ => (String::new(), None),
                    };
                    let sort_key_orders: Vec<SortKeyOrder> = order_str
                        .chars()
                        .take(key_columns)
                        .map(|ch| match ch {
                            '-' => SortKeyOrder::Desc,
                            '>' => SortKeyOrder::AscNullsLast,
                            '<' => SortKeyOrder::DescNullsFirst,
                            _ => SortKeyOrder::Asc,
                        })
                        .collect();
                    let collations: Vec<Option<String>> = if let Some(cs) = collation_str {
                        cs.split(',')
                            .take(key_columns)
                            .map(|c| {
                                if c.is_empty() {
                                    None
                                } else {
                                    Some(c.to_owned())
                                }
                            })
                            .collect()
                    } else {
                        Vec::new()
                    };
                    self.sorters.insert(
                        cursor_id,
                        SorterCursor::with_collation_registry(
                            key_columns,
                            sort_key_orders,
                            collations,
                            Arc::clone(&self.collation_registry),
                            top_n_limit,
                        ),
                    );
                    // A cursor id cannot be both table and sorter cursor.
                    self.cursors.remove(&cursor_id);
                    self.storage_cursors.remove(&cursor_id);
                    pc += 1;
                }

                Opcode::Close => {
                    self.cursors.remove(&op.p1);
                    self.storage_cursors.remove(&op.p1);
                    self.sorters.remove(&op.p1);
                    if let Some(cold_state) = self.cold_state_mut() {
                        cold_state.vtab_cursors.remove(&op.p1);
                    }
                    self.pending_next_after_delete.remove(&op.p1);
                    pc += 1;
                }

                Opcode::ColumnsUsed => {
                    pc += 1;
                }

                Opcode::Rewind | Opcode::Sort | Opcode::SorterSort => {
                    // Position cursor at the first row. Jump to p2 if empty.
                    let cursor_id = op.p1;
                    // Rewind repositions the cursor, so clear any pending delete state.
                    self.pending_next_after_delete.remove(&cursor_id);
                    let is_empty = if let Some(sorter) = self.sorters.get_mut(&cursor_id) {
                        if matches!(op.opcode, Opcode::Sort | Opcode::SorterSort) {
                            sorter.sort()?;
                            // Flush per-sorter metrics to global counters.
                            let rows = sorter.rows_sorted_total;
                            let spill_pages = sorter.spill_pages_total;
                            let merge_runs = sorter.spill_runs.len() as u64;
                            if collect_vdbe_metrics {
                                FSQLITE_SORT_ROWS_TOTAL.fetch_add(rows, AtomicOrdering::Relaxed);
                                FSQLITE_SORT_SPILL_PAGES_TOTAL
                                    .fetch_add(spill_pages, AtomicOrdering::Relaxed);
                            }
                            sorter.rows_sorted_total = 0;
                            sorter.spill_pages_total = 0;
                            // Tracing span for sort observability.
                            let _span = tracing::debug_span!(
                                "sort",
                                rows_sorted = rows,
                                spill_pages = spill_pages,
                                merge_runs = merge_runs,
                            )
                            .entered();
                            tracing::debug!(
                                rows_sorted = rows,
                                spill_pages = spill_pages,
                                merge_runs = merge_runs,
                                "sort completed"
                            );
                        }
                        if sorter.rows.is_empty() {
                            sorter.position = None;
                            true
                        } else {
                            sorter.position = Some(0);
                            false
                        }
                    } else if let Some(cursor) = self.cursors.get_mut(&cursor_id) {
                        if cursor.is_pseudo {
                            cursor.pseudo_row.is_none()
                        } else if let Some(db) = self.db.as_ref() {
                            if let Some(table) = db.get_table(cursor.root_page) {
                                if table.rows.is_empty() {
                                    true
                                } else {
                                    cursor.position = Some(0);
                                    false
                                }
                            } else {
                                true
                            }
                        } else {
                            true
                        }
                    } else if let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) {
                        !cursor.cursor.first(&cursor.cx)?
                    } else {
                        true
                    };
                    if is_empty {
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                Opcode::Last => {
                    // Position cursor at the last row. Jump to p2 if empty.
                    let cursor_id = op.p1;
                    // Last repositions the cursor, so clear any pending delete state.
                    self.pending_next_after_delete.remove(&cursor_id);
                    let is_empty = if let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) {
                        !cursor.cursor.last(&cursor.cx)?
                    } else if let Some(cursor) = self.cursors.get_mut(&cursor_id) {
                        if cursor.is_pseudo {
                            cursor.pseudo_row.is_none()
                        } else if let Some(db) = self.db.as_ref() {
                            if let Some(table) = db.get_table(cursor.root_page) {
                                if table.rows.is_empty() {
                                    true
                                } else {
                                    cursor.position = Some(table.rows.len() - 1);
                                    false
                                }
                            } else {
                                true
                            }
                        } else {
                            true
                        }
                    } else {
                        true
                    };
                    if is_empty {
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                Opcode::Next | Opcode::SorterNext => {
                    // Advance cursor to the next row. Jump to p2 if more rows.
                    let cursor_id = op.p1;
                    let has_next = if !self.pending_next_after_delete.is_empty()
                        && self.pending_next_after_delete.remove(&cursor_id)
                    {
                        if let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) {
                            !cursor.cursor.eof()
                        } else if let Some(cursor) = self.cursors.get_mut(&cursor_id) {
                            if cursor.is_pseudo {
                                false
                            } else if let Some(pos) = cursor.position {
                                if let Some(table) = self
                                    .db
                                    .as_ref()
                                    .and_then(|db| db.get_table(cursor.root_page))
                                {
                                    if pos < table.rows.len() {
                                        true
                                    } else {
                                        cursor.position = None;
                                        false
                                    }
                                } else {
                                    false
                                }
                            } else {
                                false
                            }
                        } else {
                            false
                        }
                    } else if let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) {
                        cursor.cursor.next(&cursor.cx)?
                    } else if let Some(sorter) = self.sorters.get_mut(&cursor_id) {
                        if let Some(pos) = sorter.position {
                            let next = pos + 1;
                            if next < sorter.rows.len() {
                                sorter.position = Some(next);
                                true
                            } else {
                                sorter.position = None;
                                false
                            }
                        } else {
                            false
                        }
                    } else if let Some(cursor) = self.cursors.get_mut(&cursor_id) {
                        if cursor.is_pseudo {
                            false
                        } else if let Some(db) = self.db.as_ref() {
                            if let Some(table) = db.get_table(cursor.root_page) {
                                if let Some(pos) = cursor.position {
                                    let next = pos + 1;
                                    if next < table.rows.len() {
                                        cursor.position = Some(next);
                                        true
                                    } else {
                                        cursor.position = None;
                                        false
                                    }
                                } else {
                                    false
                                }
                            } else {
                                false
                            }
                        } else {
                            false
                        }
                    } else {
                        false
                    };
                    if has_next {
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                Opcode::Prev => {
                    // Move cursor backward. Jump to p2 if more rows.
                    let cursor_id = op.p1;
                    // Prev repositions the cursor, so clear any pending
                    // delete/next state before evaluating movement.
                    if !self.pending_next_after_delete.is_empty() {
                        self.pending_next_after_delete.remove(&cursor_id);
                    }
                    let has_prev = if let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) {
                        cursor.cursor.prev(&cursor.cx)?
                    } else if let Some(cursor) = self.cursors.get_mut(&cursor_id) {
                        if let Some(pos) = cursor.position {
                            if pos > 0 {
                                cursor.position = Some(pos - 1);
                                true
                            } else {
                                cursor.position = None;
                                false
                            }
                        } else {
                            false
                        }
                    } else {
                        false
                    };
                    if has_prev {
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                Opcode::Column => {
                    // Read column p2 from cursor p1 into register p3.
                    //
                    // Fast path for storage cursors: write from the lazy
                    // column cache directly into the target register,
                    // reusing existing Text/Blob buffer capacity.  This
                    // eliminates the alloc+dealloc cycle that the
                    // cursor_column→clone→set_reg path requires for
                    // every text/blob column on every row.
                    let cursor_id = op.p1;
                    let col_idx = op.p2 as usize;
                    let target = op.p3;
                    if !self.column_to_reg_direct(cursor_id, col_idx, target)? {
                        let val = self.cursor_column(cursor_id, col_idx)?;
                        self.set_reg_fast(target, val);
                    }
                    pc += 1;
                }

                Opcode::Rowid => {
                    // Get rowid from cursor p1 into register p2.
                    let cursor_id = op.p1;
                    let target = op.p2;
                    let val = self.cursor_rowid(cursor_id)?;
                    self.set_reg_fast(target, val);
                    pc += 1;
                }

                Opcode::RowData => {
                    // Store raw row data as a blob in register p2.
                    let cursor_id = op.p1;
                    let target = op.p2;
                    if let Some(cursor) = self.storage_cursors.get(&cursor_id) {
                        if cursor.cursor.eof() {
                            self.set_reg_fast(target, SqliteValue::Null);
                        } else {
                            let payload = cursor.cursor.payload(&cursor.cx)?;
                            self.set_reg_fast(target, SqliteValue::Blob(payload.into()));
                        }
                    } else if let Some(cursor) = self.cursors.get(&cursor_id) {
                        if cursor.is_pseudo {
                            if let Some(reg) = cursor.pseudo_reg {
                                let blob = self.get_reg(reg).clone();
                                self.set_reg_fast(target, blob);
                            } else {
                                self.set_reg_fast(target, SqliteValue::Null);
                            }
                        } else {
                            self.set_reg_fast(target, SqliteValue::Null);
                        }
                    } else {
                        self.set_reg_fast(target, SqliteValue::Null);
                    }
                    pc += 1;
                }

                Opcode::NullRow => {
                    // Set cursor p1 to a null row. Subsequent Column/Rowid
                    // reads will return NULL (storage cursor via eof(),
                    // mem cursor via position=None, vtab cursor via
                    // synthetic null-row state).
                    if let Some(cursor) = self.storage_cursors.get_mut(&op.p1) {
                        // Move storage cursor past the last entry so eof()
                        // returns true for subsequent Column/Rowid reads.
                        cursor.cursor.clear_position();
                    }
                    if let Some(cursor) = self.cursors.get_mut(&op.p1) {
                        cursor.position = None;
                    }
                    if let Some(state) = self
                        .cold_state_mut()
                        .and_then(|cold_state| cold_state.vtab_cursors.get_mut(&op.p1))
                    {
                        state.null_row = true;
                    }
                    pc += 1;
                }

                Opcode::Offset => {
                    self.set_reg_fast(op.p3, SqliteValue::Null);
                    pc += 1;
                }

                // ── Seek operations (in-memory) ─────────────────────────
                Opcode::SeekRowid => {
                    // Seek cursor p1 to the row with rowid in register p3.
                    // If not found, jump to p2.  NULL key → not found.
                    let cursor_id = op.p1;
                    // Seek repositions the cursor, so clear any pending delete state.
                    self.pending_next_after_delete.remove(&cursor_id);
                    let key = self.get_reg(op.p3);
                    if key.is_null() {
                        pc = op.p2 as usize;
                        continue;
                    }
                    let rowid_val = key.to_integer();
                    let use_rowset_advance = pc > 0
                        && ops.get(pc - 1).is_some_and(|prev| {
                            prev.opcode == Opcode::RowSetRead && prev.p3 == op.p3
                        });
                    let found = if let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) {
                        let seek_result = if use_rowset_advance {
                            cursor.cursor.table_advance_to(&cursor.cx, rowid_val)?
                        } else {
                            cursor.cursor.table_move_to(&cursor.cx, rowid_val)?
                        };
                        seek_result.is_found()
                    } else if let Some(cursor) = self.cursors.get_mut(&cursor_id) {
                        if let Some(db) = self.db.as_ref() {
                            if let Some(table) = db.get_table(cursor.root_page) {
                                if let Some(idx) = table.find_by_rowid(rowid_val) {
                                    cursor.position = Some(idx);
                                    true
                                } else {
                                    false
                                }
                            } else {
                                false
                            }
                        } else {
                            false
                        }
                    } else {
                        false
                    };
                    if found {
                        pc += 1;
                    } else {
                        pc = op.p2 as usize;
                    }
                }

                Opcode::SeekGE | Opcode::SeekGT | Opcode::SeekLE | Opcode::SeekLT => {
                    // bd-3pti: Route seek opcodes through B-tree cursor.
                    //
                    // Seek operations position the cursor relative to a key:
                    // - SeekGE: Position at first row >= key
                    // - SeekGT: Position at first row > key
                    // - SeekLE: Position at last row <= key
                    // - SeekLT: Position at last row < key
                    //
                    // Jump to p2 if no matching row exists.  NULL key → not found.
                    let cursor_id = op.p1;
                    // Seek repositions the cursor, so clear any pending delete state.
                    self.pending_next_after_delete.remove(&cursor_id);
                    let key_val = self.clone_reg_materialized(op.p3);
                    if key_val.is_null() {
                        pc = op.p2 as usize;
                        continue;
                    }

                    // Dispatch based on cursor type (table vs index), NOT on
                    // key value type. Using the key type was incorrect: an
                    // index cursor receiving an integer key would wrongly call
                    // table_move_to, triggering "table leaf cell has no rowid"
                    // on index pages. (Fixes br#138-140, #144, #145.)
                    let coll_arc = Arc::clone(&self.collation_registry);
                    let seek_idx_desc_flags = self.index_desc_flags_for_cursor(cursor_id);
                    let seek_idx_collations = self.index_collations_for_cursor(cursor_id);
                    let uses_collated_seek = seek_idx_collations.iter().any(|collation| {
                        collation
                            .as_deref()
                            .is_some_and(|name| !name.eq_ignore_ascii_case("BINARY"))
                    });
                    let collated_seek_registry = uses_collated_seek.then(|| {
                        self.collation_registry
                            .lock()
                            .unwrap_or_else(|err| err.into_inner())
                            .clone()
                    });
                    let found = if let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) {
                        if cursor.cursor.is_table_btree() {
                            // Table seek: key is a rowid (integer).
                            let key = key_val.to_integer();
                            let seek_result = cursor.cursor.table_move_to(&cursor.cx, key)?;

                            match op.opcode {
                                Opcode::SeekGE => {
                                    // Need first row >= key.
                                    // table_move_to already positions at key (Found) or
                                    // at next larger (NotFound). Check for EOF.
                                    !cursor.cursor.eof()
                                }
                                Opcode::SeekGT => {
                                    // Need first row > key.
                                    // If Found (at exact key), advance past it.
                                    // If NotFound, already past key.
                                    if seek_result.is_found() {
                                        cursor.cursor.next(&cursor.cx)?
                                    } else {
                                        !cursor.cursor.eof()
                                    }
                                }
                                Opcode::SeekLE => {
                                    // Need last row <= key.
                                    // If Found, we're at the exact key - done.
                                    // If NotFound, cursor is at entry > key, so prev().
                                    if seek_result.is_found() {
                                        true
                                    } else if cursor.cursor.eof() {
                                        // All entries < key, position at last.
                                        cursor.cursor.last(&cursor.cx)?
                                    } else {
                                        // Cursor at entry > key, move to previous.
                                        cursor.cursor.prev(&cursor.cx)?
                                    }
                                }
                                Opcode::SeekLT => {
                                    // Need last row < key.
                                    // Cursor is either at key (Found) or past key (NotFound).
                                    // Either way, we need to go to the previous entry.
                                    if cursor.cursor.eof() {
                                        // All entries < key, position at last.
                                        cursor.cursor.last(&cursor.cx)?
                                    } else {
                                        // Go to previous entry (which will be < key).
                                        cursor.cursor.prev(&cursor.cx)?
                                    }
                                }
                                _ => unreachable!(),
                            }
                        } else {
                            // Index seek: key is a packed record blob.
                            let key_bytes = record_blob_bytes(&key_val);
                            if uses_collated_seek {
                                let Some(collation_registry) = collated_seek_registry.as_ref()
                                else {
                                    return Err(FrankenError::internal(
                                        "Seek*: missing collation registry for collated index seek",
                                    ));
                                };
                                storage_cursor_seek_collated(
                                    cursor_id,
                                    cursor,
                                    key_bytes,
                                    op.opcode,
                                    &seek_idx_desc_flags,
                                    &seek_idx_collations,
                                    collation_registry,
                                )?
                            } else {
                                let seek_result =
                                    cursor.cursor.index_move_to(&cursor.cx, key_bytes)?;

                                match op.opcode {
                                    Opcode::SeekGE => !cursor.cursor.eof(),
                                    Opcode::SeekGT => {
                                        if seek_result.is_found() {
                                            cursor.cursor.next(&cursor.cx)?;
                                        }
                                        if !cursor.cursor.eof() {
                                            decode_storage_cursor_target_index_record_strict(
                                                cursor,
                                                key_bytes,
                                                "SeekGT: malformed seek key record",
                                            )?;
                                            // Use pre-fetched index collations for
                                            // COLLATE NOCASE and similar — &[] would
                                            // use binary comparison, breaking
                                            // case-insensitive indexes.
                                            let idx_collations = seek_idx_collations.as_slice();
                                            loop {
                                                if cursor.cursor.eof() {
                                                    break;
                                                }
                                                decode_storage_cursor_current_index_record_strict(
                                                    cursor_id,
                                                    cursor,
                                                    "SeekGT: malformed cursor record",
                                                )?;
                                                let coll = coll_arc
                                                    .lock()
                                                    .unwrap_or_else(|e| e.into_inner());
                                                let cmp = compare_sorter_keys(
                                                    &cursor.cur_vals_buf,
                                                    &cursor.target_vals_buf,
                                                    cursor.target_vals_buf.len(),
                                                    idx_collations,
                                                    &coll,
                                                );
                                                drop(coll);
                                                if cmp == std::cmp::Ordering::Equal {
                                                    cursor.cursor.next(&cursor.cx)?;
                                                } else {
                                                    break;
                                                }
                                            }
                                        }
                                        !cursor.cursor.eof()
                                    }
                                    Opcode::SeekLE => {
                                        if !cursor.cursor.eof() {
                                            decode_storage_cursor_target_index_record_strict(
                                                cursor,
                                                key_bytes,
                                                "SeekLE: malformed seek key record",
                                            )?;
                                            let idx_collations = seek_idx_collations.as_slice();
                                            loop {
                                                if cursor.cursor.eof() {
                                                    break;
                                                }
                                                decode_storage_cursor_current_index_record_strict(
                                                    cursor_id,
                                                    cursor,
                                                    "SeekLE: malformed cursor record",
                                                )?;
                                                let coll = coll_arc
                                                    .lock()
                                                    .unwrap_or_else(|e| e.into_inner());
                                                let cmp = compare_sorter_keys(
                                                    &cursor.cur_vals_buf,
                                                    &cursor.target_vals_buf,
                                                    cursor.target_vals_buf.len(),
                                                    idx_collations,
                                                    &coll,
                                                );
                                                drop(coll);
                                                if cmp == std::cmp::Ordering::Equal {
                                                    cursor.cursor.next(&cursor.cx)?;
                                                } else {
                                                    break;
                                                }
                                            }
                                        }
                                        if cursor.cursor.eof() {
                                            cursor.cursor.last(&cursor.cx)?
                                        } else {
                                            cursor.cursor.prev(&cursor.cx)?
                                        }
                                    }
                                    Opcode::SeekLT => {
                                        if cursor.cursor.eof() {
                                            cursor.cursor.last(&cursor.cx)?
                                        } else {
                                            cursor.cursor.prev(&cursor.cx)?
                                        }
                                    }
                                    _ => unreachable!(),
                                }
                            }
                        }
                    } else if let Some(cursor) = self.cursors.get_mut(&cursor_id) {
                        // MemCursor fallback (Phase 4 path).
                        let key = key_val.to_integer();
                        if let Some(db) = self.db.as_ref() {
                            if let Some(table) = db.get_table(cursor.root_page) {
                                if table.rows.is_empty() {
                                    false
                                } else {
                                    match op.opcode {
                                        Opcode::SeekGE => {
                                            let pos = table
                                                .rows
                                                .binary_search_by_key(&key, |r| r.rowid)
                                                .unwrap_or_else(|e| e);
                                            if pos < table.rows.len() {
                                                cursor.position = Some(pos);
                                                true
                                            } else {
                                                false
                                            }
                                        }
                                        Opcode::SeekGT => {
                                            let pos = match table
                                                .rows
                                                .binary_search_by_key(&key, |r| r.rowid)
                                            {
                                                Ok(idx) => idx + 1,
                                                Err(idx) => idx,
                                            };
                                            if pos < table.rows.len() {
                                                cursor.position = Some(pos);
                                                true
                                            } else {
                                                false
                                            }
                                        }
                                        Opcode::SeekLE => {
                                            let pos = match table
                                                .rows
                                                .binary_search_by_key(&key, |r| r.rowid)
                                            {
                                                Ok(idx) => Some(idx),
                                                Err(idx) => idx.checked_sub(1),
                                            };
                                            if let Some(idx) = pos {
                                                cursor.position = Some(idx);
                                                true
                                            } else {
                                                false
                                            }
                                        }
                                        Opcode::SeekLT => {
                                            let pos = table
                                                .rows
                                                .binary_search_by_key(&key, |r| r.rowid)
                                                .unwrap_or_else(|e| e)
                                                .checked_sub(1);
                                            if let Some(idx) = pos {
                                                cursor.position = Some(idx);
                                                true
                                            } else {
                                                false
                                            }
                                        }
                                        _ => unreachable!(),
                                    }
                                }
                            } else {
                                false
                            }
                        } else {
                            false
                        }
                    } else {
                        false
                    };
                    if found {
                        pc += 1;
                    } else {
                        pc = op.p2 as usize;
                    }
                }

                Opcode::SeekScan | Opcode::SeekEnd | Opcode::SeekHit => {
                    pc += 1;
                }

                Opcode::NotFound | Opcode::NotExists | Opcode::IfNoHope => {
                    // Check if key in register P3 exists in cursor P1.
                    // Jump to P2 if NOT found; fall through if found.
                    // NULL key → always "not found".
                    let cursor_id = op.p1;
                    // Probe repositions the cursor; clear pending delete/next
                    // state so a following Next advances relative to the new
                    // cursor position.
                    if self.storage_cursors.contains_key(&cursor_id) {
                        self.pending_next_after_delete.remove(&cursor_id);
                    }
                    let key_val = self.clone_reg_materialized(op.p3);
                    if key_val.is_null() {
                        pc = op.p2 as usize;
                        continue;
                    }
                    let exists = if matches!(key_val, SqliteValue::Blob(_)) {
                        // Index seek path: P3 contains a packed record blob
                        // (from MakeRecord). Use index_move_to to find the key.
                        let key_bytes = record_blob_bytes(&key_val);
                        self.storage_cursor_find_exact_index_key(
                            cursor_id,
                            key_bytes,
                            "NotFound: missing collation registry for collated probe",
                        )?
                    } else {
                        // Table seek path: P3 contains an integer rowid.
                        let rowid_val = key_val.to_integer();
                        if let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) {
                            cursor
                                .cursor
                                .table_move_to(&cursor.cx, rowid_val)?
                                .is_found()
                        } else if let Some(cursor) = self.cursors.get(&cursor_id) {
                            if let Some(db) = self.db.as_ref() {
                                if let Some(table) = db.get_table(cursor.root_page) {
                                    table.find_by_rowid(rowid_val).is_some()
                                } else {
                                    false
                                }
                            } else {
                                false
                            }
                        } else {
                            false
                        }
                    };
                    if exists {
                        pc += 1; // Found: fall through.
                    } else {
                        pc = op.p2 as usize; // Not found: jump.
                    }
                }

                Opcode::Found => {
                    // Jump to P2 if key found in cursor P1 (exact match).
                    // NULL key → never found (don't jump).
                    let cursor_id = op.p1;
                    if self.storage_cursors.contains_key(&cursor_id) {
                        self.pending_next_after_delete.remove(&cursor_id);
                    }
                    let key_val = self.clone_reg_materialized(op.p3);
                    if key_val.is_null() {
                        pc += 1;
                        continue;
                    }
                    let exists = if matches!(key_val, SqliteValue::Blob(_)) {
                        let key_bytes = record_blob_bytes(&key_val);
                        self.storage_cursor_find_exact_index_key(
                            cursor_id,
                            key_bytes,
                            "Found: missing collation registry for collated probe",
                        )?
                    } else {
                        let rowid_val = key_val.to_integer();
                        if let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) {
                            cursor
                                .cursor
                                .table_move_to(&cursor.cx, rowid_val)?
                                .is_found()
                        } else if let Some(cursor) = self.cursors.get(&cursor_id) {
                            if let Some(db) = self.db.as_ref() {
                                if let Some(table) = db.get_table(cursor.root_page) {
                                    table.find_by_rowid(rowid_val).is_some()
                                } else {
                                    false
                                }
                            } else {
                                false
                            }
                        } else {
                            false
                        }
                    };
                    if exists {
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                Opcode::NoConflict => {
                    // Jump to P2 if NO matching key prefix exists in index
                    // cursor P1.  Falls through when a conflict IS found
                    // (cursor positioned on the conflicting entry).
                    // NULL in any key field → always jump (no conflict).
                    let cursor_id = op.p1;
                    if self.storage_cursors.contains_key(&cursor_id) {
                        self.pending_next_after_delete.remove(&cursor_id);
                    }
                    let key_val = self.clone_reg_materialized(op.p3);
                    let index_desc_flags = self.index_desc_flags_for_cursor(cursor_id);
                    let index_collations = self.index_collations_for_cursor(cursor_id);

                    // NULL short-circuit: NULL != NULL for UNIQUE purposes.
                    if key_val.is_null() {
                        pc = op.p2 as usize;
                        continue;
                    }

                    let uses_collated_probe = if let SqliteValue::Blob(ref bytes) = key_val {
                        if let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) {
                            if try_decode_storage_cursor_target_index_record(cursor, bytes)
                                && cursor.target_vals_buf.iter().any(SqliteValue::is_null)
                            {
                                pc = op.p2 as usize;
                                continue;
                            }
                            cursor.target_vals_buf.iter().enumerate().any(|(idx, _)| {
                                index_collations
                                    .get(idx)
                                    .and_then(|collation| collation.as_deref())
                                    .is_some_and(|name| !name.eq_ignore_ascii_case("BINARY"))
                            })
                        } else {
                            false
                        }
                    } else {
                        false
                    };
                    let collated_probe_registry = uses_collated_probe.then(|| {
                        self.collation_registry
                            .lock()
                            .unwrap_or_else(|err| err.into_inner())
                            .clone()
                    });

                    // Prefix-based conflict check: seek the index, then
                    // compare only the first N fields (where N = number of
                    // fields in the probe key) against the entry at the
                    // cursor position.  The index stores (columns, rowid)
                    // but the probe key has only (columns).
                    let conflict = if let SqliteValue::Blob(ref bytes) = key_val {
                        if let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) {
                            if uses_collated_probe {
                                let Some(collation_registry) = collated_probe_registry.as_ref()
                                else {
                                    return Err(FrankenError::internal(
                                        "NoConflict: missing collation registry for collated probe",
                                    ));
                                };
                                storage_cursor_no_conflict_prefix_match_collated(
                                    cursor_id,
                                    cursor,
                                    bytes,
                                    &index_desc_flags,
                                    &index_collations,
                                    collation_registry,
                                )?
                            } else {
                                storage_cursor_no_conflict_prefix_match(cursor_id, cursor, bytes)?
                            }
                        } else {
                            false
                        }
                    } else {
                        false
                    };

                    if conflict {
                        pc += 1; // Conflict found: fall through.
                    } else {
                        pc = op.p2 as usize; // No conflict: jump.
                    }
                }

                // ── Insert / Delete / NewRowid ──────────────────────────
                Opcode::NewRowid => {
                    // Allocate a new rowid for cursor p1, store in register p2.
                    //
                    // In concurrent mode, StorageCursor inserts must reserve
                    // rowids from the shared coordinator allocator so separate
                    // writers do not reuse `max(rowid)+1` from the same
                    // snapshot and silently overwrite each other.
                    let cursor_id = op.p1;
                    let target = op.p2;
                    let concurrent_mode = op.p3 != 0;
                    let concurrent_allocator = if concurrent_mode {
                        self.concurrent_rowid_allocator.clone()
                    } else {
                        None
                    };
                    let concurrent_schema_epoch = self.concurrent_rowid_schema_epoch;
                    let rowid = if let Some(sc) = self.storage_cursors.get_mut(&cursor_id) {
                        let root_page = sc.root_page;
                        let autoinc_max = sc.autoincrement_high_water;
                        let rowid_mode = sc.rowid_mode;
                        // Storage NewRowid probes max rowid via `last()`, which
                        // repositions the cursor. Clear any pending delete/next
                        // state so subsequent Next/Prev behavior is consistent
                        // with the new position.
                        self.pending_next_after_delete.remove(&cursor_id);
                        if concurrent_mode {
                            if let Some(allocator) = concurrent_allocator.as_ref() {
                                Self::allocate_concurrent_storage_rowid(
                                    allocator,
                                    concurrent_schema_epoch,
                                    root_page,
                                    rowid_mode,
                                    autoinc_max,
                                    sc,
                                    "rowid overflow: maximum rowid reached",
                                )?
                            } else {
                                Self::allocate_serialized_storage_rowid(
                                    sc,
                                    autoinc_max,
                                    "rowid overflow: maximum rowid reached",
                                )?
                            }
                        } else {
                            Self::allocate_serialized_storage_rowid(
                                sc,
                                autoinc_max,
                                "rowid overflow: maximum rowid reached",
                            )?
                        }
                    } else {
                        // MemDatabase fallback (Phase 4 in-memory cursors).
                        let root = self.cursors.get(&cursor_id).map(|c| c.root_page);
                        if let Some(root) = root {
                            if let Some(db) = self.db.as_mut() {
                                if concurrent_mode {
                                    db.alloc_rowid_concurrent(root)
                                } else {
                                    db.alloc_rowid(root)
                                }
                            } else {
                                1
                            }
                        } else {
                            1
                        }
                    };
                    self.set_reg(target, SqliteValue::Integer(rowid));
                    pc += 1;
                }

                Opcode::Insert => {
                    // Insert record in register p2 with rowid from register p3
                    // into cursor p1. p5 encodes conflict resolution mode:
                    // 1=ROLLBACK, 2=ABORT (default), 3=FAIL, 4=IGNORE, 5=REPLACE.
                    // Higher bits carry OPFLAG_* metadata.
                    //
                    // OE_* constants matching SQLite (4=IGNORE, 5=REPLACE)
                    let cursor_id = op.p1;
                    let record_reg = op.p2;
                    let rowid_reg = op.p3;
                    let oe_flag = op.p5 & 0x0F; // Low 4 bits for OE_* mode
                    let is_update = (op.p5 & OPFLAG_ISUPDATE) != 0;
                    let rowid = self.get_reg(rowid_reg).to_integer();
                    let concurrent_allocator = self.concurrent_rowid_allocator.clone();
                    let concurrent_schema_epoch = self.concurrent_rowid_schema_epoch;
                    let previous_last_insert_rowid = self.last_insert_rowid;
                    let previous_last_insert_rowid_valid = self.last_insert_rowid_valid;
                    let pending_update_restore = if is_update {
                        self.take_pending_update_restore()
                    } else {
                        self.set_pending_update_restore(None);
                        None
                    };

                    // Phase 5B.2 (bd-1yi8): write-through — route ONLY through
                    // StorageCursor when one exists; fall back to MemDatabase
                    // only for legacy Phase 4 cursors.
                    //
                    // bd-perf: Sideband buffer — if MakeRecord stored bytes in
                    // the MakeRecord sideband lookaside, take ownership here to avoid
                    // Arc allocation. Otherwise fall back to register blob.
                    let preformatted_record = match &op.p4 {
                        P4::Blob(bytes) => Some(bytes.as_slice()),
                        _ => None,
                    };
                    let sideband_active = preformatted_record.is_none()
                        && self.make_record_lookaside.sideband_is_armed_for(record_reg);
                    // Only move the register value out when there is no active
                    // MakeRecord sideband. Doing `take_reg()` first would
                    // eagerly materialize the sideband into an Arc-backed blob
                    // and defeat the INSERT hot-path optimization.
                    let record_val = if preformatted_record.is_some() {
                        self.clear_register_subtype(record_reg);
                        self.set_reg(record_reg, SqliteValue::Null);
                        SqliteValue::Null
                    } else if sideband_active {
                        self.clear_register_subtype(record_reg);
                        SqliteValue::Null
                    } else {
                        // take_reg moves the value out (replacing with Null)
                        // instead of cloning — avoids a heap allocation for
                        // Blob/Text records.
                        self.take_reg(record_reg)
                    };
                    let mut sideband_buf = if sideband_active {
                        self.make_record_lookaside.disarm();
                        self.make_record_lookaside.take_buf()
                    } else {
                        Vec::new()
                    };
                    let mut actually_inserted = false;
                    let mut inserted_via_storage = false;
                    let mut inserted_root_page = None;
                    let insert_result = (|| -> Result<Option<ExecOutcome>> {
                        if let Some(sc) = self.storage_cursors.get_mut(&cursor_id) {
                            if sc.writable {
                                let root_page = sc.root_page;
                                let autoinc_max = sc.autoincrement_high_water;
                                let rowid_mode = sc.rowid_mode;
                                let blob = if let Some(record) = preformatted_record {
                                    record
                                } else if sideband_active {
                                    &sideband_buf[..]
                                } else {
                                    record_blob_bytes(&record_val)
                                };
                                // bd-p666i: Append fast-path — if the new rowid
                                // is strictly greater than the last successfully
                                // inserted rowid on this cursor, the row cannot
                                // already exist (B-tree keys are unique) and we
                                // can skip the full B-tree seek.  This matches
                                // C SQLite's BTREE_APPEND optimization.
                                //
                                // bd-0zxi6: Safe in concurrent mode because:
                                // 1. last_successful_insert_rowid is per-StorageCursor (per-txn)
                                // 2. Each concurrent txn has its own COW page copies
                                // 3. SSI conflict detection happens at commit time
                                let append_eligible = !is_update
                                    && sc
                                        .last_successful_insert_rowid
                                        .is_some_and(|last| rowid > last);
                                let mut insert_seek_result = None;
                                let exists = if append_eligible {
                                    FSQLITE_VDBE_INSERT_APPEND_COUNT
                                        .fetch_add(1, AtomicOrdering::Relaxed);
                                    false // Append: key is larger than anything in the table
                                } else {
                                    FSQLITE_VDBE_INSERT_SEEK_COUNT
                                        .fetch_add(1, AtomicOrdering::Relaxed);
                                    let seek_result = sc.cursor.table_move_to(&sc.cx, rowid)?;
                                    insert_seek_result = Some(seek_result);
                                    seek_result.is_found()
                                };

                                if exists {
                                    if sc.last_successful_insert_rowid.take().is_some() {
                                        FSQLITE_VDBE_INSERT_APPEND_HINT_CLEAR_COUNT
                                            .fetch_add(1, AtomicOrdering::Relaxed);
                                    }
                                    // Match on the low OE_* bits directly — p5 is
                                    // not a plain bitfield in this engine because
                                    // it also carries the custom OPFLAG_ISUPDATE
                                    // bit above the conflict-mode nibble.
                                    if oe_flag == 5 {
                                        // OE_REPLACE: Delete old, insert new
                                        self.native_replace_row(cursor_id, rowid)?;
                                        let sc2 = self
                                            .storage_cursors
                                            .get_mut(&cursor_id)
                                            .ok_or_else(|| {
                                                FrankenError::internal(
                                                    "cursor disappeared during REPLACE",
                                                )
                                            })?;
                                        sc2.cursor.table_insert(&sc2.cx, rowid, blob)?;
                                        invalidate_storage_cursor_row_cache_with_reason(
                                            sc2,
                                            self.collect_vdbe_metrics,
                                            DecodeCacheInvalidationReason::WriteMutation,
                                        );
                                        if let Some(allocator) = concurrent_allocator.as_ref() {
                                            Self::bump_concurrent_storage_rowid_floor(
                                                allocator,
                                                concurrent_schema_epoch,
                                                root_page,
                                                rowid_mode,
                                                autoinc_max,
                                                sc2,
                                                rowid,
                                            )?;
                                        }
                                        inserted_via_storage = true;
                                        inserted_root_page = Some(root_page);
                                        actually_inserted = true;
                                    } else if oe_flag == 4 {
                                        // OE_IGNORE: Skip insert for conflicting row
                                        if let Some(update_restore) = pending_update_restore.clone()
                                        {
                                            self.restore_pending_update_after_conflict(
                                                update_restore,
                                            )?;
                                        }
                                    } else {
                                        // Default (ABORT/FAIL/ROLLBACK): constraint error.
                                        if let Some(update_restore) = pending_update_restore.clone()
                                        {
                                            self.restore_pending_update_after_conflict(
                                                update_restore,
                                            )?;
                                        }
                                        return Ok(Some(ExecOutcome::Error {
                                            code: ErrorCode::Constraint as i32,
                                            message: "PRIMARY KEY constraint failed".to_owned(),
                                        }));
                                    }
                                } else {
                                    // No conflict — reuse the successor/EOF position from the
                                    // existence probe when it already proved the row is absent.
                                    if insert_seek_result == Some(SeekResult::NotFound) {
                                        let rightmost_insert = sc.cursor.eof();
                                        sc.cursor
                                            .table_insert_prechecked_absent(&sc.cx, rowid, blob)?;
                                        if rightmost_insert {
                                            sc.cursor
                                                .table_refresh_rightmost_leaf_cache_after_insert(
                                                    &sc.cx, rowid,
                                                )?;
                                            sc.last_successful_insert_rowid = Some(rowid);
                                        } else if sc.last_successful_insert_rowid.take().is_some() {
                                            FSQLITE_VDBE_INSERT_APPEND_HINT_CLEAR_COUNT
                                                .fetch_add(1, AtomicOrdering::Relaxed);
                                        }
                                    } else {
                                        sc.cursor.table_insert(&sc.cx, rowid, blob)?;
                                        if append_eligible {
                                            sc.last_successful_insert_rowid = Some(rowid);
                                        } else if sc.last_successful_insert_rowid.take().is_some() {
                                            FSQLITE_VDBE_INSERT_APPEND_HINT_CLEAR_COUNT
                                                .fetch_add(1, AtomicOrdering::Relaxed);
                                        }
                                    }
                                    invalidate_storage_cursor_row_cache_with_reason(
                                        sc,
                                        self.collect_vdbe_metrics,
                                        DecodeCacheInvalidationReason::WriteMutation,
                                    );
                                    if let Some(allocator) = concurrent_allocator.as_ref() {
                                        Self::bump_concurrent_storage_rowid_floor(
                                            allocator,
                                            concurrent_schema_epoch,
                                            root_page,
                                            rowid_mode,
                                            autoinc_max,
                                            sc,
                                            rowid,
                                        )?;
                                    }
                                    inserted_via_storage = true;
                                    inserted_root_page = Some(root_page);
                                    actually_inserted = true;
                                }
                            }
                        } else if let Some(root) = self.cursors.get(&cursor_id).map(|c| c.root_page)
                        {
                            // MemDatabase fallback (Phase 4 in-memory cursors).
                            let values = if let Some(record) = preformatted_record {
                                let values = parse_record(record).ok_or_else(|| {
                                    FrankenError::internal("malformed SQLite record blob")
                                })?;
                                if self.collect_vdbe_metrics {
                                    FSQLITE_VDBE_RECORD_DECODE_CALLS_TOTAL
                                        .fetch_add(1, AtomicOrdering::Relaxed);
                                    for value in &values {
                                        record_decoded_value_metrics(value);
                                    }
                                }
                                values
                            } else if sideband_active {
                                let values = parse_record(&sideband_buf).ok_or_else(|| {
                                    FrankenError::internal("malformed SQLite record blob")
                                })?;
                                if self.collect_vdbe_metrics {
                                    FSQLITE_VDBE_RECORD_DECODE_CALLS_TOTAL
                                        .fetch_add(1, AtomicOrdering::Relaxed);
                                    for value in &values {
                                        record_decoded_value_metrics(value);
                                    }
                                }
                                values
                            } else {
                                decode_record_with_metrics(&record_val, self.collect_vdbe_metrics)?
                            };
                            if let Some(db) = self.db.as_mut() {
                                // Check rowid conflict first.
                                let rowid_conflict = db
                                    .get_table(root)
                                    .and_then(|t| t.find_by_rowid(rowid))
                                    .is_some();

                                // Check UNIQUE column constraint conflicts (non-IPK).
                                // We check even if rowid_conflict is true, because
                                // the new values might conflict with a DIFFERENT
                                // row on a UNIQUE column.
                                let unique_conflicts = db
                                    .get_table(root)
                                    .map(|t| t.find_unique_conflicts(&values))
                                    .unwrap_or_default();

                                let has_conflict = rowid_conflict || !unique_conflicts.is_empty();

                                if has_conflict {
                                    match oe_flag {
                                        4 => {
                                            // OE_IGNORE: Skip insert for conflicting row
                                            if let Some(update_restore) =
                                                pending_update_restore.clone()
                                            {
                                                self.restore_pending_update_after_conflict(
                                                    update_restore,
                                                )?;
                                            }
                                        }
                                        5 => {
                                            // OE_REPLACE: Delete conflicting row(s),
                                            // then insert new.
                                            for conflict_rid in unique_conflicts {
                                                // Delete conflicting rows that are not the new rowid
                                                // (which will be replaced by upsert_row).
                                                if conflict_rid != rowid {
                                                    db.delete_rowid(root, conflict_rid);
                                                }
                                            }
                                            db.upsert_row(root, rowid, values);
                                            actually_inserted = true;
                                        }
                                        _ => {
                                            // Default (ABORT/FAIL/ROLLBACK): constraint error.
                                            if let Some(update_restore) =
                                                pending_update_restore.clone()
                                            {
                                                self.restore_pending_update_after_conflict(
                                                    update_restore,
                                                )?;
                                            }
                                            return Ok(Some(ExecOutcome::Error {
                                                code: ErrorCode::Constraint as i32,
                                                message: "PRIMARY KEY constraint failed".to_owned(),
                                            }));
                                        }
                                    }
                                } else {
                                    // No conflict — insert normally
                                    db.upsert_row(root, rowid, values);
                                    actually_inserted = true;
                                }
                            }
                        }
                        Ok(None)
                    })();
                    // bd-perf: Return sideband buffer for reuse (keeps capacity)
                    // before propagating every fallible Insert path.
                    if sideband_active {
                        sideband_buf.clear();
                        self.make_record_lookaside.replace_buf(sideband_buf);
                    }
                    if let Some(outcome) = insert_result? {
                        break outcome;
                    }
                    if inserted_via_storage && let Some(root_page) = inserted_root_page {
                        // Hot INSERT path already knows the table root page, so
                        // avoid another cursor_root_pages lookup when marking
                        // the MemDB mirror stale.
                        self.mark_storage_root_page_dirty(root_page);
                    }

                    // Track last insert rowid only when a row was actually inserted.
                    // C SQLite does not update last_insert_rowid() when IGNORE skips.
                    if actually_inserted {
                        self.changes += 1;
                        if !is_update {
                            self.last_insert_rowid = rowid;
                            self.last_insert_rowid_valid = true;
                        }
                        self.set_pending_insert_rollback(Some(PendingInsertRollback {
                            cursor_id,
                            rowid,
                            previous_last_insert_rowid,
                            previous_last_insert_rowid_valid,
                            update_restore: pending_update_restore,
                        }));
                    } else {
                        self.set_pending_insert_rollback(None);
                        // When OE_IGNORE skips the insert (unique or rowid
                        // conflict handled internally), tell subsequent
                        // IdxInsert opcodes to skip this row's index entries.
                        if oe_flag == 4 {
                            self.set_conflict_skip_idx(true);
                        }
                    }
                    self.last_insert_cursor_id = Some(cursor_id);
                    if actually_inserted {
                        self.set_conflict_skip_idx(false);
                    }
                    self.clear_pending_idx_entries();

                    // br-22iss: Clear pending_next_after_delete since Insert repositions
                    // the cursor. This is critical for UPDATE (Delete+Insert) to avoid
                    // infinite loops when the rowid doesn't change.
                    self.pending_next_after_delete.remove(&cursor_id);
                    pc += 1;
                }

                Opcode::Delete => {
                    // Delete the row at the current cursor position.
                    let cursor_id = op.p1;
                    let is_update = (op.p5 & OPFLAG_ISUPDATE) != 0;
                    let mut deleted = false;
                    let mut deleted_storage_rowid = None;
                    let mut update_restore = None;
                    // Phase 5B.3 (bd-1r0d): write-through — route ONLY through
                    // storage cursor when one exists; fall back to MemDatabase
                    // only for legacy Phase 4 cursors.
                    if let Some(sc) = self.storage_cursors.get_mut(&cursor_id) {
                        if sc.writable && !sc.cursor.eof() {
                            let current_rowid = sc.cursor.rowid(&sc.cx)?;
                            if is_update {
                                update_restore = Some(PendingUpdateRestore::Storage {
                                    cursor_id,
                                    rowid: current_rowid,
                                    payload: sc.cursor.payload(&sc.cx)?,
                                });
                            }
                            sc.cursor.delete(&sc.cx)?;
                            invalidate_storage_cursor_row_cache_with_reason(
                                sc,
                                self.collect_vdbe_metrics,
                                DecodeCacheInvalidationReason::WriteMutation,
                            );
                            deleted_storage_rowid = Some(current_rowid);
                            deleted = true;
                        }
                    } else if let Some(cursor) = self.cursors.get(&cursor_id) {
                        // Pure in-memory path (Phase 4).
                        if let Some(pos) = cursor.position {
                            let root = cursor.root_page;
                            let can_delete = self
                                .db
                                .as_ref()
                                .and_then(|db| db.get_table(root))
                                .is_some_and(|table| pos < table.rows.len());
                            if can_delete && let Some(db) = self.db.as_mut() {
                                if is_update
                                    && let Some(row) = db
                                        .get_table(root)
                                        .and_then(|table| table.rows.get(pos))
                                        .cloned()
                                {
                                    update_restore = Some(PendingUpdateRestore::Mem {
                                        root_page: root,
                                        rowid: row.rowid,
                                        values: row.values,
                                    });
                                }
                                db.delete_at(root, pos);
                                deleted = true;
                            }
                        }
                    }
                    if let Some(storage_rowid) = deleted_storage_rowid {
                        self.sync_storage_table_delete_into_memdb_mirror(cursor_id, storage_rowid);
                    }
                    if deleted {
                        if is_update && update_restore.is_some() {
                            self.mark_statement_cold_state(StatementColdState::CONFLICT_TRACKING);
                        }
                        self.set_pending_update_restore(if is_update {
                            update_restore
                        } else {
                            None
                        });
                        // P5 bit 0 = OPFLAG_NCHANGE: only count standalone
                        // DELETE changes. UPDATE's internal Delete uses P5=0
                        // so only the subsequent Insert counts.
                        if op.p5 & 1 != 0 {
                            self.changes += 1;
                        }
                        self.pending_next_after_delete.insert(cursor_id);
                    } else if is_update {
                        self.set_pending_update_restore(None);
                    }
                    pc += 1;
                }

                Opcode::IdxInsert => {
                    // Insert key from register P2 into index cursor P1.
                    // bd-qluy: Phase 5I.6 - Wire to B-tree index_insert.
                    // P5 encoding: bit 0 = is_unique, bits 1-4 = oe_flag
                    // (conflict resolution mode for UNIQUE violations).
                    // P3 = number of indexed columns (excluding trailing
                    // rowid). P4 = columns string for the error message.
                    let cursor_id = op.p1;
                    let key_reg = op.p2;
                    let is_unique = (op.p5 & 1) != 0;
                    #[allow(clippy::cast_possible_truncation)]
                    let oe_flag = ((op.p5 >> 1) & 0x0F) as u8;
                    let n_idx_cols = op.p3 as usize;

                    // If a previous IdxInsert for the same row triggered IGNORE,
                    // skip all remaining index inserts for this row before touching
                    // the MakeRecord sideband. Otherwise a skipped index entry drops
                    // the reusable sideband buffer and forces the next row to
                    // reallocate it.
                    if self.conflict_skip_idx() {
                        pc += 1;
                        continue;
                    }

                    let key_val = self.get_reg(key_reg).clone();
                    let sideband_active = self.make_record_lookaside.sideband_is_armed_for(key_reg);
                    let key_blob = if sideband_active {
                        self.make_record_lookaside.disarm();
                        self.make_record_lookaside.take_buf()
                    } else {
                        record_blob_bytes(&key_val).to_vec()
                    };

                    let index_desc_flags = self.index_desc_flags_for_cursor(cursor_id);
                    let index_collations = self.index_collations_for_cursor(cursor_id);
                    let uses_collated_unique_probe = is_unique
                        && n_idx_cols > 0
                        && index_collations.iter().take(n_idx_cols).any(|collation| {
                            collation
                                .as_deref()
                                .is_some_and(|name| !name.eq_ignore_ascii_case("BINARY"))
                        });
                    let collated_unique_registry = uses_collated_unique_probe.then(|| {
                        self.collation_registry
                            .lock()
                            .unwrap_or_else(|err| err.into_inner())
                            .clone()
                    });

                    if let Some(sc) = self.storage_cursors.get_mut(&cursor_id) {
                        if sc.writable {
                            let key_bytes = key_blob.as_slice();

                            if is_unique && n_idx_cols > 0 {
                                let columns_label = match &op.p4 {
                                    P4::Table(s) => s.as_str(),
                                    _ => "",
                                };
                                let mut rightmost_prefix_after_insert: Option<Vec<SqliteValue>> =
                                    None;
                                let unique_insert_result = if uses_collated_unique_probe {
                                    sc.last_rightmost_unique_index_prefix = None;
                                    sc.last_rightmost_unique_index_position = None;
                                    if let Some(collation_registry) =
                                        collated_unique_registry.as_ref()
                                    {
                                        match find_conflicting_rowid_in_index_collated(
                                            sc,
                                            key_bytes,
                                            n_idx_cols,
                                            &index_desc_flags,
                                            &index_collations,
                                            collation_registry,
                                        ) {
                                            Ok(Some(conflict_rowid)) => Err((
                                                FrankenError::UniqueViolation {
                                                    columns: columns_label.to_owned(),
                                                },
                                                Some(conflict_rowid),
                                            )),
                                            Ok(None) => {
                                                match sc.cursor.index_insert(&sc.cx, key_bytes) {
                                                    Ok(()) => Ok(()),
                                                    Err(err) => Err((err, None)),
                                                }
                                            }
                                            Err(err) => Err((err, None)),
                                        }
                                    } else {
                                        Err((
                                            FrankenError::internal(
                                                "IdxInsert: missing collation registry for collated unique probe",
                                            ),
                                            None,
                                        ))
                                    }
                                } else {
                                    let fast_append_prefix =
                                        if let (Some(last_prefix), Some(last_position)) = (
                                            sc.last_rightmost_unique_index_prefix.as_ref(),
                                            sc.last_rightmost_unique_index_position,
                                        ) {
                                            if sc.cursor.position_stamp() == Some(last_position) {
                                                parse_non_null_index_prefix(key_bytes, n_idx_cols)
                                                    .and_then(|new_prefix| {
                                                        (compare_index_prefix_keys(
                                                            last_prefix,
                                                            &new_prefix,
                                                            n_idx_cols,
                                                            &index_desc_flags,
                                                            &index_collations,
                                                            &BUILTIN_COLLATION_REGISTRY,
                                                        ) == Ordering::Less)
                                                            .then_some(new_prefix)
                                                    })
                                            } else {
                                                None
                                            }
                                        } else {
                                            None
                                        };
                                    if let Some(prefix) = fast_append_prefix {
                                        match sc
                                            .cursor
                                            .index_append_after_current_rightmost_position(
                                                &sc.cx, key_bytes,
                                            ) {
                                            Ok(true) => {
                                                rightmost_prefix_after_insert = Some(prefix);
                                                Ok(())
                                            }
                                            Ok(false) => {
                                                match sc
                                                    .cursor
                                                    .index_insert_unique_with_rightmost_report(
                                                        &sc.cx,
                                                        key_bytes,
                                                        n_idx_cols,
                                                        columns_label,
                                                    ) {
                                                    Ok(inserted_after_rightmost) => {
                                                        if inserted_after_rightmost {
                                                            rightmost_prefix_after_insert =
                                                                Some(prefix);
                                                        }
                                                        Ok(())
                                                    }
                                                    Err(FrankenError::UniqueViolation {
                                                        columns,
                                                    }) => Err((
                                                        FrankenError::UniqueViolation { columns },
                                                        None,
                                                    )),
                                                    Err(err) => Err((err, None)),
                                                }
                                            }
                                            Err(err) => Err((err, None)),
                                        }
                                    } else {
                                        match sc.cursor.index_insert_unique_with_rightmost_report(
                                            &sc.cx,
                                            key_bytes,
                                            n_idx_cols,
                                            columns_label,
                                        ) {
                                            Ok(inserted_after_rightmost) => {
                                                if inserted_after_rightmost {
                                                    rightmost_prefix_after_insert =
                                                        parse_non_null_index_prefix(
                                                            key_bytes, n_idx_cols,
                                                        );
                                                }
                                                Ok(())
                                            }
                                            Err(FrankenError::UniqueViolation { columns }) => Err(
                                                (FrankenError::UniqueViolation { columns }, None),
                                            ),
                                            Err(err) => Err((err, None)),
                                        }
                                    }
                                };
                                match unique_insert_result {
                                    Ok(()) => {
                                        if let (Some(prefix), Some(position)) = (
                                            rightmost_prefix_after_insert,
                                            sc.cursor.position_stamp(),
                                        ) {
                                            sc.last_rightmost_unique_index_prefix = Some(prefix);
                                            sc.last_rightmost_unique_index_position =
                                                Some(position);
                                        } else {
                                            sc.last_rightmost_unique_index_prefix = None;
                                            sc.last_rightmost_unique_index_position = None;
                                        }
                                        invalidate_storage_cursor_row_cache_with_reason(
                                            sc,
                                            self.collect_vdbe_metrics,
                                            DecodeCacheInvalidationReason::WriteMutation,
                                        );
                                        self.push_pending_idx_entry(cursor_id, key_bytes.to_vec());
                                    }
                                    Err((FrankenError::UniqueViolation { .. }, conflict_rowid)) => {
                                        sc.last_rightmost_unique_index_prefix = None;
                                        sc.last_rightmost_unique_index_position = None;
                                        match oe_flag {
                                            // OE_IGNORE (4): Undo the table
                                            // insert, roll back any already-inserted
                                            // index entries, and skip remaining indexes.
                                            4 => {
                                                if let Err(error) = self
                                                    .rollback_pending_insert_after_index_conflict()
                                                {
                                                    if sideband_active {
                                                        self.make_record_lookaside
                                                            .replace_cleared_buf(key_blob);
                                                    }
                                                    return Err(error);
                                                }
                                                self.set_conflict_skip_idx(true);
                                                if sideband_active {
                                                    self.make_record_lookaside
                                                        .replace_cleared_buf(key_blob);
                                                }
                                                pc += 1;
                                                continue;
                                            }
                                            // OE_REPLACE (5 or 8): Find the
                                            // conflicting row, delete it (and its
                                            // index entries), then insert the new
                                            // index entry.
                                            5 | 8 => {
                                                // Find the rowid of the
                                                // conflicting row from the index.
                                                let conflict_rowid = if uses_collated_unique_probe {
                                                    conflict_rowid
                                                } else {
                                                    match find_conflicting_rowid_in_index(
                                                        sc, key_bytes, n_idx_cols,
                                                    ) {
                                                        Ok(rowid) => rowid,
                                                        Err(error) => {
                                                            if sideband_active {
                                                                self.make_record_lookaside
                                                                    .replace_cleared_buf(key_blob);
                                                            }
                                                            return Err(error);
                                                        }
                                                    }
                                                };

                                                if let Some(old_rowid) = conflict_rowid {
                                                    if let Some(tbl_cid) =
                                                        self.last_insert_cursor_id
                                                    {
                                                        if let Err(error) = self
                                                            .native_replace_row(tbl_cid, old_rowid)
                                                        {
                                                            if sideband_active {
                                                                self.make_record_lookaside
                                                                    .replace_cleared_buf(key_blob);
                                                            }
                                                            return Err(error);
                                                        }
                                                    }
                                                }

                                                // Now insert the new index entry. The binary probe
                                                // deletes the conflicting index entry directly; the
                                                // collated probe leaves exact-key cleanup to
                                                // native_replace_row above.
                                                let Some(sc2) =
                                                    self.storage_cursors.get_mut(&cursor_id)
                                                else {
                                                    if sideband_active {
                                                        self.make_record_lookaside
                                                            .replace_cleared_buf(key_blob);
                                                    }
                                                    return Err(FrankenError::internal(
                                                        "cursor must exist",
                                                    ));
                                                };
                                                sc2.last_rightmost_unique_index_prefix = None;
                                                sc2.last_rightmost_unique_index_position = None;
                                                if let Err(error) =
                                                    sc2.cursor.index_insert(&sc2.cx, key_bytes)
                                                {
                                                    if sideband_active {
                                                        self.make_record_lookaside
                                                            .replace_cleared_buf(key_blob);
                                                    }
                                                    return Err(error);
                                                }
                                                invalidate_storage_cursor_row_cache_with_reason(
                                                    sc2,
                                                    self.collect_vdbe_metrics,
                                                    DecodeCacheInvalidationReason::WriteMutation,
                                                );
                                                self.push_pending_idx_entry(
                                                    cursor_id,
                                                    key_blob.clone(),
                                                );
                                            }
                                            // Default: propagate the error
                                            // (ABORT/FAIL/ROLLBACK).
                                            _ => {
                                                if let Err(error) = self
                                                    .rollback_pending_insert_after_index_conflict()
                                                {
                                                    if sideband_active {
                                                        self.make_record_lookaside
                                                            .replace_cleared_buf(key_blob);
                                                    }
                                                    return Err(error);
                                                }
                                                if sideband_active {
                                                    self.make_record_lookaside
                                                        .replace_cleared_buf(key_blob);
                                                }
                                                return Err(FrankenError::UniqueViolation {
                                                    columns: columns_label.to_owned(),
                                                });
                                            }
                                        }
                                    }
                                    Err((error, _)) => {
                                        if sideband_active {
                                            self.make_record_lookaside
                                                .replace_cleared_buf(key_blob);
                                        }
                                        return Err(error);
                                    }
                                }
                            } else {
                                sc.last_rightmost_unique_index_prefix = None;
                                sc.last_rightmost_unique_index_position = None;
                                if let Err(error) = sc.cursor.index_insert(&sc.cx, key_bytes) {
                                    if sideband_active {
                                        self.make_record_lookaside.replace_cleared_buf(key_blob);
                                    }
                                    return Err(error);
                                }
                                invalidate_storage_cursor_row_cache_with_reason(
                                    sc,
                                    self.collect_vdbe_metrics,
                                    DecodeCacheInvalidationReason::WriteMutation,
                                );
                                self.push_pending_idx_entry(cursor_id, key_blob.clone());
                            }
                        }
                    }
                    // No MemDatabase fallback: Phase 4 in-memory backend doesn't
                    // support indexes (they're a no-op there).
                    if sideband_active {
                        self.make_record_lookaside.replace_cleared_buf(key_blob);
                    }
                    pc += 1;
                }

                Opcode::SorterInsert => {
                    // Lazy key decode: only decode the first `key_columns`
                    // values (the sort key) instead of all columns.  The
                    // raw blob is retained for output via `SorterData`.
                    let cursor_id = op.p1;
                    let record_reg = op.p2;
                    let sideband_active =
                        self.make_record_lookaside.sideband_is_armed_for(record_reg);
                    let blob = if sideband_active {
                        self.make_record_lookaside.disarm();
                        self.make_record_lookaside.take_buf()
                    } else {
                        // Move the record value out of the register instead of
                        // cloning it; MakeRecord sideband consumers above avoid
                        // this path because take_reg would materialize the blob.
                        let record = self.take_reg(record_reg);
                        record_blob_bytes(&record).to_vec()
                    };
                    if let Some(sorter) = self.sorters.get_mut(&cursor_id) {
                        let key_values =
                            fsqlite_types::record::parse_record_prefix(&blob, sorter.key_columns)
                                .ok_or_else(|| FrankenError::DatabaseCorrupt {
                                detail: "malformed record in SorterInsert".to_owned(),
                            })?;
                        sorter.insert_row(key_values, blob)?;
                    }
                    pc += 1;
                }

                Opcode::IdxDelete => {
                    // Delete entry at current position in index cursor P1.
                    // bd-qluy: Phase 5I.6 - Wire to B-tree delete.
                    //
                    // If P2 and P3 are provided, they specify the key to delete:
                    // P2 = start register, P3 = number of registers forming the key.
                    // In that case, we first seek to the key, then delete.
                    let cursor_id = op.p1;
                    let key_start_reg = op.p2;
                    let key_count = op.p3;

                    // Collect key bytes BEFORE borrowing cursor (borrow checker).
                    let key_bytes: Option<Vec<u8>> = if key_count > 0 {
                        let iter = (0..key_count).map(|i| self.get_reg(key_start_reg + i));
                        Some(fsqlite_types::record::serialize_record_iter(iter))
                    } else {
                        None
                    };

                    if let Some(ref key) = key_bytes {
                        let writable = self
                            .storage_cursors
                            .get(&cursor_id)
                            .map(|sc| sc.writable)
                            .unwrap_or(false);
                        if writable
                            && self.storage_cursor_find_exact_index_key(
                                cursor_id,
                                key,
                                "IdxDelete: missing collation registry for collated exact probe",
                            )?
                        {
                            if let Some(sc) = self.storage_cursors.get_mut(&cursor_id) {
                                sc.last_rightmost_unique_index_prefix = None;
                                sc.last_rightmost_unique_index_position = None;
                                sc.cursor.delete(&sc.cx)?;
                                invalidate_storage_cursor_row_cache_with_reason(
                                    sc,
                                    self.collect_vdbe_metrics,
                                    DecodeCacheInvalidationReason::WriteMutation,
                                );
                            }
                        }
                    } else if let Some(sc) = self.storage_cursors.get_mut(&cursor_id) {
                        if sc.writable && !sc.cursor.eof() {
                            // Delete at current position.
                            sc.last_rightmost_unique_index_prefix = None;
                            sc.last_rightmost_unique_index_position = None;
                            sc.cursor.delete(&sc.cx)?;
                            invalidate_storage_cursor_row_cache_with_reason(
                                sc,
                                self.collect_vdbe_metrics,
                                DecodeCacheInvalidationReason::WriteMutation,
                            );
                        }
                    }
                    // No MemDatabase fallback for indexes.
                    pc += 1;
                }

                Opcode::SorterCompare => {
                    // Compare current sorter key with packed record in register p3.
                    // Jump to p2 when keys differ.
                    let cursor_id = op.p1;
                    let coll = self.lock_collation();
                    let differs = if let Some(sorter) = self.sorters.get(&cursor_id) {
                        if let Some(pos) = sorter.position {
                            if let Some(current) = sorter.rows.get(pos) {
                                let probe = decode_record_with_metrics(
                                    self.get_reg(op.p3),
                                    self.collect_vdbe_metrics,
                                )?;
                                !sorter_keys_equal(
                                    &current.values,
                                    &probe,
                                    sorter.key_columns,
                                    &sorter.collations,
                                    &coll,
                                )
                            } else {
                                true
                            }
                        } else {
                            true
                        }
                    } else {
                        true
                    };
                    if differs {
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                Opcode::SorterData => {
                    // Encode current sorter row into register p2.
                    let cursor_id = op.p1;
                    let target = op.p2;
                    let value = if let Some(sorter) = self.sorters.get(&cursor_id) {
                        if let Some(pos) = sorter.position {
                            if let Some(row) = sorter.rows.get(pos) {
                                SqliteValue::Blob(row.blob.clone().into())
                            } else {
                                SqliteValue::Null
                            }
                        } else {
                            SqliteValue::Null
                        }
                    } else {
                        SqliteValue::Null
                    };
                    self.set_reg(target, value);
                    pc += 1;
                }

                Opcode::RowCell => {
                    pc += 1;
                }

                Opcode::ResetCount => {
                    pc += 1;
                }

                // ── Record building (SQLite record format) ──────────────
                Opcode::MakeRecord => {
                    self.execute_make_record_hot(op, collect_vdbe_metrics);
                    pc += 1;
                }

                Opcode::Affinity => {
                    // Apply type affinity to p2 registers starting at p1.
                    // Uses p4 as affinity string.
                    if let P4::Affinity(aff) = &op.p4 {
                        let start = op.p1;
                        for (i, ch) in aff.chars().enumerate() {
                            #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
                            let reg = start + i as i32;
                            let val = self.take_reg(reg);
                            let affinity = char_to_affinity(ch);
                            if collect_vdbe_metrics {
                                let before = val.clone();
                                let coerced = val.apply_affinity(affinity);
                                record_type_coercion(&before, &coerced);
                                self.set_reg(reg, coerced);
                            } else {
                                self.set_reg(reg, val.apply_affinity(affinity));
                            }
                        }
                    }
                    pc += 1;
                }

                // ── Miscellaneous ───────────────────────────────────────
                Opcode::HaltIfNull => {
                    if self.get_reg(op.p3).is_null() {
                        let msg = match &op.p4 {
                            P4::Str(s) => s.clone(),
                            _ => "NOT NULL constraint failed".to_owned(),
                        };
                        break ExecOutcome::Error {
                            code: op.p1,
                            message: msg,
                        };
                    }
                    pc += 1;
                }

                Opcode::Count => {
                    // Count rows in cursor P1, store result in register P2.
                    //
                    // IMPORTANT: file-backed connections often keep a schema-only
                    // MemDatabase mirror for correctness/perf reasons. In that
                    // mode, `db.get_table(root).rows.len()` is deliberately 0 even
                    // when the pager-backed table has real rows. Only true
                    // MemCursors can trust the MemDatabase row count directly.
                    let cursor_id = op.p1;
                    let count: i64 = if let Some(cursor) = self.cursors.get(&cursor_id) {
                        if let Some(db) = self.db.as_ref()
                            && let Some(table) = db.get_table(cursor.root_page)
                        {
                            i64::try_from(table.rows.len()).unwrap_or(0)
                        } else {
                            0
                        }
                    } else if let Some(sc) = self.storage_cursors.get_mut(&cursor_id) {
                        // bd-wwqen.3: storage-cursor COUNT must report the
                        // cursor's own storage view, not a connection-local
                        // MemDatabase mirror that may lag retained same-connection
                        // writes.
                        sc.cursor.count_all_rows(&sc.cx)?
                    } else {
                        0
                    };
                    self.set_reg_int(op.p2, count);
                    pc += 1;
                }

                Opcode::CountIndexEqRun => {
                    let probe_value = self.get_reg(op.p3).clone();
                    let matched = if probe_value.is_null() {
                        0
                    } else {
                        let cursor = self.storage_cursors.get_mut(&op.p1).ok_or_else(|| {
                            FrankenError::internal(
                                "CountIndexEqRun requires an open storage cursor",
                            )
                        })?;
                        if cursor.cursor.is_table_btree() {
                            return Err(FrankenError::internal(
                                "CountIndexEqRun requires an index cursor",
                            ));
                        }
                        storage_cursor_count_equal_first_key_run(
                            cursor,
                            &probe_value,
                            self.collect_vdbe_metrics,
                        )?
                    };
                    let next_count = self.get_reg(op.p2).to_integer().wrapping_add(matched);
                    self.set_reg_int(op.p2, next_count);
                    pc += 1;
                }

                Opcode::Sequence => {
                    let counter = self
                        .ensure_cold_state_for(StatementColdState::SEQUENCE_COUNTERS)
                        .sequence_counters
                        .entry(op.p1)
                        .or_insert(0);
                    let val = *counter;
                    *counter += 1;
                    self.set_reg_int(op.p2, val);
                    pc += 1;
                }

                Opcode::SequenceTest => {
                    pc += 1;
                }

                Opcode::Variable => {
                    // Bind parameter (1-indexed). Unbound params read as NULL.
                    let idx = usize::try_from(op.p1)
                        .ok()
                        .and_then(|one_based| one_based.checked_sub(1));
                    let value = idx
                        .and_then(|idx| {
                            borrowed_bindings
                                .and_then(|bindings| bindings.get(idx))
                                .or_else(|| self.bindings.get(idx))
                        })
                        .cloned()
                        .unwrap_or(SqliteValue::Null);
                    self.set_reg(op.p2, value);
                    pc += 1;
                }

                Opcode::BeginSubrtn => {
                    self.set_reg(op.p2, SqliteValue::Null);
                    pc += 1;
                }

                Opcode::IsTrue => {
                    // Synopsis: r[P2] = coalesce(IsTrue(r[P1]),P3) ^ P4
                    // Implements IS TRUE, IS FALSE, IS NOT TRUE, IS NOT FALSE.
                    let val = self.get_reg(op.p1);
                    let p4_val = match &op.p4 {
                        P4::Int(n) => *n,
                        _ => 0,
                    };
                    if val.is_null() {
                        self.set_reg(op.p2, SqliteValue::Integer(i64::from(op.p3 ^ p4_val)));
                    } else {
                        let v = i32::from(vdbe_real_is_truthy(val));
                        self.set_reg(op.p2, SqliteValue::Integer(i64::from((v ^ p4_val) & 1)));
                    }
                    pc += 1;
                }

                Opcode::ZeroOrNull => {
                    // If either P1 or P3 is NULL, set P2 to NULL.
                    // Otherwise set P2 to 0.
                    // Reference: ZeroOrNull semantics (OP_ZeroOrNull spec).
                    if self.get_reg(op.p1).is_null() || self.get_reg(op.p3).is_null() {
                        self.set_reg(op.p2, SqliteValue::Null);
                    } else {
                        self.set_reg(op.p2, SqliteValue::Integer(0));
                    }
                    pc += 1;
                }

                Opcode::IfNullRow => {
                    // Jump to p2 if cursor p1 is not positioned on a row.
                    // C SQLite also sets register P3 to NULL before jumping.
                    let is_null = if let Some(cursor) = self.storage_cursors.get(&op.p1) {
                        cursor.cursor.eof()
                    } else if let Some(state) = self
                        .cold_state()
                        .and_then(|cold_state| cold_state.vtab_cursors.get(&op.p1))
                    {
                        state.null_row || state.cursor.eof()
                    } else {
                        self.cursors
                            .get(&op.p1)
                            .is_none_or(|c| c.position.is_none() && !c.is_pseudo)
                    };
                    if is_null {
                        if op.p3 > 0 {
                            self.set_reg(op.p3, SqliteValue::Null);
                        }
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                Opcode::IfNotOpen => {
                    // Jump to p2 if cursor p1 is not open.
                    if self.cursors.contains_key(&op.p1)
                        || self.storage_cursors.contains_key(&op.p1)
                        || self
                            .cold_state()
                            .is_some_and(|cold_state| cold_state.vtab_cursors.contains_key(&op.p1))
                        || self.sorters.contains_key(&op.p1)
                    {
                        pc += 1;
                    } else {
                        pc = op.p2 as usize;
                    }
                }

                Opcode::Compare => {
                    // Compare P1..P1+P3-1 with P2..P2+P3-1.
                    let start_a = op.p1;
                    let start_b = op.p2;
                    let count = op.p3;
                    let coll_arc = Arc::clone(&self.collation_registry);
                    let result = {
                        let coll = coll_arc.lock().unwrap_or_else(|e| e.into_inner());
                        let mut result = Ordering::Equal;
                        for i in 0..count {
                            let val_a = self.get_reg(start_a + i);
                            let val_b = self.get_reg(start_b + i);
                            let coll_name = usize::try_from(i).ok().and_then(|field_idx| {
                                compare_collation_for_field_from_p4(&op.p4, field_idx)
                            });
                            // SQLite NULL sort order: NULLs sort before all other
                            // values.  When partial_cmp returns None (NULL vs
                            // non-NULL or NaN), apply NULL-first ordering.
                            let ord = if let Some(coll_name) = coll_name {
                                collate_compare(val_a, val_b, coll_name, &coll)
                            } else {
                                val_a.partial_cmp(val_b)
                            };
                            let o = match ord {
                                Some(o) => o,
                                None => {
                                    // NULL < non-NULL; NULL == NULL for sort purposes.
                                    match (val_a.is_null(), val_b.is_null()) {
                                        (true, true) => Ordering::Equal,
                                        (true, false) => Ordering::Less,
                                        (false, true) => Ordering::Greater,
                                        (false, false) => Ordering::Equal,
                                    }
                                }
                            };
                            if o != Ordering::Equal {
                                result = o;
                                break;
                            }
                        }
                        result
                    };
                    self.last_compare_result = Some(result);
                    pc += 1;
                }

                Opcode::Jump => {
                    // Jump to one of p1/p2/p3 based on last comparison.
                    let target = match self.last_compare_result {
                        Some(Ordering::Less) => op.p1,
                        Some(Ordering::Equal) => op.p2,
                        Some(Ordering::Greater) => op.p3,
                        None => {
                            // If no comparison has happened, fall through or use p2?
                            // SQLite spec says Jump logic depends on the preceding Compare.
                            // If we haven't compared, neutral path (p2) is a safe fallback.
                            op.p2
                        }
                    };
                    pc = target as usize;
                }

                Opcode::TypeCheck => {
                    // P4 is either Affinity("IRT") or Str("IRT\ttable\tcol1\tcol2\tcol3")
                    let p4_str = match &op.p4 {
                        P4::Affinity(s) | P4::Str(s) => s.as_str(),
                        _ => "",
                    };
                    // Split on tab: first part is affinity pattern, rest is table+columns.
                    let mut parts = p4_str.split('\t');
                    let pattern = parts.next().unwrap_or("").as_bytes();
                    let table_name = parts.next().unwrap_or("");
                    let col_names: Vec<&str> = parts.collect();

                    #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
                    let count = op.p2.max(0) as usize;
                    for offset in 0..count {
                        #[allow(clippy::cast_possible_wrap)]
                        let reg = op.p1 + offset as i32;
                        let value = self.get_reg(reg);
                        let strict_type = match pattern.get(offset).copied().unwrap_or(b'A') {
                            b'A' | b'a' => None,
                            b'I' | b'i' => Some(StrictColumnType::Integer),
                            b'R' | b'r' => Some(StrictColumnType::Real),
                            b'T' | b't' => Some(StrictColumnType::Text),
                            b'L' | b'l' => Some(StrictColumnType::Blob),
                            other => {
                                return Err(FrankenError::Internal(format!(
                                    "unknown STRICT type code '{}' in OP_TypeCheck",
                                    char::from(other)
                                )));
                            }
                        };

                        if let Some(expected) = strict_type {
                            let checked =
                                value.clone().validate_strict(expected).map_err(|err| {
                                    let col_label = col_names
                                        .get(offset)
                                        .filter(|s| !s.is_empty())
                                        .map_or_else(
                                            || format!("column {offset}"),
                                            |name| {
                                                if table_name.is_empty() {
                                                    (*name).to_owned()
                                                } else {
                                                    format!("{table_name}.{name}")
                                                }
                                            },
                                        );
                                    let col_type = format!("{expected:?}").to_ascii_uppercase();
                                    let actual_str = err.actual.to_string();
                                    tracing::warn!(
                                        register = reg,
                                        expected = ?expected,
                                        actual = %actual_str,
                                        column = %col_label,
                                        value = ?value,
                                        "STRICT type violation"
                                    );
                                    FrankenError::DatatypeViolation {
                                        column: col_label,
                                        column_type: col_type,
                                        actual: actual_str,
                                    }
                                })?;
                            self.set_reg(reg, checked);
                        }
                    }
                    pc += 1;
                }

                Opcode::Permutation | Opcode::CollSeq | Opcode::ElseEq | Opcode::FkCheck => {
                    pc += 1;
                }

                Opcode::IsType => {
                    // Check datatype of a value against the P5 type bitmask.
                    // If P1 >= 0: check column P3 of cursor P1.
                    // If P1 == -1: check register P3.
                    // P4 (Int) = default type code if column is beyond row width.
                    // P5 bitmask: 0x01=INTEGER, 0x02=FLOAT, 0x04=TEXT, 0x08=BLOB, 0x10=NULL
                    // Jump to P2 if the value's type matches a bit in P5.
                    let val_ref;
                    let val = if op.p1 < 0 {
                        self.get_reg(op.p3)
                    } else {
                        val_ref = self.cursor_column(op.p1, op.p3 as usize)?;
                        &val_ref
                    };
                    let type_bit: u16 = match val {
                        SqliteValue::Integer(_) => 0x01,
                        SqliteValue::Float(_) => 0x02,
                        SqliteValue::Text(_) => 0x04,
                        SqliteValue::Blob(_) => 0x08,
                        SqliteValue::Null => 0x10,
                    };
                    if op.p5 & type_bit != 0 {
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                Opcode::IfEmpty => {
                    // Jump to P2 if the table/index at cursor P1 is empty.
                    // WARNING: For storage cursors, this repositions the cursor
                    // to the first entry via `cursor.first()` as a side-effect.
                    // This is currently dead code (codegen never emits IfEmpty),
                    // but if this opcode is ever used, the cursor repositioning
                    // may invalidate assumptions about cursor position.
                    let cursor_id = op.p1;
                    let empty = if let Some(sc) = self.storage_cursors.get_mut(&cursor_id) {
                        // Try moving to first; false means empty.
                        let had_row = sc.cursor.first(&sc.cx)?;
                        !had_row
                    } else if let Some(cursor) = self.cursors.get(&cursor_id) {
                        if let Some(db) = self.db.as_ref()
                            && let Some(table) = db.get_table(cursor.root_page)
                        {
                            table.rows.is_empty()
                        } else {
                            true // no table = empty
                        }
                    } else {
                        true
                    };
                    if empty {
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                Opcode::IfSizeBetween => {
                    // Compute X = 10*log2(N) where N = approx row count of
                    // cursor P1 (or -1 if empty). Jump to P2 if X is in
                    // [P3, P4]. When we lack exact stats, estimate from the
                    // MemTable row count or assume 0 (empty) for storage
                    // cursors (conservative).
                    let cursor_id = op.p1;
                    let row_count: i64 = if let Some(cursor) = self.cursors.get(&cursor_id) {
                        if let Some(db) = self.db.as_ref()
                            && let Some(table) = db.get_table(cursor.root_page)
                        {
                            i64::try_from(table.rows.len()).unwrap_or(0)
                        } else {
                            0
                        }
                    } else {
                        // For storage cursors, we don't have a cheap row count.
                        // Default to -1 (empty) which maps to X = -10.
                        -1
                    };
                    #[allow(clippy::cast_precision_loss)]
                    let x = if row_count <= 0 {
                        -10_i32 // empty sentinel
                    } else {
                        ((row_count as f64).log2() * 10.0) as i32
                    };
                    let lo = op.p3;
                    let hi = match &op.p4 {
                        P4::Int(v) => *v,
                        _ => i32::MAX,
                    };
                    if x >= lo && x <= hi {
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                Opcode::IdxRowid => {
                    // Extract rowid from index cursor p1 into register p2.
                    // For storage cursors this delegates to B-tree cursor
                    // rowid(), which decodes the trailing rowid field from the
                    // index key record.
                    let cursor_id = op.p1;
                    let target = op.p2;
                    let val = self.cursor_rowid(cursor_id)?;
                    self.set_reg_fast(target, val);
                    pc += 1;
                }

                Opcode::DeferredSeek | Opcode::FinishSeek => {
                    pc += 1;
                }

                // ── Index comparison ────────────────────────────────────
                //
                // Compare the current index cursor key against a probe
                // key record in register P3. Jump to P2 when the
                // condition holds.
                //
                //   IdxLE: jump if cursor_key <= probe_key
                //   IdxGT: jump if cursor_key >  probe_key
                //   IdxLT: jump if cursor_key <  probe_key
                //   IdxGE: jump if cursor_key >= probe_key
                //
                // P1 = cursor, P2 = jump target, P3 = register with
                // probe key blob, P5 = number of key columns to compare
                // (0 means use all columns from the probe).
                Opcode::IdxLE | Opcode::IdxGT | Opcode::IdxLT | Opcode::IdxGE => {
                    let cursor_id = op.p1;
                    let probe_val = self.clone_reg_materialized(op.p3);

                    let desc_flags = self.index_desc_flags_for_cursor(cursor_id);
                    let collations = self.index_collations_for_cursor(cursor_id);

                    // Extract current cursor key as parsed fields.
                    if let Some(sc) = self.storage_cursors.get_mut(&cursor_id) {
                        if sc.cursor.eof() {
                            // EOF: IdxGT/IdxGE jump (past end), IdxLT/IdxLE fall through.
                            let jump = matches!(op.opcode, Opcode::IdxGT | Opcode::IdxGE);
                            if jump {
                                pc = op.p2 as usize;
                            } else {
                                pc += 1;
                            }
                            continue;
                        }

                        sc.target_vals_buf.clear();
                        if let SqliteValue::Blob(bytes) = &probe_val {
                            fsqlite_types::record::parse_record_into(
                                bytes,
                                &mut sc.target_vals_buf,
                            )
                            .ok_or_else(|| {
                                FrankenError::internal("index seek: malformed probe key record")
                            })?;
                        }

                        if op.p5 == 1
                            && let Some(probe_first) = sc.target_vals_buf.first().cloned()
                        {
                            let coll_arc = Arc::clone(&self.collation_registry);
                            let coll_guard = coll_arc.lock().unwrap_or_else(|e| e.into_inner());
                            let cmp = storage_cursor_current_first_index_key_compare(
                                sc,
                                &probe_first,
                                self.collect_vdbe_metrics,
                                "IdxCmp: malformed index record at cursor position",
                                desc_flags.first().copied().unwrap_or(false),
                                collations
                                    .first()
                                    .and_then(|collation| collation.as_deref()),
                                &coll_guard,
                            )?;
                            drop(coll_guard);

                            if idx_compare_condition_met(op.opcode, cmp) {
                                pc = op.p2 as usize;
                            } else {
                                pc += 1;
                            }
                            continue;
                        }

                        if !try_decode_storage_cursor_current_index_record(cursor_id, sc)? {
                            return Err(FrankenError::internal(
                                "IdxCmp: malformed index record at cursor position",
                            ));
                        }

                        let n_compare = if op.p5 > 0 {
                            op.p5 as usize
                        } else {
                            sc.target_vals_buf.len()
                        };
                        // Lock collation via a separately-owned Arc clone
                        // so the mutable borrow on `sc` is not conflicted.
                        // Avoids cloning cur/tgt vals into SmallVecs per cmp.
                        let coll_arc = Arc::clone(&self.collation_registry);
                        let coll_guard = coll_arc.lock().unwrap_or_else(|e| e.into_inner());
                        let cmp = compare_index_prefix_keys(
                            &sc.cur_vals_buf,
                            &sc.target_vals_buf,
                            n_compare,
                            &desc_flags,
                            &collations,
                            &coll_guard,
                        );
                        drop(coll_guard);

                        let condition_met = idx_compare_condition_met(op.opcode, cmp);

                        if condition_met {
                            pc = op.p2 as usize;
                        } else {
                            pc += 1;
                        }
                    } else if let Some(cursor) = self.cursors.get(&cursor_id) {
                        // MemCursor fallback (Phase 4).
                        let probe_fields =
                            decode_record_with_metrics(&probe_val, self.collect_vdbe_metrics)?;
                        if let Some(pos) = cursor.position
                            && let Some(db) = self.db.as_ref()
                            && let Some(table) = db.get_table(cursor.root_page)
                            && let Some(row) = table.rows.get(pos)
                        {
                            let n_compare = if op.p5 > 0 {
                                op.p5 as usize
                            } else {
                                probe_fields.len()
                            };
                            let desc_flags = self.index_desc_flags_for_root(cursor.root_page);
                            let collations = self.index_collations_for_root(cursor.root_page);
                            let coll_guard = self.lock_collation();
                            let cmp = compare_index_prefix_keys(
                                &row.values,
                                &probe_fields,
                                n_compare,
                                &desc_flags,
                                &collations,
                                &coll_guard,
                            );
                            drop(coll_guard);
                            let condition_met = idx_compare_condition_met(op.opcode, cmp);
                            if condition_met {
                                pc = op.p2 as usize;
                            } else {
                                pc += 1;
                            }
                        } else {
                            // No position or no table: treat as past-end.
                            let jump = matches!(op.opcode, Opcode::IdxGT | Opcode::IdxGE);
                            if jump {
                                pc = op.p2 as usize;
                            } else {
                                pc += 1;
                            }
                        }
                    } else {
                        pc += 1;
                    }
                }

                // ── Schema / DDL ────────────────────────────────────────
                Opcode::CreateBtree => {
                    // Create a new B-tree (table) and store the root page in
                    // register p2. In memory mode, allocate a new MemTable.
                    let target = op.p2;
                    let root_page = if let Some(db) = self.db.as_mut() {
                        db.create_table(0) // Column count set later.
                    } else {
                        0
                    };
                    self.set_reg(target, SqliteValue::Integer(i64::from(root_page)));
                    pc += 1;
                }

                Opcode::Clear => {
                    // Clear all rows from a table. p1 = root page.
                    if let Some(db) = self.db.as_mut() {
                        db.clear_table(op.p1);
                    }
                    pc += 1;
                }

                Opcode::Destroy => {
                    // Remove a table/index. p1 = root page.
                    //
                    // NOTE: This only removes the in-memory MemDatabase entry.
                    // Page-level freelist reclamation is handled by
                    // `Connection::execute_drop` → `free_btree_pages` which is
                    // the primary DDL path. If VDBE-driven DROP is ever used
                    // for pager-backed databases, this must also walk the B-tree
                    // and call `txn_page_io.free_page()` for every page.
                    if let Some(db) = self.db.as_mut() {
                        db.destroy_table(op.p1);
                    }
                    pc += 1;
                }

                Opcode::SqlExec
                | Opcode::ParseSchema
                | Opcode::LoadAnalysis
                | Opcode::DropTable
                | Opcode::DropIndex
                | Opcode::DropTrigger => {
                    pc += 1;
                }

                Opcode::ResetSorter => {
                    if let Some(sorter) = self.sorters.get_mut(&op.p1) {
                        sorter.reset();
                    }
                    pc += 1;
                }

                // ── Savepoint ──────────────────────────────────────────
                Opcode::Savepoint => {
                    // P1: 0=BEGIN, 1=RELEASE, 2=ROLLBACK
                    // P4: savepoint name
                    // In the in-memory engine, savepoints use undo
                    // version tokens to snapshot/restore state.
                    // Full implementation deferred to WAL/pager integration.
                    pc += 1;
                }

                // ── Checkpoint ────────────────────────────────────────────
                Opcode::Checkpoint => {
                    // WAL checkpoint. No-op for in-memory engine.
                    pc += 1;
                }

                // ── Program execution (subprogram) ──────────────────────
                Opcode::Program | Opcode::Param => {
                    pc += 1;
                }

                // ── Coroutine ───────────────────────────────────────────
                Opcode::InitCoroutine => {
                    self.set_reg(op.p1, SqliteValue::Integer(i64::from(op.p3)));
                    if op.p2 > 0 {
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                Opcode::Yield => {
                    let saved = self.get_reg(op.p1).to_integer();
                    if saved < 0 || saved as usize >= ops.len() {
                        return Err(FrankenError::Internal(format!(
                            "Yield: coroutine address {} out of bounds",
                            saved
                        )));
                    }
                    let current = (pc + 1) as i32;
                    self.set_reg(op.p1, SqliteValue::Integer(i64::from(current)));
                    pc = saved as usize;
                }

                Opcode::EndCoroutine => {
                    let saved = self.get_reg(op.p1).to_integer();
                    if saved < 0 || saved as usize >= ops.len() {
                        return Err(FrankenError::Internal(format!(
                            "EndCoroutine: coroutine address {} out of bounds",
                            saved
                        )));
                    }
                    pc = saved as usize;
                }

                // ── Aggregation ─────────────────────────────────────────
                //
                // Phase 4 supports single-group aggregation (no GROUP BY) using
                // AggStep/AggFinal. Aggregate state is stored out-of-band and keyed
                // by the accumulator register.
                // AggStep1 is a single-argument fast-path with identical semantics.
                Opcode::AggStep | Opcode::AggStep1 => {
                    let (func_name, agg_collation): (&str, Option<&str>) = match &op.p4 {
                        P4::FuncName(name) => (name.as_str(), None),
                        P4::FuncNameCollated(name, coll) => (name.as_str(), Some(coll.as_str())),
                        _ => {
                            return Err(FrankenError::Internal(
                                "AggStep opcode missing P4::FuncName".to_owned(),
                            ));
                        }
                    };

                    let arg_count = i32::from(op.p5);
                    let func_pc = pc;
                    let func = if let Some(func) = self.aggregate_function_cache.get(&func_pc) {
                        Arc::clone(func)
                    } else {
                        let registry = self.func_registry.as_ref().ok_or_else(|| {
                            FrankenError::Internal(
                                "AggStep opcode executed without function registry".to_owned(),
                            )
                        })?;
                        let func =
                            registry
                                .find_aggregate(func_name, arg_count)
                                .ok_or_else(|| {
                                    FrankenError::Internal(format!(
                                        "no such aggregate function: {func_name}/{arg_count}",
                                    ))
                                })?;
                        self.aggregate_function_cache
                            .insert(func_pc, Arc::clone(&func));
                        func
                    };

                    let accum_reg = op.p3;
                    let is_distinct = op.p1 != 0;
                    let arg_count = usize::from(op.p5);
                    let execution_cx = self.execution_cx.clone();
                    if op.p2 >= 0
                        && (op.p2 as usize).saturating_add(arg_count) <= self.registers.len()
                    {
                        for offset in 0..arg_count {
                            if let Some(reg) = Self::reg_with_offset(op.p2, offset) {
                                self.materialize_make_record_sideband(reg);
                            }
                        }
                        let start_idx = op.p2 as usize;
                        let args = &self.registers[start_idx..start_idx + arg_count];
                        Self::agg_step_with_args(
                            &mut self.cold_state,
                            &mut self.statement_cold_state,
                            AggStepCall {
                                accum_reg,
                                is_distinct,
                                func: &func,
                                func_name,
                                agg_collation,
                                execution_cx: &execution_cx,
                                args,
                            },
                        )?;
                    } else {
                        let args = self.collect_reg_range(op.p2, arg_count);
                        Self::agg_step_with_args(
                            &mut self.cold_state,
                            &mut self.statement_cold_state,
                            AggStepCall {
                                accum_reg,
                                is_distinct,
                                func: &func,
                                func_name,
                                agg_collation,
                                execution_cx: &execution_cx,
                                args: &args,
                            },
                        )?;
                    }
                    pc += 1;
                }

                Opcode::AggFinal => {
                    let func_name = match &op.p4 {
                        P4::FuncName(name) | P4::FuncNameCollated(name, _) => name.as_str(),
                        _ => {
                            return Err(FrankenError::Internal(
                                "AggFinal opcode missing P4::FuncName".to_owned(),
                            ));
                        }
                    };

                    let arg_count = op.p2;
                    let func_pc = pc;
                    let func = if let Some(func) = self.aggregate_function_cache.get(&func_pc) {
                        Arc::clone(func)
                    } else {
                        let registry = self.func_registry.as_ref().ok_or_else(|| {
                            FrankenError::Internal(
                                "AggFinal opcode executed without function registry".to_owned(),
                            )
                        })?;
                        let func =
                            registry
                                .find_aggregate(func_name, arg_count)
                                .ok_or_else(|| {
                                    FrankenError::Internal(format!(
                                        "no such aggregate function: {func_name}/{arg_count}",
                                    ))
                                })?;
                        self.aggregate_function_cache
                            .insert(func_pc, Arc::clone(&func));
                        func
                    };

                    let accum_reg = op.p1;
                    observe_execution_cancellation(&self.execution_cx)?;
                    let result = match self
                        .cold_state_mut()
                        .and_then(|cold_state| cold_state.aggregates.remove(&accum_reg))
                    {
                        Some(ctx) => {
                            if !Arc::ptr_eq(&ctx.func, &func) {
                                return Err(FrankenError::Internal(
                                    "AggFinal accumulator used for a different aggregate"
                                        .to_owned(),
                                ));
                            }
                            ctx.func.finalize(ctx.state)?
                        }
                        None => func.finalize(func.initial_state())?,
                    };

                    observe_execution_cancellation(&self.execution_cx)?;
                    self.set_reg(accum_reg, result);
                    pc += 1;
                }

                Opcode::AggInverse => {
                    // Inverse aggregate step for window functions.
                    // Remove a row from the sliding window frame.
                    // P4 = function name, P2 = first arg register,
                    // P5 = arg count, P3 = accumulator register.
                    let func_name = match &op.p4 {
                        P4::FuncName(name) | P4::FuncNameCollated(name, _) => name.as_str(),
                        _ => {
                            return Err(FrankenError::Internal(
                                "AggInverse opcode missing P4::FuncName".to_owned(),
                            ));
                        }
                    };

                    let registry = self.func_registry.as_ref().ok_or_else(|| {
                        FrankenError::Internal(
                            "AggInverse opcode executed without function registry".to_owned(),
                        )
                    })?;

                    let arg_count = i32::from(op.p5);
                    let func = registry.find_window(func_name, arg_count).ok_or_else(|| {
                        FrankenError::Internal(format!(
                            "no such window function: {func_name}/{arg_count}",
                        ))
                    })?;

                    let accum_reg = op.p3;
                    let args = self.collect_reg_range(op.p2, usize::from(op.p5));
                    let execution_cx = self.execution_cx.clone();
                    let ctx = self
                        .ensure_cold_state_for(StatementColdState::WINDOW_CONTEXTS)
                        .window_contexts
                        .entry_or_insert_with(accum_reg, || {
                            let state = func.initial_state();
                            WindowContext {
                                func: func.clone(),
                                state,
                            }
                        });

                    observe_execution_cancellation(&execution_cx)?;
                    ctx.func.inverse(&mut ctx.state, &args)?;
                    observe_execution_cancellation(&execution_cx)?;
                    pc += 1;
                }

                Opcode::AggValue => {
                    // Extract the current intermediate value from the
                    // window accumulator in register P3, storing the
                    // result in register P3. Unlike AggFinal, this
                    // does NOT consume the accumulator.
                    // P4 = function name, P1 = accumulator register,
                    // P3 = destination register.
                    let func_name = match &op.p4 {
                        P4::FuncName(name) | P4::FuncNameCollated(name, _) => name.as_str(),
                        _ => {
                            return Err(FrankenError::Internal(
                                "AggValue opcode missing P4::FuncName".to_owned(),
                            ));
                        }
                    };

                    let registry = self.func_registry.as_ref().ok_or_else(|| {
                        FrankenError::Internal(
                            "AggValue opcode executed without function registry".to_owned(),
                        )
                    })?;

                    let arg_count = op.p2;
                    let func = registry.find_window(func_name, arg_count).ok_or_else(|| {
                        FrankenError::Internal(format!(
                            "no such window function: {func_name}/{arg_count}",
                        ))
                    })?;

                    let accum_reg = op.p1;
                    observe_execution_cancellation(&self.execution_cx)?;
                    let result = match self
                        .cold_state()
                        .and_then(|cold_state| cold_state.window_contexts.get(&accum_reg))
                    {
                        Some(ctx) => ctx.func.value(&ctx.state)?,
                        None => func.value(&func.initial_state())?,
                    };
                    observe_execution_cancellation(&self.execution_cx)?;
                    self.set_reg(op.p3, result);
                    pc += 1;
                }

                // ── Scalar function call ──────────────────────────────────
                //
                // Function/PureFunc: p1 = constant-p5-flags, p2 = first-arg register,
                // p3 = output register, p4 = FuncName, p5 = arg count.
                // Arguments are in registers p2..p2+p5.
                Opcode::Function | Opcode::PureFunc => {
                    let func_name = match &op.p4 {
                        P4::FuncName(name) | P4::FuncNameCollated(name, _) => name.as_str(),
                        _ => {
                            return Err(FrankenError::Internal(
                                "Function opcode missing P4::FuncName".to_owned(),
                            ));
                        }
                    };
                    let arg_count = op.p5 as usize;
                    let first_arg_reg = op.p2;
                    let output_reg = op.p3;

                    let func_pc = pc;
                    #[allow(clippy::cast_possible_wrap)]
                    let func = if let Some(func) = self.scalar_function_cache.get(&func_pc) {
                        Arc::clone(func)
                    } else {
                        let registry = self.func_registry.as_ref().ok_or_else(|| {
                            FrankenError::Internal(
                                "Function opcode executed without function registry".to_owned(),
                            )
                        })?;
                        let func = registry
                            .find_scalar_precanonical(func_name, arg_count as i32)
                            .or_else(|| registry.find_scalar(func_name, arg_count as i32))
                            .ok_or_else(|| {
                                FrankenError::Internal(format!(
                                    "no such function: {func_name}/{arg_count}",
                                ))
                            })?;
                        self.scalar_function_cache
                            .insert(func_pc, Arc::clone(&func));
                        func
                    };

                    // Use a direct slice into the register file instead of
                    // allocating a SmallVec via collect_reg_range.  Same pattern as
                    // AggStep.  Falls back to collect_reg_range only when the
                    // register range is out of bounds or negative.
                    let result = if first_arg_reg >= 0
                        && (first_arg_reg as usize).saturating_add(arg_count)
                            <= self.registers.len()
                    {
                        let start_idx = first_arg_reg as usize;
                        let end_idx = start_idx + arg_count;
                        let args = &self.registers[start_idx..end_idx];
                        observe_execution_cancellation(&self.execution_cx)?;
                        func.invoke(args)?
                    } else {
                        let args = self.collect_reg_range(first_arg_reg, arg_count);
                        observe_execution_cancellation(&self.execution_cx)?;
                        func.invoke(&args)?
                    };
                    observe_execution_cancellation(&self.execution_cx)?;

                    if self.trace_opcodes {
                        let result_type = match &result {
                            SqliteValue::Null => "null",
                            SqliteValue::Integer(_) => "integer",
                            SqliteValue::Float(_) => "real",
                            SqliteValue::Text(_) => "text",
                            SqliteValue::Blob(_) => "blob",
                        };
                        tracing::trace!(
                            target: "fsqlite_func::eval",
                            func_name,
                            arg_count,
                            result_type,
                            "func_eval",
                        );
                    }

                    // Update global call count (fast path: no Instant::now).
                    fsqlite_func::record_func_call_count_only();

                    self.set_reg(output_reg, result);
                    pc += 1;
                }

                Opcode::LikeConstFast => {
                    let kind = SqlLikeFastPathKind::from_opcode_tag(op.p3).ok_or_else(|| {
                        FrankenError::Internal(
                            "LikeConstFast opcode has invalid fast-path tag".to_owned(),
                        )
                    })?;
                    let literal = match &op.p4 {
                        P4::Str(text) => text.as_str(),
                        _ => {
                            return Err(FrankenError::Internal(
                                "LikeConstFast opcode missing P4::Str literal".to_owned(),
                            ));
                        }
                    };
                    let input = self.get_reg(op.p1);
                    let result = if input.is_null() {
                        SqliteValue::Null
                    } else {
                        let matched = match input {
                            SqliteValue::Text(text) => {
                                sql_like_fast_path_matches(kind, literal, text)
                            }
                            SqliteValue::Blob(bytes) => {
                                let text = String::from_utf8_lossy(bytes);
                                sql_like_fast_path_matches(kind, literal, &text)
                            }
                            _ => {
                                let text = input.to_text();
                                sql_like_fast_path_matches(kind, literal, &text)
                            }
                        };
                        let final_match = if op.p5 != 0 { !matched } else { matched };
                        SqliteValue::Integer(i64::from(final_match))
                    };
                    self.set_reg(op.p2, result);
                    pc += 1;
                }

                // ── LIMIT/OFFSET support ────────────────────────────────
                // DecrJumpZero: decrement register p1; if result is zero
                // jump to p2. If value is initially zero or negative, do nothing.
                // Used to count down remaining LIMIT rows.
                Opcode::DecrJumpZero => {
                    let mut val = self.get_reg(op.p1).to_integer();
                    if val > 0 {
                        val -= 1;
                        self.set_reg_int(op.p1, val);
                        if val == 0 {
                            #[allow(clippy::cast_sign_loss)]
                            {
                                pc = op.p2 as usize;
                            }
                        } else {
                            pc += 1;
                        }
                    } else {
                        pc += 1;
                    }
                }

                // IfPos: if register p1 > 0, subtract p3, then jump to p2.
                // Used for OFFSET counting (skip rows while offset > 0).
                Opcode::IfPos => {
                    let val = self.get_reg(op.p1).to_integer();
                    if val > 0 {
                        let decremented = val - i64::from(op.p3);
                        self.set_reg_int(op.p1, decremented);
                        #[allow(clippy::cast_sign_loss)]
                        {
                            pc = op.p2 as usize;
                        }
                    } else {
                        pc += 1;
                    }
                }

                // ── RowSet operations ──────────────────────────────────
                // Used by OR-optimized queries and IN subqueries.
                Opcode::RowSetAdd => {
                    // Add integer P2 to rowset in register P1.
                    let rowset_reg = op.p1;
                    let val = self.get_reg(op.p2).to_integer();
                    self.ensure_cold_state_for(StatementColdState::ROWSETS)
                        .rowsets
                        .entry_or_insert_with(rowset_reg, RowSet::new)
                        .add(val);
                    pc += 1;
                }

                Opcode::RowSetRead => {
                    // Read next value from rowset P1 into register P3;
                    // jump to P2 when exhausted.
                    let rowset_reg = op.p1;
                    let next_val = self
                        .cold_state_mut()
                        .and_then(|cold_state| cold_state.rowsets.get_mut(&rowset_reg))
                        .and_then(|rs| rs.read_next());
                    match next_val {
                        Some(val) => {
                            self.set_reg_int(op.p3, val);
                            pc += 1;
                        }
                        None => {
                            pc = op.p2 as usize;
                        }
                    }
                }

                Opcode::RowSetTest => {
                    // Test if P3 exists in rowset P1; jump to P2 if found.
                    // If not found, add P3 to the rowset and fall through.
                    let rowset_reg = op.p1;
                    let val = self.get_reg(op.p3).to_integer();
                    let found = self
                        .cold_state()
                        .and_then(|cold_state| cold_state.rowsets.get(&rowset_reg))
                        .is_some_and(|rs| rs.contains(val));
                    if found {
                        pc = op.p2 as usize;
                    } else {
                        self.ensure_cold_state_for(StatementColdState::ROWSETS)
                            .rowsets
                            .entry_or_insert_with(rowset_reg, RowSet::new)
                            .add(val);
                        pc += 1;
                    }
                }

                // ── Foreign Key counters ──────────────────────────────
                Opcode::FkCounter => {
                    // P1=0 → immediate FK counter, P1=1 → deferred.
                    // P2 = delta to add (positive or negative).
                    self.fk_counter += i64::from(op.p2);
                    pc += 1;
                }

                Opcode::FkIfZero => {
                    // Jump to P2 if FK counter is zero.
                    // P1=0 → immediate, P1=1 → deferred.
                    if self.fk_counter == 0 {
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                // ── MemMax: P2 = max(P2, P1) ─────────────────────────
                Opcode::MemMax => {
                    let val1 = self.get_reg(op.p1).to_integer();
                    let val2 = self.get_reg(op.p2).to_integer();
                    if val1 > val2 {
                        self.set_reg_int(op.p2, val1);
                    }
                    pc += 1;
                }

                // ── OffsetLimit ───────────────────────────────────────
                // Compute the combined LIMIT+OFFSET value.
                // P1 = LIMIT, P2 = OFFSET output register,
                // P3 = combined output register.
                // If LIMIT is negative (no limit), store -1 in P3.
                // Otherwise store LIMIT+OFFSET in P3.
                Opcode::OffsetLimit => {
                    let limit = self.get_reg(op.p1).to_integer();
                    let offset = self.get_reg(op.p2).to_integer();
                    let combined = if limit < 0 {
                        -1
                    } else {
                        limit.saturating_add(offset)
                    };
                    self.set_reg_int(op.p3, combined);
                    pc += 1;
                }

                // ── IfNotZero: jump if P1 != 0, decrement by 1 ───────
                Opcode::IfNotZero => {
                    let val = self.get_reg(op.p1).to_integer();
                    if val != 0 {
                        self.set_reg_int(op.p1, val.wrapping_sub(1));
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                // ── Page info ────────────────────────────────────────
                Opcode::Pagecount => {
                    // Store the total page count of database P1 into register P2.
                    // In memory mode, approximate as number of tables.
                    let count = self.db.as_ref().map_or(0, |db| db.table_count());
                    self.set_reg(op.p2, SqliteValue::Integer(i64::from(count)));
                    pc += 1;
                }

                Opcode::MaxPgcnt => {
                    // Return/set max page count. For now, return a large value.
                    self.set_reg(op.p2, SqliteValue::Integer(1_073_741_823));
                    pc += 1;
                }

                // ── Journal mode ─────────────────────────────────────
                Opcode::JournalMode => {
                    // Return current journal mode as text in register P2.
                    // FrankenSQLite defaults to WAL mode.
                    self.set_reg(op.p2, SqliteValue::Text(SmallText::new("wal")));
                    pc += 1;
                }

                // ── Vacuum ───────────────────────────────────────────
                Opcode::Vacuum | Opcode::IncrVacuum => {
                    // In the in-memory engine, vacuum is a no-op.
                    // IncrVacuum: jump to P2 when done (always done immediately).
                    if op.opcode == Opcode::IncrVacuum {
                        pc = op.p2 as usize;
                    } else {
                        pc += 1;
                    }
                }

                // ── Integrity check ──────────────────────────────────
                Opcode::IntegrityCk => {
                    // Run integrity check. For now, always report OK.
                    // P1 = root page register, P2 = output register,
                    // P3 = number of tables to check.
                    self.set_reg(op.p2, SqliteValue::Text(SmallText::new("ok")));
                    pc += 1;
                }

                // ── Expire ───────────────────────────────────────────
                Opcode::Expire => {
                    // Mark prepared statement as expired (no-op; we don't
                    // cache prepared statements yet).
                    pc += 1;
                }

                // ── Cursor lock/unlock ───────────────────────────────
                Opcode::CursorLock | Opcode::CursorUnlock => {
                    // Advisory cursor locking. No-op in single-process mode.
                    pc += 1;
                }

                // ── Subtype operations ───────────────────────────────
                // Subtypes tag registers with metadata (e.g. JSON
                // subtype 74/'J') without changing the stored value.
                Opcode::ClrSubtype => {
                    // Clear subtype flag on register P1.
                    self.clear_register_subtype(op.p1);
                    pc += 1;
                }

                Opcode::GetSubtype => {
                    // Store the subtype of register P1 into register P2.
                    // Returns 0 if no subtype is set.
                    let st = self.register_subtype(op.p1).unwrap_or(0);
                    #[allow(clippy::cast_possible_wrap)]
                    self.set_reg(op.p2, SqliteValue::Integer(st as i64));
                    pc += 1;
                }

                Opcode::SetSubtype => {
                    // Set the subtype of register P2 from the integer
                    // value in register P1.
                    let val = self.get_reg(op.p1);
                    #[allow(clippy::cast_sign_loss)]
                    let st = match val {
                        SqliteValue::Integer(i) => *i as u32,
                        _ => 0,
                    };
                    self.set_register_subtype(op.p2, st);
                    pc += 1;
                }

                // ── Bloom filter ─────────────────────────────────────
                // Bloom filters provide early rejection during index
                // lookups. P1 is the filter register, P3 the hash key
                // register, P2 the jump target (for Filter).
                Opcode::FilterAdd => {
                    // Add hash of register P3 to the Bloom filter
                    // identified by P1.
                    let hash = bloom_hash(self.get_reg(op.p3));
                    let filter = self
                        .ensure_cold_state_for(StatementColdState::BLOOM_FILTERS)
                        .bloom_filters
                        .entry(op.p1)
                        .or_insert_with(|| vec![0u64; BLOOM_FILTER_WORDS]);
                    let bit = (hash as usize) % (filter.len() * 64);
                    filter[bit / 64] |= 1u64 << (bit % 64);
                    pc += 1;
                }

                Opcode::Filter => {
                    // Test Bloom filter P1 for register P3's hash.
                    // Jump to P2 if definitely not present.
                    if let Some(filter) = self
                        .cold_state()
                        .and_then(|cold_state| cold_state.bloom_filters.get(&op.p1))
                    {
                        let hash = bloom_hash(self.get_reg(op.p3));
                        let bit = (hash as usize) % (filter.len() * 64);
                        let present = (filter[bit / 64] >> (bit % 64)) & 1 == 1;
                        if !present {
                            pc = op.p2 as usize;
                        } else {
                            pc += 1;
                        }
                    } else {
                        // No filter exists — conservatively fall through.
                        pc += 1;
                    }
                }

                // ── Hints & debug ────────────────────────────────────
                Opcode::CursorHint | Opcode::Trace | Opcode::Abortable | Opcode::ReleaseReg => {
                    // Advisory/debug opcodes. No-op.
                    pc += 1;
                }

                // ── Virtual Table opcodes ───────────────────────────────
                Opcode::VOpen => {
                    // Open a virtual table cursor.
                    // P1 = cursor number.
                    // If a vtab instance is registered, call open_cursor().
                    let cursor_id = op.p1;
                    if !self
                        .cold_state()
                        .is_some_and(|cold_state| cold_state.vtab_cursors.contains_key(&cursor_id))
                    {
                        if let Some(vtab) = self.vtab_instances.get(&cursor_id) {
                            match vtab.open_cursor() {
                                Ok(cursor) => {
                                    self.register_vtab_cursor(cursor_id, cursor);
                                }
                                Err(e) => {
                                    break vtab_exec_outcome("VOpen", e)?;
                                }
                            }
                        }
                    }
                    pc += 1;
                }

                Opcode::VFilter => {
                    // Apply filter to virtual table cursor and begin scan.
                    // P1 = cursor number
                    // P2 = jump address if cursor is empty after filter
                    // P3 = register holding idx_num; P3+1 = argc; P3+2.. = argv
                    // P4 = optional idx_str
                    let cursor_id = op.p1;
                    let jump_if_empty = op.p2;
                    let cx = self.derive_execution_cx();
                    let idx_num_reg = self.get_reg(op.p3).to_integer();
                    #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
                    let idx_num = idx_num_reg as i32;
                    let argc = self.get_reg(op.p3 + 1).to_integer();
                    let n_args = usize::try_from(argc).unwrap_or(0);
                    let args: Vec<SqliteValue> = (0..n_args)
                        .map(|i| {
                            #[allow(clippy::cast_possible_wrap)]
                            self.registers
                                .get((op.p3 + 2 + i as i32) as usize)
                                .cloned()
                                .unwrap_or(SqliteValue::Null)
                        })
                        .collect();
                    let idx_str = match &op.p4 {
                        P4::Str(s) => Some(s.as_str()),
                        _ => None,
                    };

                    if let Some(state) = self
                        .cold_state_mut()
                        .and_then(|cold_state| cold_state.vtab_cursors.get_mut(&cursor_id))
                    {
                        observe_execution_cancellation(&cx)?;
                        if let Err(e) = state.cursor.filter(&cx, idx_num, idx_str, &args) {
                            break vtab_exec_outcome("VFilter", e)?;
                        }
                        state.null_row = false;
                        observe_execution_cancellation(&cx)?;
                        if state.cursor.eof() {
                            #[allow(clippy::cast_sign_loss)]
                            {
                                pc = jump_if_empty as usize;
                            }
                            continue;
                        }
                    } else {
                        #[allow(clippy::cast_sign_loss)]
                        {
                            pc = jump_if_empty as usize;
                        }
                        continue;
                    }
                    pc += 1;
                }

                Opcode::VColumn => {
                    // Read column from virtual table cursor.
                    // P1 = cursor number
                    // P2 = column index
                    // P3 = destination register
                    let cursor_id = op.p1;
                    let col = op.p2;
                    let dest = op.p3;

                    let column_value = if let Some(state) = self
                        .cold_state()
                        .and_then(|cold_state| cold_state.vtab_cursors.get(&cursor_id))
                    {
                        if state.null_row || state.cursor.eof() {
                            SqliteValue::Null
                        } else {
                            observe_execution_cancellation(&self.execution_cx)?;
                            let mut ctx = ColumnContext::new();
                            if let Err(e) = state.cursor.column(&mut ctx, col) {
                                break vtab_exec_outcome("VColumn", e)?;
                            }
                            observe_execution_cancellation(&self.execution_cx)?;
                            ctx.take_value().unwrap_or(SqliteValue::Null)
                        }
                    } else {
                        SqliteValue::Null
                    };
                    self.set_reg(dest, column_value);
                    pc += 1;
                }

                Opcode::VNext => {
                    // Advance virtual table cursor to the next row.
                    // P1 = cursor number
                    // P2 = jump address to loop body (go back if not eof)
                    let cursor_id = op.p1;
                    let jump_if_more = op.p2;
                    let cx = self.derive_execution_cx();

                    if let Some(state) = self
                        .cold_state_mut()
                        .and_then(|cold_state| cold_state.vtab_cursors.get_mut(&cursor_id))
                    {
                        if state.null_row {
                            pc += 1;
                            continue;
                        }
                        observe_execution_cancellation(&cx)?;
                        if let Err(e) = state.cursor.next(&cx) {
                            break vtab_exec_outcome("VNext", e)?;
                        }
                        observe_execution_cancellation(&cx)?;
                        if !state.cursor.eof() {
                            #[allow(clippy::cast_sign_loss)]
                            {
                                pc = jump_if_more as usize;
                            }
                            continue;
                        }
                    }
                    pc += 1;
                }

                Opcode::VUpdate => {
                    // INSERT/UPDATE/DELETE on a virtual table.
                    // P1 = cursor number, P2 = arg count, P3 = first arg reg
                    let cursor_id = op.p1;
                    let n_args = op.p2;
                    let first_reg = op.p3;
                    let dest_reg = op.p5 as i32;
                    let cx = self.derive_execution_cx();
                    #[allow(clippy::cast_sign_loss)]
                    let args: Vec<SqliteValue> = (0..n_args)
                        .map(|i| {
                            self.registers
                                .get((first_reg + i) as usize)
                                .cloned()
                                .unwrap_or(SqliteValue::Null)
                        })
                        .collect();
                    observe_execution_cancellation(&cx)?;
                    let vtab_update_result =
                        if let Some(vtab) = self.vtab_instances.get_mut(&cursor_id) {
                            match vtab.vtab_update(&cx, &args) {
                                Ok(Some(rowid)) => SqliteValue::Integer(rowid),
                                Ok(None) => SqliteValue::Null,
                                Err(e) => {
                                    break vtab_exec_outcome("VUpdate", e)?;
                                }
                            }
                        } else {
                            SqliteValue::Null
                        };
                    observe_execution_cancellation(&cx)?;
                    #[allow(clippy::cast_sign_loss)]
                    if let Some(reg) = self.registers.get_mut(dest_reg as usize) {
                        *reg = vtab_update_result;
                    }
                    pc += 1;
                }

                Opcode::VBegin => {
                    // Begin a virtual table transaction.
                    // P1 = cursor number identifying the vtab instance.
                    let cursor_id = op.p1;
                    let cx = self.derive_execution_cx();
                    observe_execution_cancellation(&cx)?;
                    if let Some(vtab) = self.vtab_instances.get_mut(&cursor_id) {
                        if let Err(e) = vtab.begin(&cx) {
                            break vtab_exec_outcome("VBegin", e)?;
                        }
                    }
                    observe_execution_cancellation(&cx)?;
                    pc += 1;
                }

                Opcode::VCreate => {
                    // Create a virtual table — handled at Connection layer.
                    pc += 1;
                }

                Opcode::VDestroy => {
                    // Destroy a virtual table — handled at Connection layer.
                    pc += 1;
                }

                Opcode::VCheck => {
                    // Check virtual table integrity.
                    // P2 = destination register for error message (NULL if OK).
                    // P3 carries the integer xIntegrity() argument and must
                    // not be clobbered by this stub implementation.
                    let dest_reg = op.p2;
                    #[allow(clippy::cast_sign_loss)]
                    if let Some(reg) = self.registers.get_mut(dest_reg as usize) {
                        *reg = SqliteValue::Null;
                    }
                    pc += 1;
                }

                Opcode::VInitIn => {
                    // Initialize IN constraint for virtual table.
                    // P2 = register containing the IN value list
                    // P3 = destination register
                    let _cursor_id = op.p1;
                    let src_reg = op.p2;
                    let dest_reg = op.p3;
                    #[allow(clippy::cast_sign_loss)]
                    {
                        let val = self
                            .registers
                            .get(src_reg as usize)
                            .cloned()
                            .unwrap_or(SqliteValue::Null);
                        if let Some(reg) = self.registers.get_mut(dest_reg as usize) {
                            *reg = val;
                        }
                    }
                    pc += 1;
                }

                Opcode::VRename => {
                    // Rename a virtual table.
                    // P1 = cursor number for the vtab instance
                    // P4 = new table name (via P4::Str)
                    let cursor_id = op.p1;
                    let cx = self.derive_execution_cx();
                    observe_execution_cancellation(&cx)?;
                    if let Some(vtab) = self.vtab_instances.get_mut(&cursor_id) {
                        let new_name = match &op.p4 {
                            P4::Str(s) => s.as_str(),
                            _ => "",
                        };
                        if let Err(e) = vtab.rename(&cx, new_name) {
                            break vtab_exec_outcome("VRename", e)?;
                        }
                    }
                    observe_execution_cancellation(&cx)?;
                    pc += 1;
                }

                // ── Catch-all for future opcodes ─────────────────────
                #[allow(unreachable_patterns)]
                _ => {
                    break ExecOutcome::Error {
                        code: 1,
                        message: format!("unimplemented opcode {:?} at pc={}", op.opcode, pc),
                    };
                }
            }
        };

        // ── Post-execution metrics and tracing (bd-1rw.1) ──────────────────
        if !needs_statement_timing {
            return Ok(outcome);
        }

        let elapsed = start_time
            .expect("statement timing state exists when post-execution bookkeeping is enabled")
            .elapsed();
        let elapsed_us = elapsed.as_micros();
        let result_rows = self.results.len();

        if collect_vdbe_metrics {
            let local_opcode_execution_totals = local_opcode_execution_totals
                .as_deref()
                .expect("opcode metrics buffer exists when VDBE metrics are enabled");
            FSQLITE_VDBE_OPCODES_EXECUTED_TOTAL.fetch_add(opcode_count, AtomicOrdering::Relaxed);
            FSQLITE_VDBE_STATEMENTS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
            #[allow(clippy::cast_possible_truncation)]
            FSQLITE_VDBE_STATEMENT_DURATION_US_TOTAL
                .fetch_add(elapsed_us as u64, AtomicOrdering::Relaxed);
            for (idx, total) in local_opcode_execution_totals.iter().enumerate().skip(1) {
                if *total == 0 {
                    continue;
                }
                FSQLITE_VDBE_OPCODE_EXECUTION_TOTALS[idx]
                    .fetch_add(*total, AtomicOrdering::Relaxed);
            }
        }

        let log_statement_done = || {
            if statement_debug_enabled {
                tracing::debug!(
                    target: "fsqlite_vdbe::statement",
                    program_id,
                    opcode_count,
                    result_rows,
                    elapsed_us = elapsed_us as u64,
                    outcome = ?outcome,
                    "vdbe statement done",
                );
            }

            if slow_query_info_enabled && elapsed.as_millis() >= SLOW_QUERY_THRESHOLD_MS {
                #[allow(clippy::cast_possible_truncation)]
                let millis = elapsed.as_millis() as u64;
                tracing::info!(
                    target: "fsqlite_vdbe::slow_query",
                    program_id,
                    opcode_count,
                    result_rows,
                    elapsed_ms = millis,
                    "slow vdbe statement",
                );
            }
        };

        if exec_info_enabled {
            let span = tracing::info_span!(
                target: "fsqlite_vdbe",
                "vdbe_exec",
                opcode_count,
                program_id,
                result_rows,
                elapsed_us = elapsed_us as u64,
            );
            let _guard = span.enter();
            log_statement_done();
        } else {
            log_statement_done();
        }

        Ok(outcome)
    }

    /// Get the collected result rows.
    pub fn results(&self) -> &[smallvec::SmallVec<[SqliteValue; 16]>] {
        &self.results
    }

    /// Take the result rows, consuming them.
    pub fn take_results(&mut self) -> Vec<smallvec::SmallVec<[SqliteValue; 16]>> {
        let mut results = Vec::with_capacity(self.results.capacity());
        std::mem::swap(&mut results, &mut self.results);
        results
    }

    /// Take exactly one result row without replacing the backing results buffer.
    pub fn take_exactly_one_result_row(&mut self) -> ExactResultRowOutcome {
        match self.results.len() {
            0 => ExactResultRowOutcome::NoRows,
            1 => ExactResultRowOutcome::Row(Box::new(
                self.results
                    .pop()
                    .expect("one-row result set should contain one row"),
            )),
            _ => {
                self.results.clear();
                ExactResultRowOutcome::MultipleRows
            }
        }
    }

    #[cfg(test)]
    fn result_buffer_capacity(&self) -> usize {
        self.results.capacity()
    }

    // ── Helpers ─────────────────────────────────────────────────────────

    /// Borrow a register cell without changing ownership.
    ///
    /// Opcode implementations use this for pure read paths where the register
    /// must remain intact after the operation.
    #[inline(always)]
    #[allow(clippy::inline_always)]
    fn get_reg(&self, r: i32) -> &SqliteValue {
        if r >= 0 && (r as usize) < self.registers.len() {
            &self.registers[r as usize]
        } else {
            &SqliteValue::Null
        }
    }

    fn reg_with_offset(start: i32, offset: usize) -> Option<i32> {
        i32::try_from(offset)
            .ok()
            .and_then(|delta| start.checked_add(delta))
    }

    fn register_range_contains(start: i32, count: usize, register: i32) -> bool {
        if count == 0 {
            return false;
        }
        let Ok(count) = i64::try_from(count) else {
            return true;
        };
        let start = i64::from(start);
        let register = i64::from(register);
        register >= start && register < start + count
    }

    #[inline]
    fn invalidate_make_record_sideband_if_overwritten(&mut self, r: i32) {
        self.make_record_lookaside.clear_sideband_for_register(r);
    }

    #[inline]
    #[allow(clippy::cast_sign_loss)]
    fn materialize_make_record_sideband(&mut self, r: i32) {
        if !self.make_record_lookaside.sideband_is_armed_for(r) {
            return;
        }
        if !(0..=65535).contains(&r) {
            self.make_record_lookaside.reset();
            return;
        }
        let idx = r as usize;
        if idx >= self.registers.len() {
            self.registers.resize(idx + 1, SqliteValue::Null);
        }
        self.clear_register_subtype(r);
        #[cfg(test)]
        FSQLITE_VDBE_MAKE_RECORD_SIDEBAND_MATERIALIZATIONS_TOTAL.with(|counter| {
            counter.set(counter.get().saturating_add(1));
        });
        let buf = self.make_record_lookaside.take_buf();
        self.replace_register_value(idx, SqliteValue::Blob(Arc::<[u8]>::from(buf)));
        self.make_record_lookaside.disarm();
    }

    #[inline]
    fn replace_register_value(&mut self, idx: usize, value: SqliteValue) {
        let old_value = std::mem::replace(&mut self.registers[idx], value);
        pool_return_reusable(old_value);
    }

    /// Materialize an owned copy of a register while preserving the source.
    ///
    /// This is the helper to use when a downstream routine needs an owned
    /// `SqliteValue` but the register file should keep its current contents.
    #[inline]
    fn clone_reg_materialized(&mut self, r: i32) -> SqliteValue {
        self.materialize_make_record_sideband(r);
        self.get_reg(r).clone()
    }

    /// Explicit hot-opcode fast lane for the main interpreter loop.
    ///
    /// We keep direct enum dispatch instead of switching to indexed
    /// function-pointer dispatch because indirect branches inhibit inlining
    /// and usually lose to the compiler's jump-table lowering for a dense
    /// opcode enum.
    #[inline(always)]
    fn try_execute_hot_opcode(
        &mut self,
        op: &VdbeOp,
        pc: &mut usize,
        collect_vdbe_metrics: bool,
        row_handler: &mut Option<&mut ResultRowCallback<'_>>,
        borrowed_bindings: Option<&[SqliteValue]>,
    ) -> Result<bool> {
        match op.opcode {
            Opcode::Column => {
                self.execute_column_hot(op)?;
                *pc += 1;
                Ok(true)
            }
            Opcode::ColumnSubstrPrefix => {
                self.execute_column_substr_prefix_hot(op)?;
                *pc += 1;
                Ok(true)
            }
            Opcode::ResultRow => {
                self.execute_result_row_hot(op, collect_vdbe_metrics, row_handler)?;
                *pc += 1;
                Ok(true)
            }
            Opcode::Eq | Opcode::Ne | Opcode::Lt | Opcode::Le | Opcode::Gt | Opcode::Ge => {
                self.execute_comparison_jump_hot(op, pc)?;
                Ok(true)
            }
            Opcode::MakeRecord => {
                self.execute_make_record_hot(op, collect_vdbe_metrics);
                *pc += 1;
                Ok(true)
            }
            // Hot-path additions: Integer/Goto/Null are among the most frequent
            // opcodes in INSERT/UPDATE/DELETE programs. Handling them here avoids
            // falling through to the 190-arm match statement (~5-10ns saved per
            // occurrence from better branch prediction and smaller code footprint).
            Opcode::Integer => {
                self.set_reg_int(op.p2, i64::from(op.p1));
                *pc += 1;
                Ok(true)
            }
            Opcode::Int64 => {
                let val = match &op.p4 {
                    P4::Int64(v) => *v,
                    _ => 0,
                };
                self.set_reg_int(op.p2, val);
                *pc += 1;
                Ok(true)
            }
            Opcode::Variable => {
                let idx = usize::try_from(op.p1)
                    .ok()
                    .and_then(|one_based| one_based.checked_sub(1));
                let value = idx
                    .and_then(|idx| {
                        borrowed_bindings
                            .and_then(|bindings| bindings.get(idx))
                            .or_else(|| self.bindings.get(idx))
                    })
                    .cloned()
                    .unwrap_or(SqliteValue::Null);
                self.set_reg_fast(op.p2, value);
                *pc += 1;
                Ok(true)
            }
            Opcode::Goto => {
                *pc = op.p2 as usize;
                Ok(true)
            }
            Opcode::Noop => {
                *pc += 1;
                Ok(true)
            }
            Opcode::Null => {
                // p2 = target register, p3 = end register (if range fill)
                let target = op.p2;
                if op.p3 > 0 {
                    for reg in target..=op.p3 {
                        self.set_reg(reg, SqliteValue::Null);
                    }
                } else {
                    self.set_reg(target, SqliteValue::Null);
                }
                *pc += 1;
                Ok(true)
            }
            Opcode::SoftNull => {
                self.set_reg_fast(op.p1, SqliteValue::Null);
                *pc += 1;
                Ok(true)
            }
            // AddImm is the canonical counter/accumulator increment, emitted
            // in row counters, aggregate folding, recursive-CTE step counters,
            // and conformance-test hot loops. Nearly every execution finds an
            // Integer already in the target register, so the work is a read +
            // `wrapping_add` + integer write — promoting it out of the 190-arm
            // main match shortens the per-op instruction window meaningfully.
            Opcode::AddImm => {
                let val = self
                    .get_reg(op.p1)
                    .to_integer()
                    .wrapping_add(i64::from(op.p2));
                self.set_reg_int(op.p1, val);
                *pc += 1;
                Ok(true)
            }
            Opcode::Add => {
                let a = self.get_reg(op.p2);
                let b = self.get_reg(op.p1);
                let result = a.sql_add(b);
                self.set_reg_fast(op.p3, result);
                *pc += 1;
                Ok(true)
            }
            Opcode::Subtract => {
                let a = self.get_reg(op.p2);
                let b = self.get_reg(op.p1);
                let result = a.sql_sub(b);
                self.set_reg_fast(op.p3, result);
                *pc += 1;
                Ok(true)
            }
            Opcode::Multiply => {
                let a = self.get_reg(op.p2);
                let b = self.get_reg(op.p1);
                let result = a.sql_mul(b);
                self.set_reg_fast(op.p3, result);
                *pc += 1;
                Ok(true)
            }
            Opcode::Divide => {
                let divisor = self.get_reg(op.p1);
                let dividend = self.get_reg(op.p2);
                let result = sql_div(dividend, divisor);
                self.set_reg_fast(op.p3, result);
                *pc += 1;
                Ok(true)
            }
            Opcode::Remainder => {
                let divisor = self.get_reg(op.p1);
                let dividend = self.get_reg(op.p2);
                let result = sql_rem(dividend, divisor);
                self.set_reg_fast(op.p3, result);
                *pc += 1;
                Ok(true)
            }
            Opcode::BitAnd => {
                let a = self.get_reg(op.p1);
                let b = self.get_reg(op.p2);
                if a.is_null() || b.is_null() {
                    self.set_reg_fast(op.p3, SqliteValue::Null);
                } else {
                    self.set_reg_int(op.p3, a.to_integer() & b.to_integer());
                }
                *pc += 1;
                Ok(true)
            }
            Opcode::BitOr => {
                let a = self.get_reg(op.p1);
                let b = self.get_reg(op.p2);
                if a.is_null() || b.is_null() {
                    self.set_reg_fast(op.p3, SqliteValue::Null);
                } else {
                    self.set_reg_int(op.p3, a.to_integer() | b.to_integer());
                }
                *pc += 1;
                Ok(true)
            }
            Opcode::BitNot => {
                let a = self.get_reg(op.p1);
                if a.is_null() {
                    self.set_reg_fast(op.p2, SqliteValue::Null);
                } else {
                    self.set_reg_int(op.p2, !a.to_integer());
                }
                *pc += 1;
                Ok(true)
            }
            Opcode::ShiftLeft => {
                let a = self.get_reg(op.p1);
                let b = self.get_reg(op.p2);
                if a.is_null() || b.is_null() {
                    self.set_reg_fast(op.p3, SqliteValue::Null);
                } else {
                    let result = sql_shift_left(b.to_integer(), a.to_integer());
                    self.set_reg_fast(op.p3, result);
                }
                *pc += 1;
                Ok(true)
            }
            Opcode::ShiftRight => {
                let a = self.get_reg(op.p1);
                let b = self.get_reg(op.p2);
                if a.is_null() || b.is_null() {
                    self.set_reg_fast(op.p3, SqliteValue::Null);
                } else {
                    let result = sql_shift_right(b.to_integer(), a.to_integer());
                    self.set_reg_fast(op.p3, result);
                }
                *pc += 1;
                Ok(true)
            }
            // Copy is the most frequently emitted register-motion opcode in
            // codegen (71 sites), driving SELECT projection, expression
            // propagation, and materialization staging. p3 is almost always 0
            // (single-register copy); the body is `clone + set_reg_fast`.
            // Keeping the loop form preserves semantics for the rare range
            // copies while leaving the single-reg case a straight line.
            Opcode::Copy => {
                if op.p3 == 0 {
                    let value = self.clone_reg_materialized(op.p1);
                    self.set_reg_fast(op.p2, value);
                } else {
                    self.copy_reg_range(op.p1, op.p2, op.p3);
                }
                *pc += 1;
                Ok(true)
            }
            // SCopy is the shallow single-register copy. A MakeRecord sideband
            // is still the source register's logical value, so materialize it
            // before cloning just like single-register Copy.
            Opcode::SCopy => {
                let val = self.clone_reg_materialized(op.p1);
                self.set_reg_fast(op.p2, val);
                *pc += 1;
                Ok(true)
            }
            Opcode::IntCopy => {
                let val = self.get_reg(op.p1).to_integer();
                self.set_reg_int(op.p2, val);
                *pc += 1;
                Ok(true)
            }
            // Move is emitted for register handoff where the source range is
            // drained. The common p3=1 case skips the range helper's loop and
            // overlap checks; multi-register moves still delegate to
            // `move_reg_range` so overlap handling stays identical to the
            // main-match arm.
            Opcode::Move => {
                if op.p3 == 1 {
                    let value = self.take_reg(op.p1);
                    self.set_reg_fast(op.p2, value);
                } else {
                    let count = usize::try_from(op.p3).unwrap_or(0);
                    self.move_reg_range(op.p1, op.p2, count);
                }
                *pc += 1;
                Ok(true)
            }
            // DecrJumpZero is the canonical LIMIT counter: fires once per
            // emitted result row. Body reads a register as integer,
            // decrements if positive, writes the new value back, and jumps
            // to p2 when the counter reaches zero. Every live DML/OLTP
            // query with a LIMIT clause hits this on every row, so even a
            // few-ns per-op dispatch saving compounds. Mirrors the
            // existing main-match arm verbatim.
            Opcode::DecrJumpZero => {
                let mut val = self.get_reg(op.p1).to_integer();
                if val > 0 {
                    val -= 1;
                    self.set_reg_int(op.p1, val);
                    if val == 0 {
                        #[allow(clippy::cast_sign_loss)]
                        {
                            *pc = op.p2 as usize;
                        }
                    } else {
                        *pc += 1;
                    }
                } else {
                    *pc += 1;
                }
                Ok(true)
            }
            // IfPos is the canonical OFFSET counter: fires once per
            // skipped row in any LIMIT/OFFSET-bounded query (25 codegen
            // sites). Body reads a register as integer; if positive,
            // subtracts p3 and jumps to p2; otherwise falls through.
            // Mirror-structure to DecrJumpZero — same dispatch-shortening
            // logic applies. Body mirrors the existing main-match arm
            // verbatim.
            Opcode::IfPos => {
                let val = self.get_reg(op.p1).to_integer();
                if val > 0 {
                    let decremented = val - i64::from(op.p3);
                    self.set_reg_int(op.p1, decremented);
                    #[allow(clippy::cast_sign_loss)]
                    {
                        *pc = op.p2 as usize;
                    }
                } else {
                    *pc += 1;
                }
                Ok(true)
            }
            // IfNotZero backs countdown loops such as recursive query
            // step limits: read integer register p1, decrement when
            // non-zero, and jump to p2 until it reaches zero. Same
            // small-body branch-counter shape as IfPos/DecrJumpZero.
            Opcode::IfNotZero => {
                let val = self.get_reg(op.p1).to_integer();
                if val != 0 {
                    self.set_reg_int(op.p1, val.wrapping_sub(1));
                    #[allow(clippy::cast_sign_loss)]
                    {
                        *pc = op.p2 as usize;
                    }
                } else {
                    *pc += 1;
                }
                Ok(true)
            }
            // IsNull is the highest-frequency unpromoted opcode in
            // codegen (87 emit sites — more than Goto/Column/Copy/Null
            // individually). It backs every NOT NULL constraint check,
            // every IS NULL / IS NOT NULL WHERE clause, and the
            // null-guarding boilerplate around aggregate folding,
            // CASE/COALESCE evaluation, and JOIN match probing. Body
            // is a single `is_null` read + branch — the smallest
            // possible body shape, where dispatch routing cost
            // dominates work cost. Mirrors the existing main-match
            // arm verbatim.
            Opcode::IsNull => {
                if self.get_reg(op.p1).is_null() {
                    #[allow(clippy::cast_sign_loss)]
                    {
                        *pc = op.p2 as usize;
                    }
                } else {
                    *pc += 1;
                }
                Ok(true)
            }
            // IfNot is the canonical falsy-branch jump: emitted ~48
            // production sites driving CASE/COALESCE WHEN-fallthrough,
            // AND-short-circuit, LIMIT-zero detection, HAVING-skip,
            // and the second arm of every truthiness probe.  Same
            // family as the already-promoted IsNull/IfPos/DecrJumpZero
            // — small body, high call frequency, dispatch routing
            // cost dominates work cost.  Body mirrors the existing
            // main-match arm verbatim, including the C-SQLite
            // null-as-jump-iff-p3 semantics.
            Opcode::IfNot => {
                let val = self.get_reg(op.p1);
                let should_jump = if val.is_null() {
                    op.p3 != 0
                } else {
                    !vdbe_real_is_truthy(val)
                };
                if should_jump {
                    #[allow(clippy::cast_sign_loss)]
                    {
                        *pc = op.p2 as usize;
                    }
                } else {
                    *pc += 1;
                }
                Ok(true)
            }
            Opcode::IsTrue => {
                let val = self.get_reg(op.p1);
                let p4_val = match &op.p4 {
                    P4::Int(n) => *n,
                    _ => 0,
                };
                if val.is_null() {
                    self.set_reg_int(op.p2, i64::from(op.p3 ^ p4_val));
                } else {
                    let v = i32::from(vdbe_real_is_truthy(val));
                    self.set_reg_int(op.p2, i64::from((v ^ p4_val) & 1));
                }
                *pc += 1;
                Ok(true)
            }
            Opcode::ZeroOrNull => {
                if self.get_reg(op.p1).is_null() || self.get_reg(op.p3).is_null() {
                    self.set_reg_fast(op.p2, SqliteValue::Null);
                } else {
                    self.set_reg_int(op.p2, 0);
                }
                *pc += 1;
                Ok(true)
            }
            // Not is a compact expression-evaluation opcode: read p1, apply
            // SQLite truthiness, and write a boolean/null result into p2. Keep
            // the body byte-equivalent to the main-match arm while avoiding
            // the large dispatch table for repeated boolean projections.
            Opcode::Not => {
                let val = self.get_reg(op.p1);
                if val.is_null() {
                    self.set_reg_fast(op.p2, SqliteValue::Null);
                } else {
                    self.set_reg_int(op.p2, i64::from(!vdbe_real_is_truthy(val)));
                }
                *pc += 1;
                Ok(true)
            }
            // Rowid extracts the rowid of the currently-positioned row on
            // cursor p1 into register p2.  Emitted at ~51 codegen sites
            // across fsqlite-vdbe/-planner/-core, driving every JOIN
            // match-probe rowid lookup, every UPDATE/DELETE row-key read,
            // every INSERT-returning-rowid path, every rowid-as-column
            // projection, and the by-rowid aggregate fast paths.  Body
            // mirrors the existing main-match arm verbatim — single
            // `cursor_rowid` call (one HashMap probe in the common
            // storage-cursor path, one cursor.rowid call) plus a register
            // write.  Same family as the already-promoted IsNull/IfPos
            // /IfNot/SCopy: small body, high call frequency, dispatch
            // routing cost dominates work cost.
            Opcode::Rowid => {
                let cursor_id = op.p1;
                let target = op.p2;
                let val = self.cursor_rowid(cursor_id)?;
                self.set_reg_fast(target, val);
                *pc += 1;
                Ok(true)
            }
            // IdxRowid extracts the trailing rowid field from the index-key
            // record at cursor p1's current position into register p2.  Body
            // is byte-identical to Rowid above — both arms route through
            // `cursor_rowid`, which dispatches uniformly across storage
            // cursors (table & index B-trees), legacy in-memory cursors, and
            // virtual-table cursors.  Emitted ~13 production sites in
            // fsqlite-vdbe + fsqlite-planner codegen, firing once per matched
            // index row in every index-driven JOIN, IN-list lookup, covering-
            // index scan converting index entries to table rowids for the
            // subsequent SeekRowid probe, and any DELETE/UPDATE walking an
            // index to find row keys.  Same family as the just-promoted
            // Rowid: small body (HashMap probe + cursor.rowid call), high
            // call frequency along JOIN inner loops, dispatch routing cost
            // is a meaningful fraction of per-op cost.
            Opcode::IdxRowid => {
                let cursor_id = op.p1;
                let target = op.p2;
                let val = self.cursor_rowid(cursor_id)?;
                self.set_reg_fast(target, val);
                *pc += 1;
                Ok(true)
            }
            // bd-perf (V2.1): Fused NewRowid + MakeRecord + Insert for
            // sequential append. Combines 3 opcodes into 1 dispatch.
            // P1=cursor, P2=first_reg, P3=num_cols, P5=insert_flags.
            Opcode::FusedAppendInsert => {
                let cursor_id = op.p1;
                let first_reg = op.p2;
                let num_cols = usize::try_from(op.p3).unwrap_or(0);
                let concurrent_allocator = self.concurrent_rowid_allocator.clone();
                let concurrent_schema_epoch = self.concurrent_rowid_schema_epoch;
                let previous_last_insert_rowid = self.last_insert_rowid;
                let previous_last_insert_rowid_valid = self.last_insert_rowid_valid;

                if let Some(sc) = self.storage_cursors.get_mut(&cursor_id) {
                    if sc.writable {
                        let root_page = sc.root_page;
                        let autoinc_max = sc.autoincrement_high_water;
                        let rowid_mode = sc.rowid_mode;
                        // 1. Allocate rowid (same logic as NewRowid).
                        let rowid = if let Some(allocator) = concurrent_allocator.as_ref() {
                            Self::allocate_concurrent_storage_rowid(
                                allocator,
                                concurrent_schema_epoch,
                                root_page,
                                rowid_mode,
                                autoinc_max,
                                sc,
                                "rowid overflow in FusedAppendInsert",
                            )?
                        } else {
                            Self::allocate_serialized_storage_rowid(
                                sc,
                                autoinc_max,
                                "rowid overflow in FusedAppendInsert",
                            )?
                        };

                        // 2. Serialize record from registers into sideband buf.
                        // The peephole preserves MakeRecord's p4 metadata; keep
                        // this fused path byte-equivalent to OP_MakeRecord so IPK
                        // placeholder columns and fixed record headers do not
                        // silently fall back to the generic encoder.
                        let mut rec_buf = self.make_record_lookaside.take_buf();
                        self.serialize_record_from_register_range(
                            first_reg,
                            num_cols,
                            &op.p4,
                            &mut rec_buf,
                        );

                        // 3. Append to B-tree (prechecked absent — sequential rowid)
                        let append_result =
                            if let Some(sc) = self.storage_cursors.get_mut(&cursor_id) {
                                let result = sc
                                    .cursor
                                    .table_append_after_last_position(&sc.cx, rowid, &rec_buf);
                                if result.is_ok() {
                                    sc.last_successful_insert_rowid = Some(rowid);
                                }
                                result
                            } else {
                                Err(FrankenError::internal(
                                    "cursor disappeared in FusedAppendInsert",
                                ))
                            };

                        // Return buffer for reuse
                        rec_buf.clear();
                        self.make_record_lookaside.replace_buf(rec_buf);
                        append_result?;

                        // 4. Bookkeeping (same as Insert opcode)
                        self.changes += 1;
                        self.last_insert_rowid = rowid;
                        self.last_insert_rowid_valid = true;
                        self.last_insert_cursor_id = Some(cursor_id);
                        self.set_conflict_skip_idx(false);
                        self.set_pending_insert_rollback(Some(PendingInsertRollback {
                            cursor_id,
                            rowid,
                            previous_last_insert_rowid,
                            previous_last_insert_rowid_valid,
                            update_restore: None,
                        }));
                        self.pending_next_after_delete.remove(&cursor_id);
                        // CRITICAL FIX: Mark table dirty so MemDB fast paths
                        // know the data is stale and fall back to pager reads.
                        // Without this, subsequent COUNT(*)/LIKE queries via
                        // MemDB would return stale results.
                        self.mark_storage_root_page_dirty(root_page);
                    } else {
                        return Err(FrankenError::internal(
                            "FusedAppendInsert: cursor is not writable",
                        ));
                    }
                } else {
                    return Err(FrankenError::internal(format!(
                        "FusedAppendInsert: no storage cursor for id {cursor_id}"
                    )));
                }
                *pc += 1;
                Ok(true)
            }
            // IMPL-13: Fused `Integer(p1=lit, p2=reg) + ResultRow(p1=reg, p2=1)`.
            // Emits a single-column result row whose only value is the literal
            // integer `p1`, then clears the source register to mirror the
            // drain semantics of `ResultRow` (`take_reg_range`).
            //
            // Correctness: byte-equivalent to the unfused sequence. The
            // codegen-side peephole pass is responsible for verifying that
            // the `ResultRow` consumes exactly the register written by the
            // preceding `Integer` and emits exactly one column.
            Opcode::FusedLiteralResultRow => {
                let lit = i64::from(op.p1);
                let reg = op.p2;
                // Preserve Integer's write-then-ResultRow-drain side effects:
                // the register ends up cleared after `take_reg_range`.
                self.set_reg_int(reg, lit);

                let should_retain_row = self.collect_result_rows
                    && match self.max_collected_result_rows {
                        Some(limit) => self.results.len() < limit,
                        None => true,
                    };
                if row_handler.is_some() || should_retain_row {
                    let materialize_start = collect_vdbe_metrics.then(Instant::now);
                    // Single-column row: drain the register just like
                    // `take_reg_range(reg, 1)` would.
                    let mut row: smallvec::SmallVec<[SqliteValue; 16]> =
                        smallvec::SmallVec::with_capacity(1);
                    row.push(self.take_reg(reg));
                    if collect_vdbe_metrics {
                        if let Some(materialize_start) = materialize_start {
                            FSQLITE_VDBE_RESULT_ROW_MATERIALIZATION_TIME_NS_TOTAL.fetch_add(
                                u64::try_from(materialize_start.elapsed().as_nanos())
                                    .unwrap_or(u64::MAX),
                                AtomicOrdering::Relaxed,
                            );
                        }
                        record_result_row_metrics(&row);
                    }
                    if let Some(handler) = row_handler.as_mut() {
                        (*handler)(row)?;
                    } else {
                        self.results.push(row);
                    }
                } else {
                    // No retention: still drain the register to match the
                    // unfused ResultRow's `discard_reg_range` behavior.
                    let _ = self.take_reg(reg);
                }
                *pc += 1;
                Ok(true)
            }
            Opcode::String8 => {
                match &op.p4 {
                    P4::Str(s) => self.write_text_to_reg(op.p2, s),
                    _ => self.set_reg_fast(op.p2, SqliteValue::Text(SmallText::new(""))),
                }
                *pc += 1;
                Ok(true)
            }
            Opcode::String => {
                match &op.p4 {
                    P4::Str(s) => self.write_text_to_reg(op.p2, s),
                    _ => self.set_reg_fast(op.p2, SqliteValue::Text(SmallText::new(""))),
                }
                *pc += 1;
                Ok(true)
            }
            Opcode::Real => {
                let val = match &op.p4 {
                    P4::Real(v) => *v,
                    _ => 0.0,
                };
                self.set_reg_fast(op.p2, SqliteValue::Float(val));
                *pc += 1;
                Ok(true)
            }
            Opcode::If => {
                let val = self.get_reg(op.p1);
                let should_jump = if val.is_null() {
                    op.p3 != 0
                } else {
                    vdbe_real_is_truthy(val)
                };
                if should_jump {
                    #[allow(clippy::cast_sign_loss)]
                    {
                        *pc = op.p2 as usize;
                    }
                } else {
                    *pc += 1;
                }
                Ok(true)
            }
            Opcode::Next | Opcode::SorterNext => {
                self.execute_next_hot(op, pc)?;
                Ok(true)
            }
            _ => Ok(false),
        }
    }

    #[inline(always)]
    fn execute_next_hot(&mut self, op: &VdbeOp, pc: &mut usize) -> Result<()> {
        let cursor_id = op.p1;
        let has_next = if !self.pending_next_after_delete.is_empty()
            && self.pending_next_after_delete.remove(&cursor_id)
        {
            if let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) {
                !cursor.cursor.eof()
            } else if let Some(cursor) = self.cursors.get_mut(&cursor_id) {
                if cursor.is_pseudo {
                    false
                } else if let Some(pos) = cursor.position {
                    if let Some(table) = self
                        .db
                        .as_ref()
                        .and_then(|db| db.get_table(cursor.root_page))
                    {
                        if pos < table.rows.len() {
                            true
                        } else {
                            cursor.position = None;
                            false
                        }
                    } else {
                        false
                    }
                } else {
                    false
                }
            } else {
                false
            }
        } else if let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) {
            cursor.cursor.next(&cursor.cx)?
        } else if let Some(sorter) = self.sorters.get_mut(&cursor_id) {
            if let Some(pos) = sorter.position {
                let next = pos + 1;
                if next < sorter.rows.len() {
                    sorter.position = Some(next);
                    true
                } else {
                    sorter.position = None;
                    false
                }
            } else {
                false
            }
        } else if let Some(cursor) = self.cursors.get_mut(&cursor_id) {
            if cursor.is_pseudo {
                false
            } else if let Some(db) = self.db.as_ref() {
                if let Some(table) = db.get_table(cursor.root_page) {
                    if let Some(pos) = cursor.position {
                        let next = pos + 1;
                        if next < table.rows.len() {
                            cursor.position = Some(next);
                            true
                        } else {
                            cursor.position = None;
                            false
                        }
                    } else {
                        false
                    }
                } else {
                    false
                }
            } else {
                false
            }
        } else {
            false
        };
        if has_next {
            #[allow(clippy::cast_sign_loss)]
            {
                *pc = op.p2 as usize;
            }
        } else {
            *pc += 1;
        }
        Ok(())
    }

    #[inline(always)]
    fn execute_column_hot(&mut self, op: &VdbeOp) -> Result<()> {
        let cursor_id = op.p1;
        let col_idx = op.p2 as usize;
        let target = op.p3;
        if !self.column_to_reg_direct(cursor_id, col_idx, target)? {
            let val = self.cursor_column(cursor_id, col_idx)?;
            self.set_reg_fast(target, val);
        }
        Ok(())
    }

    #[inline(always)]
    fn execute_column_substr_prefix_hot(&mut self, op: &VdbeOp) -> Result<()> {
        let Ok(col_idx) = usize::try_from(op.p2) else {
            self.set_reg_fast(op.p3, SqliteValue::Null);
            return Ok(());
        };
        let P4::Int(prefix_len) = &op.p4 else {
            self.set_reg_fast(op.p3, SqliteValue::Null);
            return Ok(());
        };
        let Ok(prefix_len) = usize::try_from(*prefix_len) else {
            self.set_reg_fast(op.p3, SqliteValue::Null);
            return Ok(());
        };

        if let Some(value) = self.column_substr_prefix_direct(op.p1, col_idx, prefix_len)? {
            self.set_reg_fast(op.p3, value);
            return Ok(());
        }

        let value = self.cursor_column(op.p1, col_idx)?;
        self.set_reg_fast(op.p3, sqlite_substr_prefix_value(&value, prefix_len));
        Ok(())
    }

    #[inline(always)]
    fn execute_result_row_hot(
        &mut self,
        op: &VdbeOp,
        collect_vdbe_metrics: bool,
        row_handler: &mut Option<&mut ResultRowCallback<'_>>,
    ) -> Result<()> {
        let count = usize::try_from(op.p2).unwrap_or(0);
        let should_retain_row = self.collect_result_rows
            && match self.max_collected_result_rows {
                Some(limit) => self.results.len() < limit,
                None => true,
            };
        if row_handler.is_some() || should_retain_row {
            let materialize_start = collect_vdbe_metrics.then(Instant::now);
            let row = self.take_reg_range(op.p1, count);
            if collect_vdbe_metrics {
                if let Some(materialize_start) = materialize_start {
                    FSQLITE_VDBE_RESULT_ROW_MATERIALIZATION_TIME_NS_TOTAL.fetch_add(
                        u64::try_from(materialize_start.elapsed().as_nanos()).unwrap_or(u64::MAX),
                        AtomicOrdering::Relaxed,
                    );
                }
                record_result_row_metrics(&row);
            }
            if let Some(handler) = row_handler.as_mut() {
                (*handler)(row)?;
            } else {
                self.results.push(row);
            }
        } else {
            self.discard_reg_range(op.p1, count);
        }
        Ok(())
    }

    fn emit_compiled_result_row(
        &mut self,
        values: &[SqliteValue],
        collect_vdbe_metrics: bool,
        row_handler: &mut Option<&mut ResultRowCallback<'_>>,
    ) -> Result<()> {
        let should_retain_row = self.collect_result_rows
            && match self.max_collected_result_rows {
                Some(limit) => self.results.len() < limit,
                None => true,
            };
        if row_handler.is_some() || should_retain_row {
            let materialize_start = collect_vdbe_metrics.then(Instant::now);
            let row: smallvec::SmallVec<[SqliteValue; 16]> = values.iter().cloned().collect();
            if collect_vdbe_metrics {
                if let Some(materialize_start) = materialize_start {
                    FSQLITE_VDBE_RESULT_ROW_MATERIALIZATION_TIME_NS_TOTAL.fetch_add(
                        u64::try_from(materialize_start.elapsed().as_nanos()).unwrap_or(u64::MAX),
                        AtomicOrdering::Relaxed,
                    );
                }
                record_result_row_metrics(&row);
            }
            if let Some(handler) = row_handler.as_mut() {
                (*handler)(row)?;
            } else {
                self.results.push(row);
            }
        }
        Ok(())
    }

    fn compiled_column_values(
        &self,
        value_sources: &[InsertValueSource],
        borrowed_bindings: Option<&[SqliteValue]>,
        collect_vdbe_metrics: bool,
        affinity: Option<&str>,
    ) -> smallvec::SmallVec<[SqliteValue; 16]> {
        let binding_source = borrowed_bindings.unwrap_or(self.bindings.as_slice());
        let mut values = value_sources
            .iter()
            .map(|source| match source {
                InsertValueSource::Binding(binding_index) => binding_source
                    .get(*binding_index)
                    .cloned()
                    .unwrap_or(SqliteValue::Null),
                InsertValueSource::Constant(value) => value.clone(),
            })
            .collect::<smallvec::SmallVec<[_; 16]>>();

        if let Some(affinity) = affinity {
            for (value, ch) in values.iter_mut().zip(affinity.chars()) {
                let before = value.clone();
                let coerced = value.clone().apply_affinity(char_to_affinity(ch));
                if collect_vdbe_metrics {
                    record_type_coercion(&before, &coerced);
                }
                *value = coerced;
            }
        }

        values
    }

    fn apply_make_record_null_placeholders(values: &mut [SqliteValue], record_p4: &P4) {
        match record_p4 {
            P4::PrecomputedHeader(header) if header.column_count() == values.len() => {
                for (value, slot) in values.iter_mut().zip(&header.slots) {
                    if slot.kind == PrecomputedSerialTypeKind::NullPlaceholder {
                        *value = SqliteValue::Null;
                    }
                }
            }
            P4::Affinity(affinity) => {
                for (value, affinity) in values.iter_mut().zip(affinity.bytes()) {
                    if affinity == b'X' {
                        *value = SqliteValue::Null;
                    }
                }
            }
            _ => {}
        }
    }

    fn execute_compiled_constant_result_row(
        &mut self,
        template: &ConstantResultRowTemplate,
        collect_vdbe_metrics: bool,
        row_handler: &mut Option<&mut ResultRowCallback<'_>>,
    ) -> Result<ExecOutcome> {
        self.emit_compiled_result_row(&template.values, collect_vdbe_metrics, row_handler)?;
        Ok(ExecOutcome::Done)
    }

    fn execute_compiled_simple_insert(
        &mut self,
        template: &SimpleInsertTemplate,
        borrowed_bindings: Option<&[SqliteValue]>,
        collect_vdbe_metrics: bool,
    ) -> Result<ExecOutcome> {
        if (template.insert_flags & 0x0F) != 2 {
            return Err(FrankenError::internal(
                "compiled simple INSERT only supports OE_Abort semantics",
            ));
        }

        if !self.open_storage_cursor(template.cursor_id, template.root_page, true) {
            return Err(FrankenError::internal(format!(
                "compiled simple INSERT could not open writable cursor {} on root {}",
                template.cursor_id, template.root_page
            )));
        }

        let mut values = self.compiled_column_values(
            &template.value_sources,
            borrowed_bindings,
            collect_vdbe_metrics,
            template.affinity.as_deref(),
        );
        Self::apply_make_record_null_placeholders(&mut values, &template.record_p4);
        let concurrent_allocator = self.concurrent_rowid_allocator.clone();
        let concurrent_schema_epoch = self.concurrent_rowid_schema_epoch;
        let previous_last_insert_rowid = self.last_insert_rowid;
        let previous_last_insert_rowid_valid = self.last_insert_rowid_valid;

        let rowid = {
            let sc = self
                .storage_cursors
                .get_mut(&template.cursor_id)
                .ok_or_else(|| FrankenError::internal("compiled simple INSERT lost its cursor"))?;
            if !sc.writable {
                return Err(FrankenError::internal(
                    "compiled simple INSERT cursor is not writable",
                ));
            }
            let autoinc_max = sc.autoincrement_high_water;
            let rowid_mode = sc.rowid_mode;
            let rowid = if let Some(allocator) = concurrent_allocator.as_ref() {
                Self::allocate_concurrent_storage_rowid(
                    allocator,
                    concurrent_schema_epoch,
                    template.root_page,
                    rowid_mode,
                    autoinc_max,
                    sc,
                    "rowid overflow in compiled simple INSERT",
                )?
            } else {
                Self::allocate_serialized_storage_rowid(
                    sc,
                    autoinc_max,
                    "rowid overflow in compiled simple INSERT",
                )?
            };
            rowid
        };
        let record_plan =
            build_compiled_record_write_plan(values.as_slice(), &template.record_builder);
        let payload_len = record_plan.exact_size();
        let appended_directly = if let Some(sc) = self.storage_cursors.get_mut(&template.cursor_id)
        {
            let result = sc.cursor.table_append_after_last_position_with_writer(
                &sc.cx,
                rowid,
                payload_len,
                move |dst| {
                    record_plan.write_into_slice(dst).map_err(|()| {
                        FrankenError::internal(
                            "compiled simple INSERT direct record serialization size mismatch",
                        )
                    })
                },
            )?;
            if result {
                sc.last_successful_insert_rowid = Some(rowid);
            }
            result
        } else {
            return Err(FrankenError::internal(
                "compiled simple INSERT lost its cursor",
            ));
        };
        if !appended_directly {
            let mut payload_buf = self.make_record_lookaside.take_buf();
            serialize_compiled_record_into_vec(
                values.as_slice(),
                &template.record_builder,
                &mut payload_buf,
            );
            let append_result = if let Some(sc) = self.storage_cursors.get_mut(&template.cursor_id)
            {
                let result =
                    sc.cursor
                        .table_append_after_last_position(&sc.cx, rowid, &payload_buf);
                if result.is_ok() {
                    sc.last_successful_insert_rowid = Some(rowid);
                }
                result
            } else {
                Err(FrankenError::internal(
                    "compiled simple INSERT lost its cursor",
                ))
            };
            payload_buf.clear();
            self.make_record_lookaside.replace_buf(payload_buf);
            append_result?;
        }

        self.changes += 1;
        self.last_insert_rowid = rowid;
        self.last_insert_rowid_valid = true;
        self.last_insert_cursor_id = Some(template.cursor_id);
        self.set_conflict_skip_idx(false);
        self.set_pending_insert_rollback(Some(PendingInsertRollback {
            cursor_id: template.cursor_id,
            rowid,
            previous_last_insert_rowid,
            previous_last_insert_rowid_valid,
            update_restore: None,
        }));
        self.clear_pending_idx_entries();
        self.pending_next_after_delete.remove(&template.cursor_id);
        self.mark_storage_root_page_dirty(template.root_page);

        Ok(ExecOutcome::Done)
    }

    fn execute_compiled_rowid_lookup_select(
        &mut self,
        template: &RowidLookupSelectTemplate,
        borrowed_bindings: Option<&[SqliteValue]>,
        collect_vdbe_metrics: bool,
        row_handler: &mut Option<&mut ResultRowCallback<'_>>,
    ) -> Result<ExecOutcome> {
        if !self.open_storage_cursor(template.cursor_id, template.root_page, false) {
            return Err(FrankenError::internal(format!(
                "compiled rowid-lookup SELECT could not open cursor {} on root {}",
                template.cursor_id, template.root_page
            )));
        }

        let rowid_val = match &template.rowid_source {
            InsertValueSource::Binding(idx) => {
                let bindings = borrowed_bindings.ok_or_else(|| {
                    FrankenError::internal("compiled rowid-lookup SELECT: missing bindings")
                })?;
                bindings
                    .get(*idx)
                    .cloned()
                    .unwrap_or(SqliteValue::Null)
                    .to_integer()
            }
            InsertValueSource::Constant(v) => v.to_integer(),
        };

        let found = {
            let sc = self
                .storage_cursors
                .get_mut(&template.cursor_id)
                .ok_or_else(|| {
                    FrankenError::internal("compiled rowid-lookup SELECT lost its cursor")
                })?;
            sc.cursor.table_move_to(&sc.cx, rowid_val)?.is_found()
        };

        if found {
            let mut values = Vec::with_capacity(template.column_indices.len());
            for &col_idx in &template.column_indices {
                let val = self.cursor_column(template.cursor_id, col_idx as usize)?;
                values.push(val);
            }
            self.emit_compiled_result_row(&values, collect_vdbe_metrics, row_handler)?;
        }

        self.storage_cursors.remove(&template.cursor_id);
        Ok(ExecOutcome::Done)
    }

    fn execute_compiled_full_scan_select(
        &mut self,
        template: &FullScanSelectTemplate,
        collect_vdbe_metrics: bool,
        row_handler: &mut Option<&mut ResultRowCallback<'_>>,
    ) -> Result<ExecOutcome> {
        if !self.open_storage_cursor(template.cursor_id, template.root_page, false) {
            return Err(FrankenError::internal(format!(
                "compiled full-scan SELECT could not open cursor {} on root {}",
                template.cursor_id, template.root_page
            )));
        }

        let has_rows = {
            let sc = self
                .storage_cursors
                .get_mut(&template.cursor_id)
                .ok_or_else(|| {
                    FrankenError::internal("compiled full-scan SELECT lost its cursor")
                })?;
            sc.cursor.first(&sc.cx)?
        };

        if has_rows {
            loop {
                let mut values = Vec::with_capacity(template.column_indices.len());
                for &col_idx in &template.column_indices {
                    let val = self.cursor_column(template.cursor_id, col_idx as usize)?;
                    values.push(val);
                }
                self.emit_compiled_result_row(&values, collect_vdbe_metrics, row_handler)?;

                let more = {
                    let sc = self
                        .storage_cursors
                        .get_mut(&template.cursor_id)
                        .ok_or_else(|| {
                            FrankenError::internal("compiled full-scan SELECT lost cursor mid-scan")
                        })?;
                    sc.cursor.next(&sc.cx)?
                };
                if !more {
                    break;
                }
            }
        }

        self.storage_cursors.remove(&template.cursor_id);
        Ok(ExecOutcome::Done)
    }

    fn execute_compiled_program(
        &mut self,
        compiled_program: &CompiledProgram,
        borrowed_bindings: Option<&[SqliteValue]>,
        collect_vdbe_metrics: bool,
        row_handler: &mut Option<&mut ResultRowCallback<'_>>,
    ) -> Result<ExecOutcome> {
        match compiled_program {
            CompiledProgram::ConstantResultRow(template) => self
                .execute_compiled_constant_result_row(template, collect_vdbe_metrics, row_handler),
            CompiledProgram::SimpleInsert(template) => self.execute_compiled_simple_insert(
                template,
                borrowed_bindings,
                collect_vdbe_metrics,
            ),
            CompiledProgram::RowidLookupSelect(template) => self
                .execute_compiled_rowid_lookup_select(
                    template,
                    borrowed_bindings,
                    collect_vdbe_metrics,
                    row_handler,
                ),
            CompiledProgram::FullScanSelect(template) => {
                self.execute_compiled_full_scan_select(template, collect_vdbe_metrics, row_handler)
            }
        }
    }

    #[inline(always)]
    fn execute_comparison_jump_hot(&mut self, op: &VdbeOp, pc: &mut usize) -> Result<()> {
        let lhs = self.get_reg(op.p3);
        let rhs = self.get_reg(op.p1);
        let store_p2 = (op.p5 & 0x20) != 0; // SQLITE_STOREP2

        if lhs.is_null() || rhs.is_null() {
            let null_eq = (op.p5 & 0x80) != 0;
            if null_eq {
                let both_null = lhs.is_null() && rhs.is_null();
                let should_jump = match op.opcode {
                    Opcode::Eq => both_null,
                    Opcode::Ne => !both_null,
                    _ => false,
                };
                if store_p2 {
                    self.set_reg_int(op.p2, i64::from(should_jump));
                    *pc += 1;
                } else if should_jump {
                    *pc = op.p2 as usize;
                } else {
                    *pc += 1;
                }
            } else if store_p2 {
                self.set_reg_fast(op.p2, SqliteValue::Null);
                *pc += 1;
            } else if (op.p5 & 0x10) != 0 {
                *pc = op.p2 as usize;
            } else {
                *pc += 1;
            }
            return Ok(());
        }

        let cmp = if let Some(cmp) = fast_compare_same_storage_class(lhs, rhs, &op.p4) {
            cmp
        } else {
            let (cmp_lhs, cmp_rhs) = coerce_for_comparison(lhs, rhs, op.p5);
            if let P4::Collation(ref coll_name) = op.p4 {
                let coll = self.lock_collation();
                collate_compare(&cmp_lhs, &cmp_rhs, coll_name, &coll)
            } else {
                cmp_lhs.partial_cmp(&cmp_rhs)
            }
        };

        let should_jump = matches!(
            (op.opcode, cmp),
            (Opcode::Eq, Some(Ordering::Equal))
                | (Opcode::Lt, Some(Ordering::Less))
                | (Opcode::Le, Some(Ordering::Less | Ordering::Equal))
                | (Opcode::Gt, Some(Ordering::Greater))
                | (Opcode::Ge, Some(Ordering::Greater | Ordering::Equal))
        ) || matches!((op.opcode, cmp), (Opcode::Ne, Some(ord)) if ord != Ordering::Equal);

        if store_p2 {
            if cmp.is_none() {
                self.set_reg_fast(op.p2, SqliteValue::Null);
            } else {
                self.set_reg_int(op.p2, i64::from(should_jump));
            }
            *pc += 1;
        } else if should_jump {
            *pc = op.p2 as usize;
        } else {
            *pc += 1;
        }
        Ok(())
    }

    #[inline(always)]
    fn serialize_record_from_register_range(
        &self,
        first_reg: i32,
        n_cols: usize,
        p4: &P4,
        rec_buf: &mut Vec<u8>,
    ) {
        match p4 {
            P4::PrecomputedHeader(header) if header.column_count() == n_cols => {
                let null_placeholder = SqliteValue::Null;
                let make_iter = || {
                    header
                        .slots
                        .iter()
                        .enumerate()
                        .map(|(i, slot)| match slot.kind {
                            PrecomputedSerialTypeKind::NullPlaceholder => &null_placeholder,
                            _ => {
                                #[allow(clippy::cast_possible_wrap)]
                                let reg = first_reg + i as i32;
                                self.get_reg(reg)
                            }
                        })
                };
                let used_integer_fast_path =
                    n_cols >= 4 && simd_serialize_integer_record(make_iter(), rec_buf);
                if !used_integer_fast_path
                    && !serialize_record_iter_with_precomputed_header_into(
                        make_iter(),
                        header,
                        rec_buf,
                    )
                {
                    fsqlite_types::record::serialize_record_iter_into(make_iter(), rec_buf);
                }
            }
            // The opcode's p1..p1+p2-1 register range is the source of truth
            // for OP_MakeRecord. If cached header metadata drifts from that
            // shape, fall back to the generic path instead of serializing the
            // wrong number of columns.
            P4::PrecomputedHeader(_) => {
                let iter = (0..n_cols).map(move |i| {
                    #[allow(clippy::cast_possible_wrap)]
                    let reg = first_reg + i as i32;
                    self.get_reg(reg)
                });
                if n_cols >= 4 && simd_serialize_integer_record(iter.clone(), rec_buf) {
                    // Integer-only row used the SIMD/scalar fixed-width fast path.
                } else {
                    fsqlite_types::record::serialize_record_iter_into(iter, rec_buf);
                }
            }
            P4::Affinity(aff) => {
                let null_placeholder = SqliteValue::Null;
                let affinity = aff.as_bytes();
                let make_iter = || {
                    (0..n_cols).map(|i| {
                        if affinity.get(i) == Some(&b'X') {
                            &null_placeholder
                        } else {
                            #[allow(clippy::cast_possible_wrap)]
                            let reg = first_reg + i as i32;
                            self.get_reg(reg)
                        }
                    })
                };
                if n_cols >= 4 && simd_serialize_integer_record(make_iter(), rec_buf) {
                    // Integer-only row used the SIMD/scalar fixed-width fast path.
                } else {
                    fsqlite_types::record::serialize_record_iter_into(make_iter(), rec_buf);
                }
            }
            _ => {
                let iter = (0..n_cols).map(move |i| {
                    #[allow(clippy::cast_possible_wrap)]
                    let reg = first_reg + i as i32;
                    self.get_reg(reg)
                });
                if n_cols >= 4 && simd_serialize_integer_record(iter.clone(), rec_buf) {
                    // Integer-only row used the SIMD/scalar fixed-width fast path.
                } else {
                    fsqlite_types::record::serialize_record_iter_into(iter, rec_buf);
                }
            }
        }
    }

    #[inline(always)]
    fn execute_make_record_hot(&mut self, op: &VdbeOp, collect_vdbe_metrics: bool) {
        let target = op.p3;
        let n_cols = usize::try_from(op.p2).unwrap_or(0);
        if let Some(armed_reg) = self.make_record_lookaside.armed_register()
            && (armed_reg != target || Self::register_range_contains(op.p1, n_cols, armed_reg))
        {
            self.materialize_make_record_sideband(armed_reg);
        }
        let mut rec_buf = self.make_record_lookaside.take_buf();
        self.serialize_record_from_register_range(op.p1, n_cols, &op.p4, &mut rec_buf);
        if collect_vdbe_metrics {
            FSQLITE_VDBE_MAKE_RECORD_CALLS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
            FSQLITE_VDBE_MAKE_RECORD_BLOB_BYTES_TOTAL.fetch_add(
                u64::try_from(rec_buf.len()).unwrap_or(u64::MAX),
                AtomicOrdering::Relaxed,
            );
        }
        // Set the register to a Null sentinel first so any stale sideband
        // state attached to this register is cleared before arming the new
        // sideband payload below.
        self.set_reg(target, SqliteValue::Null);
        // bd-perf: Sideband buffer — keep record bytes in the statement lookaside
        // and let Insert/IdxInsert read from there directly, avoiding the
        // Arc<[u8]> heap allocation + memcpy per row. The sideband register
        // number tells Insert which register to intercept.
        self.make_record_lookaside.replace_buf(rec_buf); // keep bytes (don't clear!)
        self.make_record_lookaside.arm_sideband(target);
    }

    #[cold]
    fn execute_set_snapshot_cold(&mut self, op: &VdbeOp) -> Result<()> {
        let cursor_id = op.p1;
        let target = match &op.p4 {
            P4::TimeTravelCommitSeq(seq) => TimeTravelMarker::CommitSeq(*seq),
            P4::TimeTravelTimestamp(ts) => TimeTravelMarker::Timestamp(ts.clone()),
            _ => {
                return Err(FrankenError::Internal(
                    "SetSnapshot: invalid P4 (expected time-travel target)".to_owned(),
                ));
            }
        };
        self.time_travel_cursors.insert(cursor_id, target.clone());

        let version_store = self.version_store.as_ref().ok_or_else(|| {
            FrankenError::Internal(
                "SetSnapshot: VersionStore not available for time-travel".to_owned(),
            )
        })?;
        let commit_log = self.time_travel_commit_log.as_ref().ok_or_else(|| {
            FrankenError::Internal(
                "SetSnapshot: CommitLog not available for time-travel".to_owned(),
            )
        })?;
        let gc_horizon = self.time_travel_gc_horizon.ok_or_else(|| {
            FrankenError::Internal(
                "SetSnapshot: GC horizon not available for time-travel".to_owned(),
            )
        })?;

        let has_txn_cursor = self
            .storage_cursors
            .get(&cursor_id)
            .is_some_and(|sc| matches!(&sc.cursor, CursorBackend::Txn(_)));
        if !has_txn_cursor {
            return Err(FrankenError::Internal(format!(
                "SetSnapshot: cursor {cursor_id} is not a transactional cursor"
            )));
        }

        let time_travel_target = match &target {
            TimeTravelMarker::CommitSeq(seq) => {
                TimeTravelTarget::CommitSequence(CommitSeq::new(*seq))
            }
            TimeTravelMarker::Timestamp(ts) => {
                return Err(FrankenError::Internal(format!(
                    "SetSnapshot: timestamp-based time-travel not yet supported (datetime parser not wired); timestamp='{ts}'"
                )));
            }
        };

        let schema_epoch = SchemaEpoch::new(u64::from(self.schema_cookie));
        let commit_log = commit_log
            .lock()
            .unwrap_or_else(std::sync::PoisonError::into_inner);
        let tt_snapshot =
            create_time_travel_snapshot(time_travel_target, &commit_log, gc_horizon, schema_epoch)
                .map_err(|err| {
                    FrankenError::Internal(format!("time-travel snapshot error: {err}"))
                })?;

        let old_sc = self.storage_cursors.remove(&cursor_id).ok_or_else(|| {
            FrankenError::Internal(format!("SetSnapshot: cursor {} not found", cursor_id))
        })?;
        let root_page = self.cursor_root_pages.get(&cursor_id).copied().unwrap_or(1);
        let root_pgno = PageNumber::new(root_page as u32).unwrap_or(PageNumber::ONE);
        let inner_page_io = if let Some(ref page_io) = self.txn_page_io {
            page_io.clone()
        } else {
            return Err(FrankenError::Internal(
                "SetSnapshot: no transaction page I/O available".to_owned(),
            ));
        };

        let commit_seq = tt_snapshot.target_commit_seq().get();
        let tt_page_io = TimeTravelPageIo {
            inner: inner_page_io,
            version_store: Arc::clone(version_store),
            snapshot: tt_snapshot,
        };
        let is_table_btree = old_sc.cursor.is_table_btree();
        let index_desc_flags = if is_table_btree {
            Vec::new()
        } else {
            self.index_desc_flags_for_root(root_page)
        };
        let page_layout = BtreeCursorPageLayout {
            usable_size: old_sc.cursor.usable_size(),
            page_size: old_sc.cursor.page_size(),
        };
        let mut new_cursor = BtCursor::new_with_index_desc(
            tt_page_io,
            root_pgno,
            page_layout.usable_size,
            is_table_btree,
            index_desc_flags,
        );
        if !is_table_btree {
            new_cursor.set_index_collation_context(
                self.index_collations_for_root(root_page),
                Arc::clone(&self.collation_registry),
            );
        }
        configure_btree_cursor_page_size(&mut new_cursor, page_layout);
        self.storage_cursors.insert(
            cursor_id,
            StorageCursor {
                cursor: CursorBackend::TimeTravel(new_cursor),
                cx: old_sc.cx,
                writable: false,
                root_page,
                rowid_mode: old_sc.rowid_mode,
                autoincrement_high_water: old_sc.autoincrement_high_water,
                last_alloc_rowid: 0,
                payload_buf: Vec::new(),
                target_vals_buf: Vec::new(),
                cur_vals_buf: Vec::new(),
                row_decode: RowDecodeScratch::default(),
                last_position_stamp: None,
                cached_rowid: None,
                payload_includes_rowid_alias: None,
                last_successful_insert_rowid: None,
                last_rightmost_unique_index_prefix: None,
                last_rightmost_unique_index_position: None,
                ipk_col_idx: old_sc.ipk_col_idx,
                table_column_count: old_sc.table_column_count,
                first_not_null_non_ipk_col: old_sc.first_not_null_non_ipk_col,
            },
        );
        tracing::info!(
            cursor_id,
            commit_seq,
            "SetSnapshot: upgraded cursor to time-travel backend"
        );
        Ok(())
    }

    /// Collect a borrowed register range into owned values without clearing it.
    ///
    /// Prefer this pattern for function arguments and compare-style opcodes
    /// that need a stable owned snapshot but should not consume the registers.
    fn collect_reg_range(
        &mut self,
        start: i32,
        count: usize,
    ) -> smallvec::SmallVec<[SqliteValue; 16]> {
        let mut row = smallvec::SmallVec::with_capacity(count);
        for offset in 0..count {
            let val = Self::reg_with_offset(start, offset)
                .map_or_else(|| SqliteValue::Null, |reg| self.clone_reg_materialized(reg));
            row.push(val);
        }
        row
    }

    /// Like `collect_reg_range` but *moves* values out of the register file,
    /// leaving Null in the source registers. This avoids deep-cloning Text and
    /// Blob values on the hot ResultRow path where the registers are about to
    /// be overwritten by the next iteration anyway.
    #[inline]
    fn take_reg_range(
        &mut self,
        start: i32,
        count: usize,
    ) -> smallvec::SmallVec<[SqliteValue; 16]> {
        let mut row = smallvec::SmallVec::with_capacity(count);
        for offset in 0..count {
            let val = Self::reg_with_offset(start, offset)
                .map_or_else(|| SqliteValue::Null, |reg| self.take_reg(reg));
            row.push(val);
        }
        row
    }

    #[inline]
    fn positive_register_ranges_overlap(left_start: i32, right_start: i32, count: usize) -> bool {
        let Ok(count) = i64::try_from(count) else {
            return true;
        };
        let left_start = i64::from(left_start);
        let right_start = i64::from(right_start);
        let left_end = left_start + count;
        let right_end = right_start + count;
        left_start < right_end && right_start < left_end
    }

    #[inline]
    fn move_reg_range(&mut self, src_start: i32, dst_start: i32, count: usize) {
        if count == 0 {
            return;
        }

        let can_stream_directly = count == 1
            || (src_start > 0
                && dst_start > 0
                && !Self::positive_register_ranges_overlap(src_start, dst_start, count));

        if can_stream_directly {
            for offset in 0..count {
                let value = Self::reg_with_offset(src_start, offset)
                    .map_or_else(|| SqliteValue::Null, |reg| self.take_reg(reg));
                if let Some(dst) = Self::reg_with_offset(dst_start, offset) {
                    self.set_reg_fast(dst, value);
                }
            }
            return;
        }

        let mut temp: smallvec::SmallVec<[SqliteValue; 16]> =
            smallvec::SmallVec::with_capacity(count);
        for offset in 0..count {
            let value = Self::reg_with_offset(src_start, offset)
                .map_or_else(|| SqliteValue::Null, |reg| self.take_reg(reg));
            temp.push(value);
        }

        for (offset, value) in temp.into_iter().enumerate() {
            if let Some(dst) = Self::reg_with_offset(dst_start, offset) {
                self.set_reg_fast(dst, value);
            }
        }
    }

    #[inline]
    fn copy_reg_range(&mut self, src_start: i32, dst_start: i32, additional: i32) {
        let Ok(additional) = usize::try_from(additional) else {
            return;
        };
        let Some(count) = additional.checked_add(1) else {
            return;
        };

        if count == 1 {
            let value = self.clone_reg_materialized(src_start);
            self.set_reg_fast(dst_start, value);
            return;
        }

        let values = self.collect_reg_range(src_start, count);
        for (offset, value) in values.into_iter().enumerate() {
            if let Some(dst) = Self::reg_with_offset(dst_start, offset) {
                self.set_reg_fast(dst, value);
            }
        }
    }

    #[inline]
    fn discard_reg_range(&mut self, start: i32, count: usize) {
        for offset in 0..count {
            if let Some(reg) = Self::reg_with_offset(start, offset) {
                let _ = self.take_reg(reg);
            }
        }
    }

    fn agg_step_with_args(
        cold_state: &mut Option<Box<ColdVdbeState>>,
        statement_cold_state: &mut StatementColdState,
        step: AggStepCall<'_>,
    ) -> Result<()> {
        statement_cold_state.insert(StatementColdState::AGGREGATES);
        let ctx = cold_state
            .get_or_insert_with(|| Box::new(ColdVdbeState::new()))
            .aggregates
            .entry_or_insert_with(step.accum_reg, || {
                let state = step.func.initial_state();
                AggregateContext {
                    func: step.func.clone(),
                    state,
                    distinct_seen: if step.is_distinct {
                        Some(std::collections::HashSet::new())
                    } else {
                        None
                    },
                }
            });

        if !Arc::ptr_eq(&ctx.func, step.func) {
            return Err(FrankenError::Internal(
                "AggStep accumulator reused for a different aggregate".to_owned(),
            ));
        }

        // For DISTINCT aggregates, skip if we've already seen these args.
        // NULL values are always skipped for DISTINCT (SQL semantics).
        let should_step = if let Some(ref mut seen) = ctx.distinct_seen {
            if step.args.iter().any(|a| matches!(a, SqliteValue::Null)) {
                false
            } else {
                seen.insert(distinct_key_collated(step.args, step.agg_collation))
            }
        } else {
            true
        };

        observe_execution_cancellation(step.execution_cx)?;
        if should_step {
            if let Some(collation) = step.agg_collation
                && (step.func_name.eq_ignore_ascii_case("min")
                    || step.func_name.eq_ignore_ascii_case("max"))
                && !step.args.is_empty()
                && !step.args[0].is_null()
            {
                agg_step_min_max_collated(
                    &mut ctx.state,
                    &step.args[0],
                    step.func_name.eq_ignore_ascii_case("max"),
                    collation,
                );
            } else {
                ctx.func.step(&mut ctx.state, step.args)?;
            }
        }
        observe_execution_cancellation(step.execution_cx)
    }

    #[allow(dead_code)]
    fn collect_reg_range_refs(&self, start: i32, count: usize) -> Vec<&SqliteValue> {
        let mut row = Vec::with_capacity(count);
        for offset in 0..count {
            let val = Self::reg_with_offset(start, offset)
                .map_or(&SqliteValue::Null, |reg| self.get_reg(reg));
            row.push(val);
        }
        row
    }

    #[inline(always)]
    #[allow(clippy::inline_always)]
    /// Consume a single register cell and clear its slot back to NULL.
    ///
    /// Use this when the opcode is transferring ownership out of the register
    /// file and the source register should no longer hold the previous value.
    fn take_reg(&mut self, r: i32) -> SqliteValue {
        if r >= 0 && (r as usize) < self.registers.len() {
            self.materialize_make_record_sideband(r);
            self.clear_register_subtype(r);
            std::mem::replace(&mut self.registers[r as usize], SqliteValue::Null)
        } else {
            SqliteValue::Null
        }
    }

    /// General register write path for opcodes that do not need the hottest
    /// possible store sequence.
    ///
    /// All write helpers invalidate `MakeRecord` sideband state and subtype
    /// metadata so callers do not have to remember that bookkeeping.
    #[inline]
    #[allow(clippy::cast_sign_loss)]
    fn set_reg(&mut self, r: i32, val: SqliteValue) {
        if !(0..=65535).contains(&r) {
            // Drop out-of-bounds register writes to prevent OOM.
            // SQLite defines a max register limit (SQLITE_MAX_COLUMN + some overhead).
            return;
        }
        let idx = r as usize;
        if idx >= self.registers.len() {
            self.registers.resize(idx + 1, SqliteValue::Null);
        }
        self.invalidate_make_record_sideband_if_overwritten(r);
        // Register writes replace the logical value, so any prior subtype
        // metadata must be discarded as well.  Guard with is_empty() to
        // avoid a HashMap probe on every register write — subtypes are
        // rare (only JSON/pointer types).
        self.clear_register_subtype(r);
        let normalized = match val {
            SqliteValue::Float(f) if f.is_nan() => SqliteValue::Null,
            other => other,
        };
        self.replace_register_value(idx, normalized);
    }

    /// Fast-path register write with NaN -> Null normalization.
    /// Auto-resizes the register file when necessary (handles both
    /// builder-allocated programs and hand-crafted test programs).
    ///
    /// Use this for hot opcode write paths that still need the same register
    /// invalidation semantics as `set_reg`.
    #[inline(always)]
    #[allow(clippy::inline_always)]
    #[allow(clippy::cast_sign_loss)]
    fn set_reg_fast(&mut self, r: i32, val: SqliteValue) {
        if !(0..=65535).contains(&r) {
            return;
        }
        let idx = r as usize;
        if idx >= self.registers.len() {
            self.registers.resize(idx + 1, SqliteValue::Null);
        }
        self.invalidate_make_record_sideband_if_overwritten(r);
        self.clear_register_subtype(r);
        let normalized = match val {
            SqliteValue::Float(f) if f.is_nan() => SqliteValue::Null,
            other => other,
        };
        self.replace_register_value(idx, normalized);
    }

    /// Fastest possible register write for Integer values.
    /// Skips: bounds check (registers pre-sized in execute()),
    /// NaN normalization (integers can't be NaN).
    /// Only safe when the register file has been pre-sized.
    #[allow(clippy::inline_always)]
    #[inline(always)]
    #[allow(clippy::cast_sign_loss)]
    fn set_reg_int(&mut self, r: i32, val: i64) {
        let idx = r as usize;
        // The register file is pre-sized in execute() to program.register_count(),
        // so this should never need to resize. Guard with debug_assert only.
        debug_assert!(
            idx < self.registers.len(),
            "register {r} out of pre-sized bounds"
        );
        if idx < self.registers.len() {
            self.invalidate_make_record_sideband_if_overwritten(r);
            self.clear_register_subtype(r);
            self.replace_register_value(idx, SqliteValue::Integer(val));
        }
    }

    /// Write a text string to a register, reusing the existing `String`
    /// buffer's capacity when the register already holds a `Text` value.
    /// Avoids the allocate-then-free cycle for repeated text writes to the
    /// same register (common in scan loops with string constant columns).
    #[inline]
    #[allow(clippy::cast_sign_loss)]
    fn write_text_to_reg(&mut self, r: i32, text: &str) {
        if !(0..=65535).contains(&r) {
            return;
        }
        let idx = r as usize;
        if idx >= self.registers.len() {
            self.registers.resize(idx + 1, SqliteValue::Null);
        }
        self.invalidate_make_record_sideband_if_overwritten(r);
        self.clear_register_subtype(r);
        if let SqliteValue::Text(existing) = &mut self.registers[idx] {
            existing.overwrite(text);
        } else {
            self.replace_register_value(idx, SqliteValue::Text(SmallText::new(text)));
        }
    }

    /// Write a blob to a register.
    ///
    /// Reuses the existing `Arc<[u8]>` allocation when the register owns a
    /// same-length blob exclusively; otherwise allocates a fresh `Arc`.
    #[inline]
    #[allow(clippy::cast_sign_loss)]
    fn write_blob_to_reg(&mut self, r: i32, blob: &[u8]) {
        if !(0..=65535).contains(&r) {
            return;
        }
        let idx = r as usize;
        if idx >= self.registers.len() {
            self.registers.resize(idx + 1, SqliteValue::Null);
        }
        self.invalidate_make_record_sideband_if_overwritten(r);
        self.clear_register_subtype(r);
        if let SqliteValue::Blob(existing) = &mut self.registers[idx]
            && existing.len() == blob.len()
            && let Some(existing_bytes) = Arc::get_mut(existing)
        {
            existing_bytes.copy_from_slice(blob);
            return;
        }
        self.replace_register_value(idx, SqliteValue::Blob(Arc::from(blob)));
    }

    /// Return `SUBSTR(column, 1, prefix_len)` directly from raw storage bytes
    /// when the column's storage class makes that equivalent to scalar `substr`.
    #[allow(clippy::too_many_lines, clippy::cast_sign_loss)]
    fn column_substr_prefix_direct(
        &mut self,
        cursor_id: i32,
        col_idx: usize,
        prefix_len: usize,
    ) -> Result<Option<SqliteValue>> {
        let collect_vdbe_metrics = self.collect_vdbe_metrics;
        let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) else {
            return Ok(None);
        };
        if cursor.cursor.eof() {
            return Ok(Some(SqliteValue::Null));
        }

        ensure_storage_cursor_row_layout(cursor, 0, collect_vdbe_metrics)?;

        let ipk_col_idx = cursor.ipk_col_idx;
        let payload_includes = if let Some(ipk) = ipk_col_idx {
            if let Some(cached) = cursor.payload_includes_rowid_alias {
                cached
            } else {
                let includes = payload_includes_rowid_alias_without_rowid(
                    &cursor.row_decode,
                    ipk,
                    cursor.table_column_count,
                    cursor.first_not_null_non_ipk_col,
                );
                cursor.payload_includes_rowid_alias = Some(includes);
                includes
            }
        } else {
            false
        };

        let payload_idx = if let Some(ipk) = ipk_col_idx {
            if col_idx == ipk {
                return Ok(None);
            }
            if col_idx > ipk && !payload_includes {
                col_idx - 1
            } else {
                col_idx
            }
        } else {
            col_idx
        };

        let Some(col) = cursor.row_decode.column_offset(payload_idx).copied() else {
            return Ok(None);
        };
        let Some(col_end) = column_payload_end(&col) else {
            return Err(FrankenError::DatabaseCorrupt {
                detail: format!("malformed column {payload_idx} payload length"),
            });
        };
        ensure_storage_cursor_row_layout(cursor, col_end, collect_vdbe_metrics)?;
        let start =
            usize::try_from(col.body_offset).map_err(|_| FrankenError::DatabaseCorrupt {
                detail: format!("malformed column {payload_idx} payload offset"),
            })?;
        let bytes = cursor.payload_buf.get(start..col_end).ok_or_else(|| {
            FrankenError::DatabaseCorrupt {
                detail: format!("column {payload_idx} payload exceeds row image"),
            }
        })?;

        let value = match classify_serial_type(col.serial_type) {
            SerialTypeClass::Null => Some(SqliteValue::Null),
            SerialTypeClass::Text => {
                if !bytes.is_ascii() {
                    return Ok(None);
                }
                let end = prefix_len.min(bytes.len());
                let text = std::str::from_utf8(&bytes[..end])
                    .expect("ASCII storage text prefix must be valid UTF-8");
                Some(SqliteValue::Text(SmallText::new(text)))
            }
            SerialTypeClass::Blob => {
                let end = prefix_len.min(bytes.len());
                Some(SqliteValue::Blob(Arc::from(&bytes[..end])))
            }
            SerialTypeClass::Integer
            | SerialTypeClass::Float
            | SerialTypeClass::Zero
            | SerialTypeClass::One
            | SerialTypeClass::Reserved => None,
        };

        if collect_vdbe_metrics && let Some(value) = value.as_ref() {
            FSQLITE_VDBE_COLUMN_READS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
            record_decoded_value_metrics(value);
        }
        Ok(value)
    }

    /// Zero-clone column-to-register write for storage cursors.
    ///
    /// Inlines the storage-cursor branch of [`cursor_column`] but writes
    /// the column value *directly* into the target register, reusing
    /// existing `Text`/`Blob` buffer capacity via disjoint struct field
    /// borrows (`self.storage_cursors` vs `self.registers`).
    ///
    /// Returns `Ok(true)` when the column read was handled,
    /// `Ok(false)` when the cursor is not a storage cursor (caller must
    /// fall back to `cursor_column`).
    #[allow(clippy::too_many_lines, clippy::cast_sign_loss)]
    fn column_to_reg_direct(
        &mut self,
        cursor_id: i32,
        col_idx: usize,
        target: i32,
    ) -> Result<bool> {
        let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) else {
            return Ok(false);
        };
        if cursor.cursor.eof() {
            self.set_reg(target, SqliteValue::Null);
            return Ok(true);
        }

        let mut refresh_state =
            ensure_storage_cursor_row_layout(cursor, 0, self.collect_vdbe_metrics)?;

        // ── Resolve IPK alias and payload column index ────────────
        let ipk_col_idx = cursor.ipk_col_idx;
        let payload_includes = if let Some(ipk) = ipk_col_idx {
            if let Some(cached) = cursor.payload_includes_rowid_alias {
                cached
            } else {
                let includes = payload_includes_rowid_alias_without_rowid(
                    &cursor.row_decode,
                    ipk,
                    cursor.table_column_count,
                    cursor.first_not_null_non_ipk_col,
                );
                cursor.payload_includes_rowid_alias = Some(includes);
                includes
            }
        } else {
            false
        };

        let payload_idx = if let Some(ipk) = ipk_col_idx {
            if col_idx == ipk {
                let rowid = storage_cursor_cached_rowid(cursor)?;
                self.set_reg_fast(target, SqliteValue::Integer(rowid));
                return Ok(true);
            }
            if col_idx > ipk && !payload_includes {
                col_idx - 1
            } else {
                col_idx
            }
        } else {
            col_idx
        };

        // ── Lazy decode + zero-clone register write ────────────────
        let collect_vdbe_metrics = self.collect_vdbe_metrics;

        if payload_idx < cursor.row_decode.column_count() {
            if let Some(col) = cursor.row_decode.column_offset(payload_idx).copied()
                && let Some(col_end) = column_payload_end(&col)
            {
                let col_refresh =
                    ensure_storage_cursor_row_layout(cursor, col_end, collect_vdbe_metrics)?;
                refresh_state.refreshed |= col_refresh.refreshed;
                refresh_state.eager_values_ready |= col_refresh.eager_values_ready;
            }
            // Already decoded? Write from cache to register with buffer reuse.
            let cached_value_ready = cursor.row_decode.cached_value_ready(payload_idx);
            if cached_value_ready {
                if let Some(cached) = cursor.row_decode.cached_value(payload_idx) {
                    if refresh_state.refreshed && refresh_state.eager_values_ready {
                        note_decode_cache_miss(collect_vdbe_metrics);
                    } else {
                        note_decode_cache_hit(collect_vdbe_metrics);
                    }
                    if collect_vdbe_metrics {
                        FSQLITE_VDBE_COLUMN_READS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
                        record_decoded_value_metrics(cached);
                    }
                    // KEY OPTIMIZATION: write from &cached (borrows
                    // self.storage_cursors) directly into self.registers
                    // (disjoint struct field) — zero clone for matching types.
                    let reg_idx = target as usize;
                    if (0..=65535).contains(&target) {
                        if reg_idx >= self.registers.len() {
                            self.registers.resize(reg_idx + 1, SqliteValue::Null);
                        }
                        self.make_record_lookaside
                            .clear_sideband_for_register(target);
                        if let Some(cold_state) = self.cold_state.as_mut()
                            && cold_state.has_subtypes
                        {
                            cold_state.register_subtypes.remove(&target);
                            if cold_state.register_subtypes.is_empty() {
                                cold_state.has_subtypes = false;
                            }
                        }
                        match cached {
                            // NaN → Null normalization (matches set_reg behavior).
                            SqliteValue::Float(f) if f.is_nan() => {
                                self.registers[reg_idx] = SqliteValue::Null;
                            }
                            val => {
                                self.registers[reg_idx] = val.clone();
                            }
                        }
                    }
                    return Ok(true);
                }
            }

            // Not yet decoded: decode from raw payload.
            // bd-db300.4.4.2 K1: pass the previous row's cached value as a hint.
            // If raw bytes match, reuse the existing Arc (skip malloc+memcpy).
            note_decode_cache_miss(collect_vdbe_metrics);
            let hint = cursor.row_decode.cached_value(payload_idx);
            let val = fsqlite_types::record::decode_column_from_offset_reuse(
                &cursor.payload_buf,
                cursor
                    .row_decode
                    .column_offset(payload_idx)
                    .expect("payload_idx checked against column_count"),
                hint,
                collect_vdbe_metrics,
            )
            .ok_or_else(|| FrankenError::DatabaseCorrupt {
                detail: format!("failed to decode column {payload_idx} from cursor payload"),
            })?;

            if collect_vdbe_metrics {
                FSQLITE_VDBE_COLUMN_READS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
                record_decoded_value_metrics(&val);
            }

            // Cache the decoded value with buffer reuse.
            cursor.row_decode.cache_decoded(payload_idx, val.clone());

            // Write freshly decoded value to register (owned, no clone needed).
            self.set_reg_fast(target, val);
            return Ok(true);
        }

        // ── Column beyond record width: ALTER TABLE ADD COLUMN defaults ─
        if let Some(&rp) = self.cursor_root_pages.get(&cursor_id) {
            if let Some(defaults) = self.column_defaults_by_root_page.get(&rp) {
                if let Some(Some(default_val)) = defaults.get(col_idx) {
                    if collect_vdbe_metrics {
                        FSQLITE_VDBE_COLUMN_READS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
                        record_decoded_value_metrics(default_val);
                    }
                    self.set_reg(target, default_val.clone());
                    return Ok(true);
                }
            }
        }
        if collect_vdbe_metrics {
            FSQLITE_VDBE_COLUMN_READS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
            record_decoded_value_metrics(&SqliteValue::Null);
        }
        self.set_reg(target, SqliteValue::Null);
        Ok(true)
    }

    /// Read a column value from the cursor's current row.
    ///
    /// Uses **lazy column decode**: when the cursor moves to a new position,
    /// only the record header is parsed (serial types + byte offsets).
    /// Individual column values are decoded on first access and cached in
    /// `row_decode.values` for subsequent reads at the same position.
    ///
    /// For records with >64 columns the full eager-decode path is used
    /// because `row_decode.decoded_mask` uses a single `u64`.
    fn cursor_column(&mut self, cursor_id: i32, col_idx: usize) -> Result<SqliteValue> {
        let collect_vdbe_metrics = self.collect_vdbe_metrics;
        if let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) {
            if cursor.cursor.eof() {
                return Ok(SqliteValue::Null);
            }
            let mut refresh_state =
                ensure_storage_cursor_row_layout(cursor, 0, collect_vdbe_metrics)?;

            let ipk_col_idx = cursor.ipk_col_idx;
            let payload_includes_rowid_alias = if let Some(ipk_col_idx) = ipk_col_idx {
                if let Some(cached) = cursor.payload_includes_rowid_alias {
                    cached
                } else {
                    let includes = payload_includes_rowid_alias_without_rowid(
                        &cursor.row_decode,
                        ipk_col_idx,
                        cursor.table_column_count,
                        cursor.first_not_null_non_ipk_col,
                    );
                    cursor.payload_includes_rowid_alias = Some(includes);
                    includes
                }
            } else {
                false
            };
            let payload_idx = if let Some(ipk) = ipk_col_idx {
                if col_idx == ipk {
                    let rowid = storage_cursor_cached_rowid(cursor)?;
                    return Ok(SqliteValue::Integer(rowid));
                }
                if col_idx > ipk && !payload_includes_rowid_alias {
                    col_idx - 1
                } else {
                    col_idx
                }
            } else {
                col_idx
            };

            // ── Lazy column decode: decode on demand ─────────────────
            if payload_idx < cursor.row_decode.column_count() {
                if let Some(col) = cursor.row_decode.column_offset(payload_idx).copied()
                    && let Some(col_end) = column_payload_end(&col)
                {
                    let col_refresh =
                        ensure_storage_cursor_row_layout(cursor, col_end, collect_vdbe_metrics)?;
                    refresh_state.refreshed |= col_refresh.refreshed;
                    refresh_state.eager_values_ready |= col_refresh.eager_values_ready;
                }
                // Check if already decoded via bitmask.
                let cached_value_ready = cursor.row_decode.cached_value_ready(payload_idx);
                if cached_value_ready {
                    // Already materialized — return cached value.
                    if let Some(val) = cursor.row_decode.cached_value(payload_idx).cloned() {
                        if refresh_state.refreshed && refresh_state.eager_values_ready {
                            note_decode_cache_miss(collect_vdbe_metrics);
                        } else {
                            note_decode_cache_hit(collect_vdbe_metrics);
                        }
                        if collect_vdbe_metrics {
                            FSQLITE_VDBE_COLUMN_READS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
                            record_decoded_value_metrics(&val);
                        }
                        return Ok(val);
                    }
                }
                // Decode just this column from the offset table + raw payload.
                // bd-db300.4.4.2 K1: reuse hint from previous row's cached value.
                note_decode_cache_miss(collect_vdbe_metrics);
                let hint = cursor.row_decode.cached_value(payload_idx);
                let val = fsqlite_types::record::decode_column_from_offset_reuse(
                    &cursor.payload_buf,
                    cursor
                        .row_decode
                        .column_offset(payload_idx)
                        .expect("payload_idx checked against column_count"),
                    hint,
                    collect_vdbe_metrics,
                )
                .ok_or_else(|| FrankenError::DatabaseCorrupt {
                    detail: format!("failed to decode column {payload_idx} from cursor payload"),
                })?;
                if collect_vdbe_metrics {
                    FSQLITE_VDBE_COLUMN_READS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
                    record_decoded_value_metrics(&val);
                }
                // Cache the decoded value for repeated column reads while the
                // cursor stays on the same physical row image.
                cursor.row_decode.cache_decoded(payload_idx, val.clone());
                return Ok(val);
            }
            // Column beyond record width — check ALTER TABLE ADD COLUMN defaults.
            if let Some(&root_page) = self.cursor_root_pages.get(&cursor_id) {
                if let Some(defaults) = self.column_defaults_by_root_page.get(&root_page) {
                    if let Some(Some(default_val)) = defaults.get(col_idx) {
                        if collect_vdbe_metrics {
                            FSQLITE_VDBE_COLUMN_READS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
                            record_decoded_value_metrics(default_val);
                        }
                        return Ok(default_val.clone());
                    }
                }
            }
            if collect_vdbe_metrics {
                FSQLITE_VDBE_COLUMN_READS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
                record_decoded_value_metrics(&SqliteValue::Null);
            }
            return Ok(SqliteValue::Null);
        }

        // Extract pseudo cursor info without holding a mutable borrow
        // on self.cursors, to avoid conflict with self.get_reg().
        if let Some(cursor) = self.cursors.get(&cursor_id) {
            if cursor.is_pseudo {
                if let Some(row) = &cursor.pseudo_row {
                    let value = row.get(col_idx).cloned().unwrap_or(SqliteValue::Null);
                    if collect_vdbe_metrics {
                        record_decoded_value_metrics(&value);
                    }
                    return Ok(value);
                }
                if let Some(reg) = cursor.pseudo_reg {
                    let blob = self.get_reg(reg).clone();

                    let use_cache = if let Some(cursor) = self.cursors.get(&cursor_id) {
                        if let Some((cached_blob, _)) = &cursor.cached_pseudo_row {
                            cached_blob == &blob
                        } else {
                            false
                        }
                    } else {
                        false
                    };

                    if !use_cache {
                        if let Some(cursor) = self.cursors.get(&cursor_id)
                            && let Some((cached_blob, _)) = &cursor.cached_pseudo_row
                            && cached_blob != &blob
                        {
                            note_decode_cache_invalidation(
                                collect_vdbe_metrics,
                                DecodeCacheInvalidationReason::PseudoRowChange,
                            );
                        }
                        note_decode_cache_miss(collect_vdbe_metrics);
                        if let Ok(values) = decode_record_with_metrics(&blob, collect_vdbe_metrics)
                        {
                            if let Some(cursor) = self.cursors.get_mut(&cursor_id) {
                                cursor.cached_pseudo_row = Some((blob, values));
                            }
                        } else if let Some(cursor) = self.cursors.get_mut(&cursor_id) {
                            cursor.cached_pseudo_row = None;
                        }
                    }

                    if let Some(cursor) = self.cursors.get(&cursor_id) {
                        if let Some((_, values)) = &cursor.cached_pseudo_row {
                            if use_cache {
                                note_decode_cache_hit(collect_vdbe_metrics);
                            }
                            let value = values.get(col_idx).cloned().unwrap_or(SqliteValue::Null);
                            if collect_vdbe_metrics {
                                record_decoded_value_metrics(&value);
                            }
                            return Ok(value);
                        }
                    }
                }
                return Ok(SqliteValue::Null);
            }
            if let Some(pos) = cursor.position
                && let Some(db) = self.db.as_ref()
                && let Some(table) = db.get_table(cursor.root_page)
                && let Some(row) = table.rows.get(pos)
            {
                let value = row
                    .values
                    .get(col_idx)
                    .cloned()
                    .unwrap_or(SqliteValue::Null);
                if collect_vdbe_metrics {
                    record_decoded_value_metrics(&value);
                }
                return Ok(value);
            }
        }

        // Sorter cursor: read column directly from the sorted row.
        if let Some(sorter) = self.sorters.get_mut(&cursor_id) {
            if let Some(pos) = sorter.position {
                if let Some(value) = sorter
                    .rows
                    .get(pos)
                    .and_then(|row| row.values.get(col_idx))
                    .cloned()
                {
                    note_decode_cache_hit(collect_vdbe_metrics);
                    if collect_vdbe_metrics {
                        record_decoded_value_metrics(&value);
                    }
                    return Ok(value);
                }
                let refresh_state = ensure_sorter_row_cache(sorter, collect_vdbe_metrics, pos)?;
                let rows = &sorter.rows;
                let cached_row_decode = &mut sorter.cached_row_decode;
                let row = rows.get(pos).ok_or_else(|| FrankenError::DatabaseCorrupt {
                    detail: format!("missing sorter row at position {pos}"),
                })?;
                let value = if col_idx < cached_row_decode.column_count() {
                    let cached_value_ready = cached_row_decode.cached_value_ready(col_idx);
                    if cached_value_ready {
                        if refresh_state.refreshed && refresh_state.eager_values_ready {
                            note_decode_cache_miss(collect_vdbe_metrics);
                        } else {
                            note_decode_cache_hit(collect_vdbe_metrics);
                        }
                        cached_row_decode
                            .cached_value(col_idx)
                            .cloned()
                            .unwrap_or(SqliteValue::Null)
                    } else if let Some(value) = fsqlite_types::record::decode_column_from_offset(
                        &row.blob,
                        cached_row_decode
                            .column_offset(col_idx)
                            .expect("col_idx checked against column_count"),
                        collect_vdbe_metrics,
                    ) {
                        note_decode_cache_miss(collect_vdbe_metrics);
                        cached_row_decode.cache_decoded(col_idx, value.clone());
                        value
                    } else {
                        return Err(FrankenError::DatabaseCorrupt {
                            detail: format!(
                                "malformed sorter record while reading column {col_idx}"
                            ),
                        });
                    }
                } else {
                    SqliteValue::Null
                };
                if collect_vdbe_metrics {
                    record_decoded_value_metrics(&value);
                }
                return Ok(value);
            }
        }

        Ok(SqliteValue::Null)
    }

    /// Get the rowid from the cursor's current row.
    fn cursor_rowid(&mut self, cursor_id: i32) -> Result<SqliteValue> {
        if let Some(cursor) = self.storage_cursors.get_mut(&cursor_id) {
            if cursor.cursor.eof() {
                return Ok(SqliteValue::Null);
            }
            return Ok(SqliteValue::Integer(storage_cursor_cached_rowid(cursor)?));
        }

        if let Some(state) = self
            .cold_state()
            .and_then(|cold_state| cold_state.vtab_cursors.get(&cursor_id))
        {
            if state.null_row || state.cursor.eof() {
                return Ok(SqliteValue::Null);
            }
            return Ok(SqliteValue::Integer(state.cursor.rowid()?));
        }

        if let Some(cursor) = self.cursors.get(&cursor_id)
            && let Some(pos) = cursor.position
            && let Some(db) = self.db.as_ref()
            && let Some(table) = db.get_table(cursor.root_page)
            && let Some(row) = table.rows.get(pos)
        {
            return Ok(SqliteValue::Integer(row.rowid));
        }
        Ok(SqliteValue::Null)
    }

    #[allow(clippy::cast_sign_loss)]
    fn open_storage_cursor(&mut self, cursor_id: i32, root_page: i32, writable: bool) -> bool {
        let _page_size_u32 = self.page_size.get();
        // bd-1xrs: storage_cursors_enabled check removed.
        // StorageCursor is now the ONLY cursor path.
        let trace_id = self.execution_cx.trace_id();
        let certifying_mode = self.reject_mem_fallback;
        let mode = if self.reject_mem_fallback {
            "parity_cert"
        } else {
            "fallback_allowed"
        };

        let Some(root_pgno) = PageNumber::new(root_page as u32) else {
            Self::log_open_storage_cursor_fallback_decision(
                trace_id,
                certifying_mode,
                cursor_id,
                root_page,
                writable,
                "none",
                "invalid_page_number",
                "refused",
                "invalid root page number",
            );
            tracing::debug!(
                cursor_id,
                root_page,
                writable,
                mode,
                backend_kind = "none",
                decision_reason = "invalid_page_number",
                "open_storage_cursor: invalid root page number"
            );
            return false;
        };

        let has_txn = self.txn_page_io.is_some();
        let txn_cx = self.derive_execution_cx();
        let (rowid_mode, autoincrement_high_water) = self.storage_cursor_runtime_meta(root_page);
        if self.cursor_root_pages.get(&cursor_id).copied() == Some(root_page)
            && let Some(existing) = self.storage_cursors.get_mut(&cursor_id)
        {
            let backend_matches = if has_txn {
                existing.cursor.is_txn()
            } else {
                existing.cursor.is_mem()
            };
            if backend_matches {
                existing.writable |= writable;
                existing.cx = txn_cx.clone();
                existing.root_page = root_page;
                existing.rowid_mode = rowid_mode;
                existing.autoincrement_high_water = autoincrement_high_water;
                self.cursor_root_pages.insert(cursor_id, root_page);
                tracing::trace!(
                    cursor_id,
                    page_id = root_page,
                    writable,
                    has_txn,
                    backend_kind = existing.cursor.kind_str(),
                    "open_storage_cursor: reused retained cursor"
                );
                return true;
            }
        }
        let mut mem_decision_reason = "no_pager_transaction";

        // Phase 5C.1 (bd-35my): Route through pager when available.
        //
        // Critical safety rule:
        // If a pager transaction exists, writable cursors must NEVER fall back
        // to MemPageStore. A writable fallback can silently route writes to a
        // non-durable in-memory copy and create divergence/corruption under
        // concurrency.
        //
        // Read-only fallback remains allowed when parity-cert is disabled and
        // MemDatabase owns the root page (for example, materialized view /
        // sqlite_master snapshots that were never pager-backed).
        //
        // The only acceptable writable pager "bootstrap" case is a truly
        // zero-initialized root page that we can initialize in-place.
        // Use a labeled block so we can break out to the MemDatabase fallback path
        // when the pager read fails but MemDatabase has the table.
        'pager_block: {
            if let Some(ref mut page_io) = self.txn_page_io {
                // If the pager read itself fails, check if MemDatabase can serve
                // this table before failing. This handles view materialization where
                // MemDatabase allocates root pages beyond the pager's db_size.
                let page_data = match page_io.read_page(&txn_cx, root_pgno) {
                    Ok(bytes) => bytes,
                    Err(err) => {
                        // Check if MemDatabase can serve this table.
                        let has_mem_table = self
                            .db
                            .as_ref()
                            .is_some_and(|db| db.get_table(root_page).is_some());
                        if has_mem_table && !self.reject_mem_fallback && !writable {
                            mem_decision_reason = "pager_read_failed_mem_fallback";
                            Self::log_open_storage_cursor_fallback_decision(
                                trace_id,
                                certifying_mode,
                                cursor_id,
                                root_page,
                                writable,
                                "mem",
                                "pager_read_failed_mem_fallback",
                                "vdbe_mempage_fallback_allowed",
                                "pager read failed and MemDatabase owns read-only root page",
                            );
                            tracing::debug!(
                                cursor_id,
                                page_id = root_page,
                                writable,
                                has_txn,
                                mode,
                                backend_kind = "mem",
                                decision_reason = "pager_read_failed_mem_fallback",
                                error = %err,
                                "open_storage_cursor: pager read failed, falling through to MemDatabase"
                            );
                            // Break out of pager_block to fall through to MemDatabase path.
                            break 'pager_block;
                        }
                        Self::log_open_storage_cursor_fallback_decision(
                            trace_id,
                            certifying_mode,
                            cursor_id,
                            root_page,
                            writable,
                            "txn",
                            "pager_read_failed",
                            "refused",
                            "pager read failed before storage cursor dispatch",
                        );
                        tracing::warn!(
                            cursor_id,
                            page_id = root_page,
                            writable,
                            has_txn,
                            mode,
                            backend_kind = "txn",
                            decision_reason = "pager_read_failed",
                            error = %err,
                            has_mem_table,
                            reject_mem_fallback = self.reject_mem_fallback,
                            "open_storage_cursor: failed to read root page from pager"
                        );
                        return false;
                    }
                };
                let hdr_offset = header_offset_for_page(root_pgno);
                let parsed_header = BtreePageHeader::parse(&page_data, hdr_offset).ok();
                // A page is a valid B-tree if the header parses successfully.
                // Legacy fallback: synthetic test pages with non-zero first byte
                // but unparseable header are also accepted, but we infer
                // is_table from the raw page-type byte rather than defaulting
                // to true (which was incorrect for index pages).
                let is_valid_btree =
                    parsed_header.is_some() || (!page_data.is_empty() && page_data[0] != 0x00);
                let is_zero_page = page_data.iter().all(|&byte| byte == 0);

                let page_layout = btree_cursor_page_layout_for_reader_or_default(
                    page_io,
                    &txn_cx,
                    self.page_size,
                );

                if is_valid_btree {
                    // Real B-tree backed by pager: infer table-vs-index from the
                    // parsed page header when available, falling back to the raw
                    // page-type byte for synthetic/legacy pages.
                    let (is_table_btree, detected_page_type) = if let Some(header) = parsed_header {
                        (header.page_type.is_table(), Some(header.page_type))
                    } else {
                        // Header parse failed but page has data. Use the raw
                        // page-type flag byte at the header offset to infer
                        // table vs index — do NOT blindly default to table.
                        let type_byte = page_data.get(hdr_offset).copied().unwrap_or(0);
                        let is_table = match type_byte {
                            0x02 | 0x0A => false, // InteriorIndex / LeafIndex
                            0x05 | 0x0D => true,  // InteriorTable / LeafTable
                            _ => {
                                tracing::warn!(
                                    cursor_id,
                                    page_id = root_page,
                                    type_byte,
                                    "open_storage_cursor: unparseable header with unknown page-type byte, defaulting to table"
                                );
                                true
                            }
                        };
                        (is_table, None)
                    };
                    let mut cursor = BtCursor::new_with_index_desc(
                        page_io.clone(),
                        root_pgno,
                        page_layout.usable_size,
                        is_table_btree,
                        if is_table_btree {
                            Vec::new()
                        } else {
                            self.index_desc_flags_for_root(root_page)
                        },
                    );
                    if !is_table_btree {
                        cursor.set_index_collation_context(
                            self.index_collations_for_root(root_page),
                            Arc::clone(&self.collation_registry),
                        );
                    }
                    configure_btree_cursor_page_size(&mut cursor, page_layout);
                    let ipk_col_idx = self.rowid_alias_col_by_root_page.get(&root_page).copied();
                    let table_column_count = self
                        .table_column_count_by_root_page
                        .get(&root_page)
                        .copied();
                    let first_not_null_non_ipk_col = self
                        .first_not_null_non_ipk_col_by_root_page
                        .get(&root_page)
                        .copied();
                    self.storage_cursors.insert(
                        cursor_id,
                        StorageCursor {
                            cursor: CursorBackend::Txn(cursor),
                            cx: txn_cx,
                            writable,
                            root_page,
                            rowid_mode,
                            autoincrement_high_water,
                            last_alloc_rowid: 0,
                            last_successful_insert_rowid: None,
                            last_rightmost_unique_index_prefix: None,
                            last_rightmost_unique_index_position: None,
                            payload_buf: Vec::new(),
                            target_vals_buf: Vec::new(),
                            cur_vals_buf: Vec::new(),
                            row_decode: RowDecodeScratch::default(),
                            last_position_stamp: None,
                            cached_rowid: None,
                            payload_includes_rowid_alias: None,
                            ipk_col_idx,
                            table_column_count,
                            first_not_null_non_ipk_col,
                        },
                    );
                    self.cursor_root_pages.insert(cursor_id, root_page);
                    Self::log_open_storage_cursor_fallback_decision(
                        trace_id,
                        certifying_mode,
                        cursor_id,
                        root_page,
                        writable,
                        "txn",
                        "valid_btree_page",
                        "real_backend_dispatch",
                        "pager transaction opened a valid B-tree page",
                    );
                    tracing::debug!(
                        cursor_id,
                        page_id = root_page,
                        writable,
                        has_txn,
                        mode,
                        backend_kind = "txn",
                        decision_reason = "valid_btree_page",
                        detected_page_type = ?detected_page_type,
                        is_table_btree,
                        "open_storage_cursor: routed through pager transaction"
                    );
                    return true;
                }

                // For writable cursors on truly zeroed pages (e.g., freshly
                // allocated roots), initialize an empty root page.
                if writable && is_zero_page {
                    // Infer root kind: check the index metadata map first (it
                    // is populated from the schema and correctly identifies all
                    // index root pages, including autoindexes). Fall back to
                    // MemDatabase only when the index map has no opinion.
                    // MemDatabase::get_table() returns Some for BOTH table and
                    // autoindex root pages (because create_table_at is called
                    // for both), so using it alone would misclassify autoindex
                    // pages as tables — causing "wrong # of entries" corruption
                    // when stock SQLite reads the file (issue #55).
                    let is_known_index =
                        self.index_desc_flags_by_root_page.contains_key(&root_page)
                            || self.index_collations_by_root_page.contains_key(&root_page);
                    let is_table_btree = if is_known_index {
                        false
                    } else {
                        self.db
                            .as_ref()
                            .is_none_or(|db| db.get_table(root_page).is_some())
                    };
                    let init_page_type = if is_table_btree {
                        BtreePageType::LeafTable
                    } else {
                        BtreePageType::LeafIndex
                    };
                    // Initialize empty leaf page for the inferred B-tree kind.
                    // The page buffer starts zeroed, so only the page-type flag
                    // and cell-content offset need to be populated explicitly.
                    let mut page = vec![0u8; page_layout.page_size as usize];
                    page[0] = if is_table_btree {
                        fsqlite_types::BTreePageType::LeafTable as u8
                    } else {
                        fsqlite_types::BTreePageType::LeafIndex as u8
                    };
                    let content_raw = if page_layout.usable_size >= 65_536 {
                        0u16
                    } else {
                        page_layout.usable_size as u16
                    };
                    page[5..7].copy_from_slice(&content_raw.to_be_bytes());

                    // Write the initialized page to pager.
                    if let Err(err) = page_io.write_page(&txn_cx, root_pgno, &page) {
                        Self::log_open_storage_cursor_fallback_decision(
                            trace_id,
                            certifying_mode,
                            cursor_id,
                            root_page,
                            writable,
                            "txn",
                            "zero_page_init_failed",
                            "refused",
                            "failed to initialize writable root page in pager",
                        );
                        tracing::warn!(
                            cursor_id,
                            page_id = root_page,
                            writable,
                            has_txn,
                            mode,
                            backend_kind = "txn",
                            decision_reason = "zero_page_init_failed",
                            error = %err,
                            "open_storage_cursor: failed to initialize writable root page in pager"
                        );
                        return false;
                    }
                    let mut cursor = BtCursor::new_with_index_desc(
                        page_io.clone(),
                        root_pgno,
                        page_layout.usable_size,
                        is_table_btree,
                        if is_table_btree {
                            Vec::new()
                        } else {
                            self.index_desc_flags_for_root(root_page)
                        },
                    );
                    if !is_table_btree {
                        cursor.set_index_collation_context(
                            self.index_collations_for_root(root_page),
                            Arc::clone(&self.collation_registry),
                        );
                    }
                    configure_btree_cursor_page_size(&mut cursor, page_layout);
                    let ipk_col_idx = self.rowid_alias_col_by_root_page.get(&root_page).copied();
                    let table_column_count = self
                        .table_column_count_by_root_page
                        .get(&root_page)
                        .copied();
                    let first_not_null_non_ipk_col = self
                        .first_not_null_non_ipk_col_by_root_page
                        .get(&root_page)
                        .copied();
                    self.storage_cursors.insert(
                        cursor_id,
                        StorageCursor {
                            cursor: CursorBackend::Txn(cursor),
                            cx: txn_cx,
                            writable,
                            root_page,
                            rowid_mode,
                            autoincrement_high_water,
                            last_alloc_rowid: 0,
                            last_successful_insert_rowid: None,
                            last_rightmost_unique_index_prefix: None,
                            last_rightmost_unique_index_position: None,
                            payload_buf: Vec::new(),
                            target_vals_buf: Vec::new(),
                            cur_vals_buf: Vec::new(),
                            row_decode: RowDecodeScratch::default(),
                            last_position_stamp: None,
                            cached_rowid: None,
                            payload_includes_rowid_alias: None,
                            ipk_col_idx,
                            table_column_count,
                            first_not_null_non_ipk_col,
                        },
                    );
                    self.cursor_root_pages.insert(cursor_id, root_page);
                    Self::log_open_storage_cursor_fallback_decision(
                        trace_id,
                        certifying_mode,
                        cursor_id,
                        root_page,
                        writable,
                        "txn",
                        "zero_page_initialized",
                        "real_backend_dispatch",
                        "initialized empty root page through pager transaction",
                    );
                    tracing::debug!(
                        cursor_id,
                        page_id = root_page,
                        writable,
                        has_txn,
                        mode,
                        backend_kind = "txn",
                        decision_reason = "zero_page_initialized",
                        initialized_page_type = ?init_page_type,
                        is_table_btree,
                        "open_storage_cursor: initialized empty root page via pager"
                    );
                    return true;
                }

                // If the page is zero/invalid but MemDatabase has this table
                // (e.g., materialized sqlite_master virtual table), read-only
                // cursors may fall through to the MemDatabase path below when
                // parity-cert is disabled. Writable cursors must still refuse.
                let has_mem_table = self
                    .db
                    .as_ref()
                    .is_some_and(|db| db.get_table(root_page).is_some());
                if !has_mem_table || writable {
                    // No MemDatabase fallback available — refuse to open.
                    Self::log_open_storage_cursor_fallback_decision(
                        trace_id,
                        certifying_mode,
                        cursor_id,
                        root_page,
                        writable,
                        "none",
                        "invalid_page_no_mem_fallback",
                        "refused",
                        "invalid transaction-backed root page has no allowed MemDatabase fallback",
                    );
                    tracing::warn!(
                        cursor_id,
                        page_id = root_page,
                        writable,
                        has_txn,
                        mode,
                        backend_kind = "none",
                        decision_reason = "invalid_page_no_mem_fallback",
                        first_byte = page_data.first().copied().unwrap_or_default(),
                        is_zero_page,
                        has_mem_table,
                        "open_storage_cursor: refusing on invalid transaction-backed root page"
                    );
                    return false;
                }
                // else: fall through to MemDatabase path
                mem_decision_reason = "txn_page_invalid_mem_fallback";
                Self::log_open_storage_cursor_fallback_decision(
                    trace_id,
                    certifying_mode,
                    cursor_id,
                    root_page,
                    writable,
                    "mem",
                    "txn_page_invalid_mem_fallback",
                    "vdbe_mempage_fallback_allowed",
                    "invalid pager page can be served by read-only MemDatabase table",
                );
                tracing::debug!(
                    cursor_id,
                    page_id = root_page,
                    writable,
                    has_txn,
                    mode,
                    backend_kind = "mem",
                    decision_reason = "txn_page_invalid_mem_fallback",
                    "open_storage_cursor: pager page invalid, falling through to MemDatabase"
                );
            } // end if let Some(ref mut page_io)
        } // end 'pager_block

        // bd-2ttd8.1: Parity-certification mode — reject MemPageStore fallback.
        if self.reject_mem_fallback {
            Self::log_open_storage_cursor_fallback_decision(
                trace_id,
                certifying_mode,
                cursor_id,
                root_page,
                writable,
                "mem",
                "parity_cert_rejection",
                "vdbe_mempage_fallback_rejected",
                "MemPageStore fallback rejected in parity-cert mode",
            );
            tracing::warn!(
                cursor_id,
                page_id = root_page,
                writable,
                has_txn,
                mode,
                backend_kind = "mem",
                decision_reason = "parity_cert_rejection",
                "open_storage_cursor: MemPageStore fallback rejected in parity-cert mode"
            );
            return false;
        }

        // Fallback: build a transient B-tree snapshot (Phase 4 path used by
        // tests without a real pager). Both read and write cursors can operate
        // on empty tables (INSERT needs to work on new tables).
        let is_table_btree = self
            .db
            .as_ref()
            .is_none_or(|db| db.get_table(root_page).is_some());
        let transient_root_pgno = transient_mem_root_pgno(root_pgno);
        let store = if is_table_btree {
            MemPageStore::with_empty_table(transient_root_pgno, self.page_size.get())
        } else {
            MemPageStore::with_empty_index(transient_root_pgno, self.page_size.get())
        };
        let cx = self.derive_execution_cx();
        let mut cursor = BtCursor::new_with_index_desc(
            store,
            transient_root_pgno,
            self.page_size.get(),
            is_table_btree,
            if is_table_btree {
                Vec::new()
            } else {
                self.index_desc_flags_for_root(root_page)
            },
        );
        if !is_table_btree {
            cursor.set_index_collation_context(
                self.index_collations_for_root(root_page),
                Arc::clone(&self.collation_registry),
            );
        }
        // Populate cursor from MemDatabase if available.
        if is_table_btree
            && let Some(table) = self.db.as_ref().and_then(|db| db.get_table(root_page))
        {
            for row in &table.rows {
                let payload = encode_record(&row.values);
                if cursor.table_insert(&cx, row.rowid, &payload).is_err() {
                    return false;
                }
            }
        }

        let ipk_col_idx = self.rowid_alias_col_by_root_page.get(&root_page).copied();
        let table_column_count = self
            .table_column_count_by_root_page
            .get(&root_page)
            .copied();
        let first_not_null_non_ipk_col = self
            .first_not_null_non_ipk_col_by_root_page
            .get(&root_page)
            .copied();
        self.storage_cursors.insert(
            cursor_id,
            StorageCursor {
                cursor: CursorBackend::Mem(cursor),
                cx,
                writable,
                root_page,
                rowid_mode,
                autoincrement_high_water,
                last_alloc_rowid: 0,
                last_successful_insert_rowid: None,
                last_rightmost_unique_index_prefix: None,
                last_rightmost_unique_index_position: None,
                payload_buf: Vec::new(),
                target_vals_buf: Vec::new(),
                cur_vals_buf: Vec::new(),
                row_decode: RowDecodeScratch::default(),
                last_position_stamp: None,
                cached_rowid: None,
                payload_includes_rowid_alias: None,
                ipk_col_idx,
                table_column_count,
                first_not_null_non_ipk_col,
            },
        );
        self.cursor_root_pages.insert(cursor_id, root_page);
        Self::log_open_storage_cursor_fallback_decision(
            trace_id,
            certifying_mode,
            cursor_id,
            root_page,
            writable,
            "mem",
            mem_decision_reason,
            "vdbe_mempage_fallback_allowed",
            "routed through transient MemPageStore fallback",
        );
        tracing::debug!(
            cursor_id,
            page_id = root_page,
            writable,
            has_txn,
            mode,
            backend_kind = "mem",
            decision_reason = mem_decision_reason,
            is_table_btree,
            "open_storage_cursor: routed through MemPageStore fallback"
        );
        true
    }

    fn trace_opcode(&self, pc: usize, op: &VdbeOp) {
        if !self.trace_opcodes || !tracing::enabled!(tracing::Level::TRACE) {
            return;
        }
        let spans = opcode_register_spans(op);
        tracing::trace!(
            target: "fsqlite_vdbe::opcode",
            logging_standard = VDBE_TRACE_LOGGING_STANDARD,
            pc,
            opcode = %op.opcode.name(),
            p1 = op.p1,
            p2 = op.p2,
            p3 = op.p3,
            p5 = op.p5,
            read_start = spans.read_start,
            read_len = spans.read_len,
            write_start = spans.write_start,
            write_len = spans.write_len,
            "executing vdbe opcode",
        );
    }
}

// ── SQLite record encoding ──────────────────────────────────────────────
//
// SQLite `OP_MakeRecord` produces a record in the on-disk record format
// (header + body). Using the same format internally avoids later translation
// when wiring VDBE cursors to the real B-tree layer.

/// SQLite affinity constants (from §3.2 of datatype3.html).
/// Encoded in the lower bits of comparison opcode p5 (masked by 0x47).
const SQLITE_AFF_TEXT: u16 = 0x42; // 'B'
const SQLITE_AFF_NUMERIC: u16 = 0x43; // 'C'

/// C SQLite OP_If/OP_IfNot truthiness: uses `sqlite3VdbeRealValue() != 0.0`,
/// which means 0.1, 0.5, -0.1 etc. are all truthy (unlike integer truncation).
fn vdbe_real_is_truthy(val: &SqliteValue) -> bool {
    match val {
        SqliteValue::Null => false,
        SqliteValue::Integer(n) => *n != 0,
        SqliteValue::Float(f) => *f != 0.0,
        SqliteValue::Text(_) | SqliteValue::Blob(_) => {
            let i = val.to_integer();
            if i != 0 {
                return true;
            }
            val.to_float() != 0.0
        }
    }
}

/// Apply SQLite comparison affinity coercion (§3.2 of datatype3.html).
///
/// Coercion only applies when the comparison opcode's p5 carries a
/// numeric-class affinity (>= NUMERIC / 0x43).  When p5 is 0 or carries
/// BLOB affinity (0x41), no coercion is performed — values compare using
/// their native storage classes (NULL < numeric < text < blob).
fn coerce_for_comparison<'a>(
    lhs: &'a SqliteValue,
    rhs: &'a SqliteValue,
    p5: u16,
) -> (
    std::borrow::Cow<'a, SqliteValue>,
    std::borrow::Cow<'a, SqliteValue>,
) {
    use std::borrow::Cow;

    let affinity = p5 & 0x47_u16; // SQLITE_AFF_MASK

    // TEXT affinity (0x42): convert numeric operands to text for comparison.
    if affinity == SQLITE_AFF_TEXT {
        let coerce_to_text = |v: &SqliteValue| -> Option<SqliteValue> {
            match v {
                SqliteValue::Integer(_) | SqliteValue::Float(_) => {
                    Some(SqliteValue::Text(v.to_text().into()))
                }
                _ => None,
            }
        };
        let new_lhs = coerce_to_text(lhs);
        let new_rhs = coerce_to_text(rhs);
        return (
            new_lhs.map_or_else(|| Cow::Borrowed(lhs), Cow::Owned),
            new_rhs.map_or_else(|| Cow::Borrowed(rhs), Cow::Owned),
        );
    }

    // Numeric affinity (>= 0x43): coerce text→numeric when one side is numeric.
    if affinity >= SQLITE_AFF_NUMERIC {
        let is_numeric =
            |v: &SqliteValue| matches!(v, SqliteValue::Integer(_) | SqliteValue::Float(_));

        if is_numeric(lhs) {
            if let SqliteValue::Text(s) = rhs {
                if let Some(coerced) = try_coerce_text_to_numeric_cmp(s) {
                    return (Cow::Borrowed(lhs), Cow::Owned(coerced));
                }
            }
        }
        if is_numeric(rhs) {
            if let SqliteValue::Text(s) = lhs {
                if let Some(coerced) = try_coerce_text_to_numeric_cmp(s) {
                    return (Cow::Owned(coerced), Cow::Borrowed(rhs));
                }
            }
        }
    }

    (Cow::Borrowed(lhs), Cow::Borrowed(rhs))
}

fn values_equal_without_collation(lhs: &SqliteValue, rhs: &SqliteValue) -> bool {
    if lhs.is_null() || rhs.is_null() {
        return false;
    }
    if let (SqliteValue::Integer(a), SqliteValue::Integer(b)) = (lhs, rhs) {
        return a == b;
    }
    let (cmp_lhs, cmp_rhs) = coerce_for_comparison(lhs, rhs, 0);
    cmp_lhs.partial_cmp(&cmp_rhs) == Some(Ordering::Equal)
}

/// Try to parse a text string as a numeric value for comparison coercion.
fn try_coerce_text_to_numeric_cmp(s: &str) -> Option<SqliteValue> {
    let trimmed = s.trim();
    if trimmed.is_empty() {
        return None;
    }
    // Try integer first.
    if let Ok(i) = trimmed.parse::<i64>() {
        return Some(SqliteValue::Integer(i));
    }
    // Try float.
    if let Ok(f) = trimmed.parse::<f64>() {
        if !f.is_finite() {
            let lower = trimmed.to_ascii_lowercase();
            if lower.contains("inf") || lower.contains("nan") {
                return None;
            }
        }
        return Some(SqliteValue::Float(f));
    }
    None
}

fn collate_compare(
    lhs: &SqliteValue,
    rhs: &SqliteValue,
    coll_name: &str,
    collation_registry: &CollationRegistry,
) -> Option<std::cmp::Ordering> {
    match (lhs, rhs) {
        (SqliteValue::Text(l), SqliteValue::Text(r)) => Some(compare_text_with_collation(
            l.as_bytes(),
            r.as_bytes(),
            coll_name,
            collation_registry,
        )),
        _ => lhs.partial_cmp(rhs),
    }
}

fn compare_text_with_collation(
    left: &[u8],
    right: &[u8],
    coll_name: &str,
    collation_registry: &CollationRegistry,
) -> Ordering {
    collation_registry
        .find(coll_name)
        .map(|collation| collation.compare(left, right))
        .unwrap_or_else(|| left.cmp(right))
}

fn builtin_collation_compare_text(left: &str, right: &str, coll_name: &str) -> Option<Ordering> {
    if coll_name.eq_ignore_ascii_case("BINARY") {
        return Some(left.as_bytes().cmp(right.as_bytes()));
    }
    if coll_name.eq_ignore_ascii_case("NOCASE") {
        return Some(compare_ascii_nocase_bytes(
            left.as_bytes(),
            right.as_bytes(),
        ));
    }
    if coll_name.eq_ignore_ascii_case("RTRIM") {
        return Some(
            trim_rtrim_collation_text(left.as_bytes())
                .cmp(trim_rtrim_collation_text(right.as_bytes())),
        );
    }
    None
}

fn cmp_sqlite_values_collated(a: &SqliteValue, b: &SqliteValue, coll: &str) -> Ordering {
    match (a, b) {
        (SqliteValue::Text(l), SqliteValue::Text(r)) => builtin_collation_compare_text(l, r, coll)
            .unwrap_or_else(|| l.as_bytes().cmp(r.as_bytes())),
        _ => a.partial_cmp(b).unwrap_or(Ordering::Equal),
    }
}

fn agg_step_min_max_collated(
    state: &mut Box<dyn Any + Send>,
    candidate: &SqliteValue,
    is_max: bool,
    coll: &str,
) {
    let current: &mut Option<SqliteValue> = state
        .downcast_mut()
        .expect("MIN/MAX aggregate state must be Option<SqliteValue>");
    match current {
        None => *current = Some(candidate.clone()),
        &mut Some(ref cur) => {
            let ord = cmp_sqlite_values_collated(candidate, cur, coll);
            if (is_max && ord == Ordering::Greater) || (!is_max && ord == Ordering::Less) {
                *current = Some(candidate.clone());
            }
        }
    }
}

fn compare_ascii_nocase_bytes(left: &[u8], right: &[u8]) -> Ordering {
    let shared_len = left.len().min(right.len());
    for idx in 0..shared_len {
        let l = left[idx].to_ascii_lowercase();
        let r = right[idx].to_ascii_lowercase();
        match l.cmp(&r) {
            Ordering::Equal => {}
            non_equal => return non_equal,
        }
    }
    left.len().cmp(&right.len())
}

fn fast_compare_same_storage_class(
    lhs: &SqliteValue,
    rhs: &SqliteValue,
    p4: &P4,
) -> Option<Option<Ordering>> {
    match (lhs, rhs) {
        (SqliteValue::Integer(a), SqliteValue::Integer(b)) => Some(Some(a.cmp(b))),
        (SqliteValue::Float(a), SqliteValue::Float(b)) => Some(a.partial_cmp(b)),
        (SqliteValue::Text(a), SqliteValue::Text(b)) => match p4 {
            P4::Collation(coll_name) => builtin_collation_compare_text(a, b, coll_name).map(Some),
            _ => Some(Some(a.as_bytes().cmp(b.as_bytes()))),
        },
        (SqliteValue::Blob(a), SqliteValue::Blob(b)) if !matches!(p4, P4::Collation(_)) => {
            Some(Some(a.as_ref().cmp(b.as_ref())))
        }
        _ => None,
    }
}

fn extract_collation_names_owned(p4: &P4) -> Vec<Option<String>> {
    match p4 {
        P4::Collation(name) => vec![Some(name.clone())],
        P4::Str(spec) => spec
            .split([',', '|', '\0'])
            .map(str::trim)
            .filter(|entry| !entry.is_empty())
            .map(|s| Some(s.to_owned()))
            .collect(),
        _ => Vec::new(),
    }
}

fn compare_collation_for_field_from_p4(p4: &P4, field_idx: usize) -> Option<&str> {
    match p4 {
        P4::Collation(name) => Some(name.as_str()),
        P4::Str(spec) => {
            let mut idx = 0;
            for entry in spec.split([',', '|', '\0']) {
                let trimmed = entry.trim();
                if trimmed.is_empty() {
                    continue;
                }
                if idx == field_idx {
                    return Some(trimmed);
                }
                idx += 1;
            }
            if idx == 1 && field_idx > 0 {
                for entry in spec.split([',', '|', '\0']) {
                    let trimmed = entry.trim();
                    if !trimmed.is_empty() {
                        return Some(trimmed);
                    }
                }
            }
            None
        }
        _ => None,
    }
}

/// For REPLACE conflict resolution: re-seek the index cursor, find the
/// conflicting entry (which has matching indexed columns but a different
/// rowid), delete it from the index, and return its rowid so the caller
/// can delete the old table row.
fn try_decode_storage_cursor_target_index_record(
    cursor: &mut StorageCursor,
    key_bytes: &[u8],
) -> bool {
    cursor.target_vals_buf.clear();
    fsqlite_types::record::parse_record_into(key_bytes, &mut cursor.target_vals_buf).is_some()
}

fn decode_storage_cursor_target_index_record_strict(
    cursor: &mut StorageCursor,
    key_bytes: &[u8],
    malformed_detail: &'static str,
) -> Result<()> {
    try_decode_storage_cursor_target_index_record(cursor, key_bytes)
        .then_some(())
        .ok_or_else(|| FrankenError::internal(malformed_detail))
}

fn try_decode_storage_cursor_current_index_record(
    cursor_id: i32,
    cursor: &mut StorageCursor,
) -> Result<bool> {
    refresh_storage_cursor_first_key_state(cursor, false);
    cursor.payload_buf.clear();
    cursor
        .cursor
        .payload_into(&cursor.cx, &mut cursor.payload_buf)?;
    cursor.cur_vals_buf.clear();
    let decoded =
        fsqlite_types::record::parse_record_into(&cursor.payload_buf, &mut cursor.cur_vals_buf)
            .is_some();
    cursor.cached_rowid = if decoded {
        cursor.cur_vals_buf.last().and_then(SqliteValue::as_integer)
    } else {
        None
    };
    tracing::trace!(
        cursor_id,
        decoded,
        payload_len = cursor.payload_buf.len(),
        decoded_value_count = cursor.cur_vals_buf.len(),
        scratch_payload_capacity = cursor.payload_buf.capacity(),
        scratch_value_capacity = cursor.cur_vals_buf.capacity(),
        scratch_model = "per_cursor",
        decode_path = "storage_cursor_index_probe",
        "decoded storage cursor index probe into reusable scratch"
    );
    Ok(decoded)
}

fn decode_storage_cursor_current_index_record_strict(
    cursor_id: i32,
    cursor: &mut StorageCursor,
    malformed_detail: &'static str,
) -> Result<()> {
    try_decode_storage_cursor_current_index_record(cursor_id, cursor)?
        .then_some(())
        .ok_or_else(|| FrankenError::internal(malformed_detail))
}

fn storage_cursor_no_conflict_prefix_match(
    cursor_id: i32,
    cursor: &mut StorageCursor,
    key_bytes: &[u8],
) -> Result<bool> {
    if !try_decode_storage_cursor_target_index_record(cursor, key_bytes) {
        return Ok(false);
    }
    cursor.cursor.index_move_to(&cursor.cx, key_bytes)?;
    if cursor.cursor.eof() {
        return Ok(false);
    }
    if !try_decode_storage_cursor_current_index_record(cursor_id, cursor)? {
        return Ok(false);
    }
    let prefix_len = cursor.target_vals_buf.len();
    Ok(cursor.cur_vals_buf.len() >= prefix_len
        && cursor.cur_vals_buf[..prefix_len] == cursor.target_vals_buf[..])
}

fn storage_cursor_no_conflict_prefix_match_collated(
    cursor_id: i32,
    cursor: &mut StorageCursor,
    key_bytes: &[u8],
    desc_flags: &[bool],
    collations: &[Option<String>],
    collation_registry: &CollationRegistry,
) -> Result<bool> {
    if !try_decode_storage_cursor_target_index_record(cursor, key_bytes) {
        return Ok(false);
    }
    let prefix_len = cursor.target_vals_buf.len();
    if !cursor.cursor.first(&cursor.cx)? {
        return Ok(false);
    }
    if prefix_len == 0 {
        return Ok(true);
    }

    loop {
        if !try_decode_storage_cursor_current_index_record(cursor_id, cursor)? {
            return Ok(false);
        }
        if cursor.cur_vals_buf.len() >= prefix_len
            && compare_index_prefix_keys(
                &cursor.cur_vals_buf,
                &cursor.target_vals_buf,
                prefix_len,
                desc_flags,
                collations,
                collation_registry,
            ) == Ordering::Equal
        {
            return Ok(true);
        }
        if !cursor.cursor.next(&cursor.cx)? {
            break;
        }
    }

    Ok(false)
}

fn storage_cursor_exact_match_collated(
    cursor_id: i32,
    cursor: &mut StorageCursor,
    key_bytes: &[u8],
    desc_flags: &[bool],
    collations: &[Option<String>],
    collation_registry: &CollationRegistry,
) -> Result<bool> {
    if !try_decode_storage_cursor_target_index_record(cursor, key_bytes) {
        return Ok(false);
    }
    let key_len = cursor.target_vals_buf.len();
    if !cursor.cursor.first(&cursor.cx)? {
        return Ok(false);
    }

    loop {
        if !try_decode_storage_cursor_current_index_record(cursor_id, cursor)? {
            return Ok(false);
        }
        if cursor.cur_vals_buf.len() == key_len
            && compare_index_prefix_keys(
                &cursor.cur_vals_buf,
                &cursor.target_vals_buf,
                key_len,
                desc_flags,
                collations,
                collation_registry,
            ) == Ordering::Equal
        {
            return Ok(true);
        }
        if !cursor.cursor.next(&cursor.cx)? {
            break;
        }
    }

    Ok(false)
}

fn storage_cursor_seek_collated(
    cursor_id: i32,
    cursor: &mut StorageCursor,
    key_bytes: &[u8],
    opcode: Opcode,
    desc_flags: &[bool],
    collations: &[Option<String>],
    collation_registry: &CollationRegistry,
) -> Result<bool> {
    decode_storage_cursor_target_index_record_strict(
        cursor,
        key_bytes,
        "Seek*: malformed seek key record",
    )?;
    if !cursor.cursor.first(&cursor.cx)? {
        return Ok(false);
    }

    let target_len = cursor.target_vals_buf.len();
    let mut best_values: Option<Vec<SqliteValue>> = None;
    let mut best_key_bytes: Option<Vec<u8>> = None;

    loop {
        decode_storage_cursor_current_index_record_strict(
            cursor_id,
            cursor,
            "Seek*: malformed cursor record",
        )?;

        let compare_len = cursor.cur_vals_buf.len().max(target_len);
        let cmp_to_target = compare_index_prefix_keys(
            &cursor.cur_vals_buf,
            &cursor.target_vals_buf,
            compare_len,
            desc_flags,
            collations,
            collation_registry,
        );
        let relation_matches = match opcode {
            Opcode::SeekGE => cmp_to_target != Ordering::Less,
            Opcode::SeekGT => cmp_to_target == Ordering::Greater,
            Opcode::SeekLE => cmp_to_target != Ordering::Greater,
            Opcode::SeekLT => cmp_to_target == Ordering::Less,
            _ => {
                return Err(FrankenError::internal(
                    "storage_cursor_seek_collated called with non-seek opcode",
                ));
            }
        };

        if relation_matches {
            let replace_best = if let Some(best_values) = best_values.as_ref() {
                let best_compare_len = cursor.cur_vals_buf.len().max(best_values.len());
                let cmp_to_best = compare_index_prefix_keys(
                    &cursor.cur_vals_buf,
                    best_values,
                    best_compare_len,
                    desc_flags,
                    collations,
                    collation_registry,
                );
                match opcode {
                    Opcode::SeekGE | Opcode::SeekGT => cmp_to_best == Ordering::Less,
                    Opcode::SeekLE | Opcode::SeekLT => cmp_to_best == Ordering::Greater,
                    _ => unreachable!(),
                }
            } else {
                true
            };

            if replace_best {
                best_values = Some(cursor.cur_vals_buf.clone());
                best_key_bytes = Some(cursor.payload_buf.clone());
            }
        }

        if !cursor.cursor.next(&cursor.cx)? {
            break;
        }
    }

    if let Some(best_key_bytes) = best_key_bytes {
        cursor.cursor.index_move_to(&cursor.cx, &best_key_bytes)?;
        Ok(true)
    } else {
        Ok(false)
    }
}

fn storage_cursor_current_first_index_key_equals(
    cursor: &mut StorageCursor,
    probe_value: &SqliteValue,
    collect_vdbe_metrics: bool,
    malformed_detail: &'static str,
) -> Result<bool> {
    refresh_storage_cursor_first_key_state(cursor, collect_vdbe_metrics);
    storage_cursor_current_first_index_key_equals_at_current_row(
        cursor,
        probe_value,
        collect_vdbe_metrics,
        malformed_detail,
    )
}

fn storage_cursor_current_first_index_key_equals_at_current_row(
    cursor: &mut StorageCursor,
    probe_value: &SqliteValue,
    collect_vdbe_metrics: bool,
    malformed_detail: &'static str,
) -> Result<bool> {
    if cursor.row_decode.cached_value_ready(0) {
        note_decode_cache_hit(collect_vdbe_metrics);
        let cached = cursor
            .row_decode
            .cached_value(0)
            .ok_or_else(|| FrankenError::internal(malformed_detail))?;
        if collect_vdbe_metrics {
            record_decoded_value_metrics(cached);
        }
        return Ok(storage_cursor_first_index_key_equals_probe(
            cursor,
            cached,
            probe_value,
        ));
    }

    note_decode_cache_miss(collect_vdbe_metrics);
    if let SqliteValue::Integer(probe_int) = probe_value {
        match storage_cursor_probe_first_index_key_integer(cursor, *probe_int, malformed_detail)? {
            FirstIndexKeyIntegerProbe::Match => return Ok(true),
            FirstIndexKeyIntegerProbe::Mismatch(current_value) => {
                cursor
                    .row_decode
                    .cache_decoded(0, SqliteValue::Integer(current_value));
                return Ok(false);
            }
            FirstIndexKeyIntegerProbe::NeedsGenericCompare(fallback_col) => {
                return storage_cursor_current_first_index_key_equals_generic_fallback(
                    cursor,
                    probe_value,
                    collect_vdbe_metrics,
                    malformed_detail,
                    fallback_col,
                );
            }
        }
    }

    let col = storage_cursor_first_index_key_column_offset(cursor, malformed_detail)?;
    storage_cursor_current_first_index_key_equals_generic_fallback(
        cursor,
        probe_value,
        collect_vdbe_metrics,
        malformed_detail,
        col,
    )
}

fn storage_cursor_current_first_index_key_compare(
    cursor: &mut StorageCursor,
    probe_value: &SqliteValue,
    collect_vdbe_metrics: bool,
    malformed_detail: &'static str,
    desc: bool,
    collation: Option<&str>,
    collation_registry: &CollationRegistry,
) -> Result<Ordering> {
    refresh_storage_cursor_first_key_state(cursor, collect_vdbe_metrics);
    storage_cursor_current_first_index_key_compare_at_current_row(
        cursor,
        probe_value,
        collect_vdbe_metrics,
        malformed_detail,
        desc,
        collation,
        collation_registry,
    )
}

fn storage_cursor_current_first_index_key_compare_at_current_row(
    cursor: &mut StorageCursor,
    probe_value: &SqliteValue,
    collect_vdbe_metrics: bool,
    malformed_detail: &'static str,
    desc: bool,
    collation: Option<&str>,
    collation_registry: &CollationRegistry,
) -> Result<Ordering> {
    let compare_ctx = FirstIndexKeyCompareContext {
        desc,
        collation,
        collation_registry,
    };
    if cursor.row_decode.cached_value_ready(0) {
        note_decode_cache_hit(collect_vdbe_metrics);
        let cached = cursor
            .row_decode
            .cached_value(0)
            .ok_or_else(|| FrankenError::internal(malformed_detail))?;
        if collect_vdbe_metrics {
            record_decoded_value_metrics(cached);
        }
        return Ok(compare_first_index_key_values(
            cached,
            probe_value,
            compare_ctx,
        ));
    }

    note_decode_cache_miss(collect_vdbe_metrics);
    if let SqliteValue::Integer(probe_int) = probe_value {
        match storage_cursor_probe_first_index_key_integer(cursor, *probe_int, malformed_detail)? {
            FirstIndexKeyIntegerProbe::Match => return Ok(Ordering::Equal),
            FirstIndexKeyIntegerProbe::Mismatch(current_value) => {
                let current_value = SqliteValue::Integer(current_value);
                let cmp = compare_first_index_key_values(&current_value, probe_value, compare_ctx);
                cursor.row_decode.cache_decoded(0, current_value);
                return Ok(cmp);
            }
            FirstIndexKeyIntegerProbe::NeedsGenericCompare(fallback_col) => {
                return storage_cursor_current_first_index_key_compare_generic_fallback(
                    cursor,
                    probe_value,
                    collect_vdbe_metrics,
                    malformed_detail,
                    fallback_col,
                    compare_ctx,
                );
            }
        }
    }

    let col = storage_cursor_first_index_key_column_offset(cursor, malformed_detail)?;
    storage_cursor_current_first_index_key_compare_generic_fallback(
        cursor,
        probe_value,
        collect_vdbe_metrics,
        malformed_detail,
        col,
        compare_ctx,
    )
}

fn refresh_storage_cursor_first_key_state(cursor: &mut StorageCursor, collect_vdbe_metrics: bool) {
    let position_stamp = cursor.cursor.position_stamp();
    if cursor.last_position_stamp == position_stamp {
        return;
    }

    if let Some(previous_stamp) = cursor.last_position_stamp
        && let Some(current_stamp) = position_stamp
    {
        note_decode_cache_invalidation(
            collect_vdbe_metrics,
            decode_cache_invalidation_reason_from_stamps(previous_stamp, current_stamp),
        );
    } else if cursor.last_position_stamp.is_some() {
        note_decode_cache_invalidation(
            collect_vdbe_metrics,
            DecodeCacheInvalidationReason::PositionChange,
        );
    }
    cursor.row_decode.invalidate();
    cursor.payload_buf.clear();
    cursor.last_position_stamp = position_stamp;
    cursor.cached_rowid = None;
    cursor.payload_includes_rowid_alias = None;
}

fn storage_cursor_cached_rowid(cursor: &mut StorageCursor) -> Result<i64> {
    refresh_storage_cursor_first_key_state(cursor, false);
    if let Some(rowid) = cursor.cached_rowid {
        return Ok(rowid);
    }
    let rowid = cursor.cursor.rowid(&cursor.cx)?;
    cursor.cached_rowid = Some(rowid);
    Ok(rowid)
}

fn storage_cursor_first_index_key_column_offset(
    cursor: &mut StorageCursor,
    malformed_detail: &'static str,
) -> Result<ColumnOffset> {
    const INITIAL_PREFIX_BYTES: usize = 16;

    let mut requested_bytes = INITIAL_PREFIX_BYTES;
    loop {
        ensure_storage_cursor_payload_prefix(cursor, requested_bytes)?;
        let loaded = cursor.payload_buf.len();

        let (header_size_u64, hdr_varint_len) =
            read_varint(&cursor.payload_buf).ok_or_else(|| FrankenError::DatabaseCorrupt {
                detail: malformed_detail.to_owned(),
            })?;
        let header_size =
            usize::try_from(header_size_u64).map_err(|_| FrankenError::DatabaseCorrupt {
                detail: malformed_detail.to_owned(),
            })?;
        if header_size < hdr_varint_len {
            return Err(FrankenError::DatabaseCorrupt {
                detail: malformed_detail.to_owned(),
            });
        }
        if header_size > loaded {
            if loaded < requested_bytes {
                return Err(FrankenError::DatabaseCorrupt {
                    detail: malformed_detail.to_owned(),
                });
            }
            requested_bytes = header_size.max(loaded.saturating_mul(2));
            continue;
        }

        let (serial_type, _) = read_varint(&cursor.payload_buf[hdr_varint_len..header_size])
            .ok_or_else(|| FrankenError::DatabaseCorrupt {
                detail: malformed_detail.to_owned(),
            })?;
        let value_len = usize::try_from(serial_type_len(serial_type).ok_or_else(|| {
            FrankenError::DatabaseCorrupt {
                detail: malformed_detail.to_owned(),
            }
        })?)
        .map_err(|_| FrankenError::DatabaseCorrupt {
            detail: malformed_detail.to_owned(),
        })?;
        let body_offset = header_size;
        let col_end =
            body_offset
                .checked_add(value_len)
                .ok_or_else(|| FrankenError::DatabaseCorrupt {
                    detail: malformed_detail.to_owned(),
                })?;
        if col_end > loaded {
            if loaded < requested_bytes {
                return Err(FrankenError::DatabaseCorrupt {
                    detail: malformed_detail.to_owned(),
                });
            }
            requested_bytes = col_end.max(loaded.saturating_mul(2));
            continue;
        }

        return Ok(ColumnOffset {
            serial_type,
            body_offset: u32::try_from(body_offset).map_err(|_| FrankenError::DatabaseCorrupt {
                detail: malformed_detail.to_owned(),
            })?,
            value_len: u32::try_from(value_len).map_err(|_| FrankenError::DatabaseCorrupt {
                detail: malformed_detail.to_owned(),
            })?,
        });
    }
}

fn storage_cursor_current_first_index_key_equals_generic_fallback(
    cursor: &mut StorageCursor,
    probe_value: &SqliteValue,
    collect_vdbe_metrics: bool,
    malformed_detail: &'static str,
    col: ColumnOffset,
) -> Result<bool> {
    let hint = cursor.row_decode.cached_value(0);
    let value = fsqlite_types::record::decode_column_from_offset_reuse(
        &cursor.payload_buf,
        &col,
        hint,
        collect_vdbe_metrics,
    )
    .ok_or_else(|| FrankenError::internal(malformed_detail))?;
    if collect_vdbe_metrics {
        record_decoded_value_metrics(&value);
    }
    let matches = storage_cursor_first_index_key_equals_probe(cursor, &value, probe_value);
    cursor.row_decode.cache_decoded(0, value);
    Ok(matches)
}

fn storage_cursor_current_first_index_key_compare_generic_fallback(
    cursor: &mut StorageCursor,
    probe_value: &SqliteValue,
    collect_vdbe_metrics: bool,
    malformed_detail: &'static str,
    col: ColumnOffset,
    compare_ctx: FirstIndexKeyCompareContext<'_>,
) -> Result<Ordering> {
    let hint = cursor.row_decode.cached_value(0);
    let value = fsqlite_types::record::decode_column_from_offset_reuse(
        &cursor.payload_buf,
        &col,
        hint,
        collect_vdbe_metrics,
    )
    .ok_or_else(|| FrankenError::internal(malformed_detail))?;
    if collect_vdbe_metrics {
        record_decoded_value_metrics(&value);
    }
    let cmp = compare_first_index_key_values(&value, probe_value, compare_ctx);
    cursor.row_decode.cache_decoded(0, value);
    Ok(cmp)
}

fn storage_cursor_first_index_key_collation(cursor: &StorageCursor) -> Option<&str> {
    cursor
        .cursor
        .index_collations()
        .first()
        .and_then(|collation| collation.as_deref())
        .filter(|name| !name.eq_ignore_ascii_case("BINARY"))
}

fn storage_cursor_first_index_key_equals_probe(
    cursor: &StorageCursor,
    current_value: &SqliteValue,
    probe_value: &SqliteValue,
) -> bool {
    let Some(collation) = storage_cursor_first_index_key_collation(cursor) else {
        return values_equal_without_collation(current_value, probe_value);
    };

    if let (SqliteValue::Text(left), SqliteValue::Text(right)) = (current_value, probe_value)
        && let Some(ordering) = builtin_collation_compare_text(left, right, collation)
    {
        return ordering == Ordering::Equal;
    }

    let registry = cursor.cursor.collation_registry();
    let guard = registry.lock().unwrap_or_else(|err| err.into_inner());
    cmp_values_collated(current_value, probe_value, Some(collation), &guard) == Ordering::Equal
}

#[derive(Clone, Copy)]
struct FirstIndexKeyCompareContext<'a> {
    desc: bool,
    collation: Option<&'a str>,
    collation_registry: &'a CollationRegistry,
}

fn compare_first_index_key_values(
    current_value: &SqliteValue,
    probe_value: &SqliteValue,
    compare_ctx: FirstIndexKeyCompareContext<'_>,
) -> Ordering {
    let mut cmp = cmp_values_collated(
        current_value,
        probe_value,
        compare_ctx.collation,
        compare_ctx.collation_registry,
    );
    if compare_ctx.desc {
        cmp = cmp.reverse();
    }
    cmp
}

enum FirstIndexKeyIntegerProbe {
    Match,
    Mismatch(i64),
    NeedsGenericCompare(ColumnOffset),
}

fn storage_cursor_probe_first_index_key_integer(
    cursor: &mut StorageCursor,
    probe_value: i64,
    malformed_detail: &'static str,
) -> Result<FirstIndexKeyIntegerProbe> {
    use fsqlite_types::serial_type::{SerialTypeClass, classify_serial_type};

    if let Some((matches, current_value)) = cursor
        .cursor
        .try_probe_current_first_index_key_integer_local(probe_value)?
    {
        return Ok(if matches {
            FirstIndexKeyIntegerProbe::Match
        } else {
            FirstIndexKeyIntegerProbe::Mismatch(current_value)
        });
    }

    const INITIAL_PREFIX_BYTES: usize = 16;

    let mut requested_bytes = INITIAL_PREFIX_BYTES;
    loop {
        ensure_storage_cursor_payload_prefix(cursor, requested_bytes)?;
        let loaded = cursor.payload_buf.len();

        let (header_size_u64, hdr_varint_len) =
            read_varint(&cursor.payload_buf).ok_or_else(|| FrankenError::DatabaseCorrupt {
                detail: malformed_detail.to_owned(),
            })?;
        let header_size =
            usize::try_from(header_size_u64).map_err(|_| FrankenError::DatabaseCorrupt {
                detail: malformed_detail.to_owned(),
            })?;
        if header_size < hdr_varint_len {
            return Err(FrankenError::DatabaseCorrupt {
                detail: malformed_detail.to_owned(),
            });
        }
        if header_size > loaded {
            if loaded < requested_bytes {
                return Err(FrankenError::DatabaseCorrupt {
                    detail: malformed_detail.to_owned(),
                });
            }
            requested_bytes = header_size.max(loaded.saturating_mul(2));
            continue;
        }

        let (serial_type, _) = read_varint(&cursor.payload_buf[hdr_varint_len..header_size])
            .ok_or_else(|| FrankenError::DatabaseCorrupt {
                detail: malformed_detail.to_owned(),
            })?;
        let value_len = usize::try_from(serial_type_len(serial_type).ok_or_else(|| {
            FrankenError::DatabaseCorrupt {
                detail: malformed_detail.to_owned(),
            }
        })?)
        .map_err(|_| FrankenError::DatabaseCorrupt {
            detail: malformed_detail.to_owned(),
        })?;
        let body_offset = header_size;
        let col_end =
            body_offset
                .checked_add(value_len)
                .ok_or_else(|| FrankenError::DatabaseCorrupt {
                    detail: malformed_detail.to_owned(),
                })?;
        if col_end > loaded {
            if loaded < requested_bytes {
                return Err(FrankenError::DatabaseCorrupt {
                    detail: malformed_detail.to_owned(),
                });
            }
            requested_bytes = col_end.max(loaded.saturating_mul(2));
            continue;
        }

        let mismatch = |current_value| FirstIndexKeyIntegerProbe::Mismatch(current_value);
        return Ok(match classify_serial_type(serial_type) {
            SerialTypeClass::Zero => {
                if probe_value == 0 {
                    FirstIndexKeyIntegerProbe::Match
                } else {
                    mismatch(0)
                }
            }
            SerialTypeClass::One => {
                if probe_value == 1 {
                    FirstIndexKeyIntegerProbe::Match
                } else {
                    mismatch(1)
                }
            }
            SerialTypeClass::Integer => {
                let bytes = cursor
                    .payload_buf
                    .get(body_offset..col_end)
                    .ok_or_else(|| FrankenError::internal(malformed_detail))?;
                let current_value = decode_big_endian_signed_fast(bytes);
                if current_value == probe_value {
                    FirstIndexKeyIntegerProbe::Match
                } else {
                    mismatch(current_value)
                }
            }
            _ => FirstIndexKeyIntegerProbe::NeedsGenericCompare(ColumnOffset {
                serial_type,
                body_offset: u32::try_from(body_offset).map_err(|_| {
                    FrankenError::DatabaseCorrupt {
                        detail: malformed_detail.to_owned(),
                    }
                })?,
                value_len: u32::try_from(value_len).map_err(|_| FrankenError::DatabaseCorrupt {
                    detail: malformed_detail.to_owned(),
                })?,
            }),
        });
    }
}

#[allow(clippy::cast_possible_wrap)]
fn decode_big_endian_signed_fast(bytes: &[u8]) -> i64 {
    match bytes.len() {
        0 => 0,
        1 => bytes[0] as i8 as i64,
        2 => {
            let mut buf = [0_u8; 2];
            buf.copy_from_slice(bytes);
            i16::from_be_bytes(buf) as i64
        }
        3 => {
            let mut buf = [if bytes[0] & 0x80 != 0 { 0xFF } else { 0 }; 4];
            buf[1..4].copy_from_slice(bytes);
            i32::from_be_bytes(buf) as i64
        }
        4 => {
            let mut buf = [0_u8; 4];
            buf.copy_from_slice(bytes);
            i32::from_be_bytes(buf) as i64
        }
        6 => {
            let mut buf = [if bytes[0] & 0x80 != 0 { 0xFF } else { 0 }; 8];
            buf[2..8].copy_from_slice(bytes);
            i64::from_be_bytes(buf)
        }
        8 => {
            let mut buf = [0_u8; 8];
            buf.copy_from_slice(bytes);
            i64::from_be_bytes(buf)
        }
        _ => {
            let negative = bytes.first().is_some_and(|&b| b & 0x80 != 0);
            let mut value: u64 = if negative { u64::MAX } else { 0 };
            for &b in bytes {
                value = (value << 8) | u64::from(b);
            }
            value as i64
        }
    }
}

fn storage_cursor_count_equal_first_key_run(
    cursor: &mut StorageCursor,
    probe_value: &SqliteValue,
    collect_vdbe_metrics: bool,
) -> Result<i64> {
    if let SqliteValue::Integer(probe_int) = probe_value {
        return storage_cursor_count_equal_first_key_run_integer_probe(
            cursor,
            *probe_int,
            collect_vdbe_metrics,
        );
    }

    let mut matched = 0_i64;
    while !cursor.cursor.eof() {
        if !storage_cursor_current_first_index_key_equals(
            cursor,
            probe_value,
            collect_vdbe_metrics,
            "CountIndexEqRun: malformed index entry record",
        )? {
            break;
        }
        matched = matched.wrapping_add(1);
        if !cursor.cursor.next(&cursor.cx)? {
            break;
        }
    }
    Ok(matched)
}

fn storage_cursor_count_equal_first_key_run_integer_probe(
    cursor: &mut StorageCursor,
    probe_int: i64,
    collect_vdbe_metrics: bool,
) -> Result<i64> {
    let mut matched = 0_i64;
    let probe_value = SqliteValue::Integer(probe_int);
    let mut payload_position_stamp = None;
    while !cursor.cursor.eof() {
        match cursor
            .cursor
            .count_equal_first_index_key_run_integer_local_segment(&cursor.cx, probe_int)?
        {
            FirstIndexKeyIntegerLocalRunSegment::Matched(local_matched) => {
                matched = matched.wrapping_add(local_matched);
                continue;
            }
            FirstIndexKeyIntegerLocalRunSegment::Mismatch {
                matched: local_matched,
                current_value,
            } => {
                matched = matched.wrapping_add(local_matched);
                cursor.row_decode.invalidate();
                cursor
                    .row_decode
                    .cache_decoded(0, SqliteValue::Integer(current_value));
                break;
            }
            FirstIndexKeyIntegerLocalRunSegment::NeedsFallback {
                matched: local_matched,
            } => {
                matched = matched.wrapping_add(local_matched);
            }
        }

        if cursor.cursor.eof() {
            break;
        }

        let position_stamp = cursor.cursor.position_stamp();
        if payload_position_stamp != position_stamp {
            cursor.payload_buf.clear();
            payload_position_stamp = position_stamp;
        }
        match storage_cursor_probe_first_index_key_integer(
            cursor,
            probe_int,
            "CountIndexEqRun: malformed index entry record",
        )? {
            FirstIndexKeyIntegerProbe::Match => {}
            FirstIndexKeyIntegerProbe::Mismatch(current_value) => {
                cursor.row_decode.invalidate();
                cursor
                    .row_decode
                    .cache_decoded(0, SqliteValue::Integer(current_value));
                break;
            }
            FirstIndexKeyIntegerProbe::NeedsGenericCompare(col) => {
                cursor.row_decode.invalidate();
                if !storage_cursor_current_first_index_key_equals_generic_fallback(
                    cursor,
                    &probe_value,
                    collect_vdbe_metrics,
                    "CountIndexEqRun: malformed index entry record",
                    col,
                )? {
                    break;
                }
            }
        }

        matched = matched.wrapping_add(1);
        if !cursor.cursor.next(&cursor.cx)? {
            break;
        }
    }
    Ok(matched)
}

fn find_conflicting_rowid_in_index(
    sc: &mut StorageCursor,
    key_bytes: &[u8],
    n_idx_cols: usize,
) -> Result<Option<i64>> {
    // Re-seek to the position where the conflicting entry should be.
    sc.cursor.index_move_to(&sc.cx, key_bytes)?;

    sc.cur_vals_buf.clear();

    // The new key we're trying to insert — parse its prefix for comparison.
    decode_storage_cursor_target_index_record_strict(
        sc,
        key_bytes,
        "find_conflicting_rowid: malformed new index key",
    )?;

    // Check the entry at current position and previous entry for a prefix match.
    for attempt in 0..2 {
        if sc.cursor.eof() {
            if attempt == 0 {
                // Try moving to the previous entry.
                sc.cursor.prev(&sc.cx)?;
                continue;
            }
            break;
        }

        decode_storage_cursor_current_index_record_strict(
            -1,
            sc,
            "find_conflicting_rowid: malformed index entry record",
        )?;

        // Check if the indexed columns (excluding the trailing rowid) match
        // and none of them are NULL.
        let mut prefix_match = true;
        let mut has_null = false;
        for i in 0..n_idx_cols {
            let new_val = sc.target_vals_buf.get(i);
            let entry_val = sc.cur_vals_buf.get(i);
            if matches!(new_val, Some(SqliteValue::Null) | None)
                || matches!(entry_val, Some(SqliteValue::Null) | None)
            {
                has_null = true;
                break;
            }
            if new_val != entry_val {
                prefix_match = false;
                break;
            }
        }

        if prefix_match && !has_null {
            let old_rowid = index_entry_rowid_at(
                &sc.cur_vals_buf,
                n_idx_cols,
                "find_conflicting_rowid: index entry must end with exactly one integer rowid suffix",
            )?;

            // Delete the conflicting index entry.
            sc.cursor.delete(&sc.cx)?;
            invalidate_storage_cursor_row_cache(sc);

            return Ok(Some(old_rowid));
        }

        if attempt == 0 {
            sc.cursor.prev(&sc.cx)?;
        }
    }

    Ok(None)
}

fn find_conflicting_rowid_in_index_collated(
    sc: &mut StorageCursor,
    key_bytes: &[u8],
    n_idx_cols: usize,
    desc_flags: &[bool],
    collations: &[Option<String>],
    collation_registry: &CollationRegistry,
) -> Result<Option<i64>> {
    let target_values = parse_record(key_bytes).ok_or_else(|| {
        FrankenError::internal("find_conflicting_rowid_in_index_collated: malformed new index key")
    })?;
    if target_values.len() < n_idx_cols
        || target_values
            .iter()
            .take(n_idx_cols)
            .any(SqliteValue::is_null)
    {
        return Ok(None);
    }

    if !sc.cursor.first(&sc.cx)? {
        return Ok(None);
    }

    loop {
        let existing_key = sc.cursor.payload(&sc.cx)?;
        let existing_values = parse_record(&existing_key).ok_or_else(|| {
            FrankenError::internal(
                "find_conflicting_rowid_in_index_collated: malformed index entry record",
            )
        })?;
        if existing_values.len() >= n_idx_cols
            && existing_values
                .iter()
                .take(n_idx_cols)
                .all(|value| !value.is_null())
            && compare_index_prefix_keys(
                &existing_values,
                &target_values,
                n_idx_cols,
                desc_flags,
                collations,
                collation_registry,
            ) == Ordering::Equal
        {
            let rowid = index_entry_rowid_at(
                &existing_values,
                n_idx_cols,
                "find_conflicting_rowid_in_index_collated: index entry must end with exactly one integer rowid suffix",
            )?;
            return Ok(Some(rowid));
        }

        if !sc.cursor.next(&sc.cx)? {
            break;
        }
    }

    Ok(None)
}

fn index_entry_rowid_at(
    values: &[SqliteValue],
    n_idx_cols: usize,
    corrupt_detail: &str,
) -> Result<i64> {
    let expected_len = n_idx_cols
        .checked_add(1)
        .ok_or_else(|| FrankenError::DatabaseCorrupt {
            detail: corrupt_detail.to_owned(),
        })?;
    if values.len() != expected_len {
        return Err(FrankenError::DatabaseCorrupt {
            detail: corrupt_detail.to_owned(),
        });
    }
    values[n_idx_cols]
        .as_integer()
        .ok_or_else(|| FrankenError::DatabaseCorrupt {
            detail: corrupt_detail.to_owned(),
        })
}

fn encode_record(values: &[SqliteValue]) -> Vec<u8> {
    serialize_record(values)
}

#[allow(dead_code)]
fn payload_includes_rowid_alias(
    payload_values: &[SqliteValue],
    _rowid: i64,
    ipk_col_idx: usize,
    table_column_count: Option<usize>,
    first_not_null_non_ipk_col_idx: Option<usize>,
) -> bool {
    let payload_cols = payload_values.len();
    if let Some(table_cols) = table_column_count {
        if payload_cols == table_cols {
            return true;
        }
        let one_column_short = payload_cols.checked_add(1) == Some(table_cols);
        if one_column_short && matches!(payload_values.get(ipk_col_idx), Some(SqliteValue::Null)) {
            return true;
        }
        if let Some(not_null_col_idx) =
            first_not_null_non_ipk_col_idx.filter(|idx| *idx > ipk_col_idx)
        {
            let omitted_payload_idx = not_null_col_idx - 1;
            let present_payload_idx = not_null_col_idx;
            if omitted_payload_idx < payload_cols
                && present_payload_idx < payload_cols
                && matches!(
                    payload_values.get(omitted_payload_idx),
                    Some(SqliteValue::Null)
                )
            {
                return true;
            }
        }
        // Rows can be more than one column short after repeated ALTER TABLE
        // ADD COLUMN operations while still carrying SQLite's NULL IPK slot.
        if matches!(payload_values.get(ipk_col_idx), Some(SqliteValue::Null)) {
            return true;
        }
        return false;
    }
    if payload_cols <= ipk_col_idx {
        return false;
    }

    matches!(payload_values.get(ipk_col_idx), Some(SqliteValue::Null))
}

#[allow(dead_code)]
fn payload_includes_rowid_alias_lazy(
    row_decode: &RowDecodeScratch,
    _record: &[u8],
    _rowid: i64,
    ipk_col_idx: usize,
    table_column_count: Option<usize>,
    first_not_null_non_ipk_col_idx: Option<usize>,
) -> bool {
    payload_includes_rowid_alias_without_rowid(
        row_decode,
        ipk_col_idx,
        table_column_count,
        first_not_null_non_ipk_col_idx,
    )
}

/// Resolve the rowid-alias payload shape from record-header metadata alone.
///
/// This stays conservative for non-NULL IPK-position values: a shifted user
/// column can coincidentally equal the physical rowid, so equality is not
/// evidence that the IPK alias slot is present.
fn payload_includes_rowid_alias_without_rowid(
    row_decode: &RowDecodeScratch,
    ipk_col_idx: usize,
    table_column_count: Option<usize>,
    first_not_null_non_ipk_col_idx: Option<usize>,
) -> bool {
    use fsqlite_types::serial_type::{SerialTypeClass, classify_serial_type};

    let payload_cols = row_decode.column_count();
    if let Some(table_cols) = table_column_count {
        if payload_cols == table_cols {
            return true;
        }
        let one_column_short = payload_cols.checked_add(1) == Some(table_cols);
        if let Some(not_null_col_idx) =
            first_not_null_non_ipk_col_idx.filter(|idx| *idx > ipk_col_idx)
        {
            let omitted_payload_idx = not_null_col_idx - 1;
            let present_payload_idx = not_null_col_idx;
            if omitted_payload_idx < payload_cols
                && present_payload_idx < payload_cols
                && let Some(col) = row_decode.column_offset(omitted_payload_idx)
                && matches!(classify_serial_type(col.serial_type), SerialTypeClass::Null)
            {
                return true;
            }
        }
        if one_column_short {
            let Some(col) = row_decode.column_offset(ipk_col_idx) else {
                return false;
            };
            return matches!(classify_serial_type(col.serial_type), SerialTypeClass::Null);
        }
        if ipk_col_idx >= payload_cols {
            return false;
        }
        let Some(col) = row_decode.column_offset(ipk_col_idx) else {
            return false;
        };
        return matches!(classify_serial_type(col.serial_type), SerialTypeClass::Null);
    }
    if ipk_col_idx >= payload_cols {
        return false;
    }
    let Some(col) = row_decode.column_offset(ipk_col_idx) else {
        return false;
    };
    matches!(classify_serial_type(col.serial_type), SerialTypeClass::Null)
}

fn column_payload_end(col: &ColumnOffset) -> Option<usize> {
    let start = usize::try_from(col.body_offset).ok()?;
    let len = usize::try_from(col.value_len).ok()?;
    start.checked_add(len)
}

fn row_decode_payload_end(row_decode: &RowDecodeScratch) -> Option<usize> {
    let mut end = 0_usize;
    for idx in 0..row_decode.column_count() {
        end = end.max(column_payload_end(row_decode.column_offset(idx)?)?);
    }
    Some(end)
}

#[derive(Debug, Clone, Copy, Default)]
struct DecodeCacheRefreshState {
    refreshed: bool,
    eager_values_ready: bool,
}

fn decode_cache_invalidation_reason_from_stamps(
    previous: CursorPositionStamp,
    current: CursorPositionStamp,
) -> DecodeCacheInvalidationReason {
    if previous.same_logical_position(current) {
        DecodeCacheInvalidationReason::WriteMutation
    } else {
        DecodeCacheInvalidationReason::PositionChange
    }
}

fn ensure_storage_cursor_payload_prefix(
    cursor: &mut StorageCursor,
    min_payload_bytes: usize,
) -> Result<()> {
    if cursor.payload_buf.len() >= min_payload_bytes {
        return Ok(());
    }
    cursor
        .cursor
        .payload_prefix_into(&cursor.cx, min_payload_bytes, &mut cursor.payload_buf)
}

fn ensure_storage_cursor_row_layout(
    cursor: &mut StorageCursor,
    min_payload_bytes: usize,
    collect_vdbe_metrics: bool,
) -> Result<DecodeCacheRefreshState> {
    let position_stamp = cursor.cursor.position_stamp();
    let mut refreshed = false;
    if cursor.last_position_stamp != position_stamp {
        if let Some(previous_stamp) = cursor.last_position_stamp
            && let Some(current_stamp) = position_stamp
        {
            note_decode_cache_invalidation(
                collect_vdbe_metrics,
                decode_cache_invalidation_reason_from_stamps(previous_stamp, current_stamp),
            );
        } else if cursor.last_position_stamp.is_some() {
            note_decode_cache_invalidation(
                collect_vdbe_metrics,
                DecodeCacheInvalidationReason::PositionChange,
            );
        }
        cursor.row_decode.invalidate();
        cursor.payload_buf.clear();
        cursor.last_position_stamp = position_stamp;
        cursor.cached_rowid = None;
        cursor.payload_includes_rowid_alias = None;
        refreshed = true;
    }

    if !refreshed && !cursor.row_decode.is_empty() && cursor.payload_buf.len() >= min_payload_bytes
    {
        return Ok(DecodeCacheRefreshState {
            refreshed: false,
            eager_values_ready: false,
        });
    }

    let required_bytes = min_payload_bytes;
    let mut requested_bytes = min_payload_bytes.max(STORAGE_CURSOR_LAYOUT_PREFIX_BYTES);

    loop {
        ensure_storage_cursor_payload_prefix(cursor, requested_bytes)?;

        if cursor.row_decode.is_empty() {
            if cursor
                .row_decode
                .prepare_for_record_prefix(&cursor.payload_buf)
                .is_none()
            {
                let loaded = cursor.payload_buf.len();
                if loaded < requested_bytes {
                    return Err(FrankenError::DatabaseCorrupt {
                        detail: "malformed record header or payload in cursor payload".to_owned(),
                    });
                }
                requested_bytes = requested_bytes
                    .saturating_mul(2)
                    .max(loaded.saturating_add(1));
                continue;
            }
        }

        if cursor.payload_buf.len() < required_bytes {
            let loaded = cursor.payload_buf.len();
            if loaded < requested_bytes {
                return Err(FrankenError::DatabaseCorrupt {
                    detail: format!(
                        "cursor payload shorter than required prefix: need {required_bytes}, have {loaded}"
                    ),
                });
            }
            requested_bytes = required_bytes.max(loaded.saturating_mul(2));
            continue;
        }

        let eager_values_ready = if cursor.row_decode.column_count() > 64
            && !cursor.row_decode.cached_value_ready(64)
            && row_decode_payload_end(&cursor.row_decode)
                .is_some_and(|record_len| cursor.payload_buf.len() >= record_len)
        {
            cursor
                .row_decode
                .prepare_for_record(&cursor.payload_buf)
                .ok_or_else(|| FrankenError::DatabaseCorrupt {
                    detail: "malformed wide record in cursor payload".to_owned(),
                })?
        } else {
            false
        };

        return Ok(DecodeCacheRefreshState {
            refreshed,
            eager_values_ready,
        });
    }
}

fn ensure_sorter_row_cache(
    sorter: &mut SorterCursor,
    collect_vdbe_metrics: bool,
    position: usize,
) -> Result<DecodeCacheRefreshState> {
    let (rows, cached_row_position, cached_row_decode) = (
        &sorter.rows,
        &mut sorter.cached_row_position,
        &mut sorter.cached_row_decode,
    );
    if *cached_row_position == Some(position) {
        return Ok(DecodeCacheRefreshState::default());
    }
    let row = rows
        .get(position)
        .ok_or_else(|| FrankenError::DatabaseCorrupt {
            detail: format!("missing sorter row at position {position}"),
        })?;
    if cached_row_position.is_some() {
        note_decode_cache_invalidation(
            collect_vdbe_metrics,
            DecodeCacheInvalidationReason::PositionChange,
        );
    }
    let eager_values_ready = cached_row_decode
        .prepare_for_record(&row.blob)
        .ok_or_else(|| FrankenError::DatabaseCorrupt {
            detail: "malformed sorter record header or payload".to_owned(),
        })?;
    *cached_row_position = Some(position);
    Ok(DecodeCacheRefreshState {
        refreshed: true,
        eager_values_ready,
    })
}

fn invalidate_storage_cursor_row_cache_with_reason(
    cursor: &mut StorageCursor,
    collect_vdbe_metrics: bool,
    reason: DecodeCacheInvalidationReason,
) {
    if cursor.last_position_stamp.is_some() || !cursor.row_decode.is_empty() {
        note_decode_cache_invalidation(collect_vdbe_metrics, reason);
    }
    cursor.row_decode.invalidate();
    cursor.last_position_stamp = None;
    cursor.cached_rowid = None;
    cursor.payload_includes_rowid_alias = None;
}

fn invalidate_storage_cursor_row_cache(cursor: &mut StorageCursor) {
    invalidate_storage_cursor_row_cache_with_reason(
        cursor,
        false,
        DecodeCacheInvalidationReason::WriteMutation,
    );
}

#[allow(dead_code)]
fn encode_record_refs(values: &[&SqliteValue]) -> Vec<u8> {
    fsqlite_types::record::serialize_record_refs(values)
}

/// Extract the raw bytes from a record blob value (output of `MakeRecord`).
fn record_blob_bytes(val: &SqliteValue) -> &[u8] {
    match val {
        SqliteValue::Blob(bytes) => bytes,
        _ => &[],
    }
}

fn decode_record_with_metrics(
    val: &SqliteValue,
    collect_vdbe_metrics: bool,
) -> Result<Vec<SqliteValue>> {
    let SqliteValue::Blob(bytes) = val else {
        return Ok(Vec::new());
    };

    let _profile_stage = enter_vdbe_decode_profile_stage();
    let values = parse_record(bytes)
        .ok_or_else(|| FrankenError::internal("malformed SQLite record blob"))?;
    if collect_vdbe_metrics {
        FSQLITE_VDBE_RECORD_DECODE_CALLS_TOTAL.fetch_add(1, AtomicOrdering::Relaxed);
        for value in &values {
            record_decoded_value_metrics(value);
        }
    }
    Ok(values)
}

#[cfg(test)]
fn decode_record(val: &SqliteValue) -> Result<Vec<SqliteValue>> {
    decode_record_with_metrics(val, vdbe_metrics_enabled())
}

fn sorter_keys_equal(
    lhs: &[SqliteValue],
    rhs: &[SqliteValue],
    key_columns: usize,
    collations: &[Option<String>],
    collation_registry: &CollationRegistry,
) -> bool {
    compare_sorter_keys(lhs, rhs, key_columns, collations, collation_registry) == Ordering::Equal
}

fn compare_sorter_keys(
    lhs: &[SqliteValue],
    rhs: &[SqliteValue],
    key_columns: usize,
    collations: &[Option<String>],
    collation_registry: &CollationRegistry,
) -> Ordering {
    let key_count = key_columns.max(1);
    for idx in 0..key_count {
        let Some(lhs_value) = lhs.get(idx) else {
            return if rhs.get(idx).is_some() {
                Ordering::Less
            } else {
                break;
            };
        };
        let Some(rhs_value) = rhs.get(idx) else {
            return Ordering::Greater;
        };

        let coll = collations.get(idx).and_then(|c| c.as_deref());
        match cmp_values_collated(lhs_value, rhs_value, coll, collation_registry) {
            Ordering::Equal => {}
            non_equal => return non_equal,
        }
    }
    Ordering::Equal
}

fn parse_non_null_index_prefix(key_bytes: &[u8], key_columns: usize) -> Option<Vec<SqliteValue>> {
    let fields = parse_record(key_bytes)?;
    if fields.len() < key_columns || fields.iter().take(key_columns).any(SqliteValue::is_null) {
        return None;
    }
    Some(fields.into_iter().take(key_columns).collect())
}

fn compare_index_prefix_keys(
    lhs: &[SqliteValue],
    rhs: &[SqliteValue],
    key_columns: usize,
    desc_flags: &[bool],
    collations: &[Option<String>],
    collation_registry: &CollationRegistry,
) -> Ordering {
    let key_count = key_columns.max(1);
    for idx in 0..key_count {
        let Some(lhs_value) = lhs.get(idx) else {
            return if rhs.get(idx).is_some() {
                Ordering::Less
            } else {
                break;
            };
        };
        let Some(rhs_value) = rhs.get(idx) else {
            return Ordering::Greater;
        };

        let coll = collations.get(idx).and_then(|c| c.as_deref());
        let mut ord = cmp_values_collated(lhs_value, rhs_value, coll, collation_registry);
        if desc_flags.get(idx).copied().unwrap_or(false) {
            ord = ord.reverse();
        }
        if ord != Ordering::Equal {
            return ord;
        }
    }
    Ordering::Equal
}

fn idx_compare_condition_met(opcode: Opcode, cmp: Ordering) -> bool {
    match opcode {
        Opcode::IdxLE => cmp != Ordering::Greater,
        Opcode::IdxGT => cmp == Ordering::Greater,
        Opcode::IdxLT => cmp == Ordering::Less,
        Opcode::IdxGE => cmp != Ordering::Less,
        _ => unreachable!("idx_compare_condition_met called with non-Idx comparison opcode"),
    }
}

/// Compare two `SqliteValue`s with an optional collation sequence.
///
/// Text values consult the collation registry so dynamically loaded
/// collations participate in ORDER BY, DISTINCT, and index probes.
fn cmp_values_collated(
    lhs: &SqliteValue,
    rhs: &SqliteValue,
    collation: Option<&str>,
    collation_registry: &CollationRegistry,
) -> Ordering {
    if let (Some(coll), SqliteValue::Text(lt), SqliteValue::Text(rt)) = (collation, lhs, rhs) {
        return compare_text_with_collation(lt.as_bytes(), rt.as_bytes(), coll, collation_registry);
    }
    lhs.partial_cmp(rhs).unwrap_or(Ordering::Equal)
}

fn compare_sorter_rows(
    lhs: &[SqliteValue],
    rhs: &[SqliteValue],
    key_columns: usize,
    sort_key_orders: &[SortKeyOrder],
    collations: &[Option<String>],
    collation_registry: &CollationRegistry,
) -> Ordering {
    let key_count = key_columns.max(1);
    for idx in 0..key_count {
        let order = sort_key_orders
            .get(idx)
            .copied()
            .unwrap_or(SortKeyOrder::Asc);
        let is_desc = matches!(order, SortKeyOrder::Desc | SortKeyOrder::DescNullsFirst);
        let nulls_last = matches!(order, SortKeyOrder::Desc | SortKeyOrder::AscNullsLast);
        let Some(lhs_value) = lhs.get(idx) else {
            return if rhs.get(idx).is_some() {
                if is_desc {
                    Ordering::Greater
                } else {
                    Ordering::Less
                }
            } else {
                break;
            };
        };
        let Some(rhs_value) = rhs.get(idx) else {
            return if is_desc {
                Ordering::Less
            } else {
                Ordering::Greater
            };
        };

        // Handle NULLs with explicit NULLS FIRST/LAST ordering.
        let l_null = lhs_value.is_null();
        let r_null = rhs_value.is_null();
        if l_null || r_null {
            if l_null && r_null {
                continue;
            }
            // l_null + nulls_last → Greater (NULL at end)
            // l_null + !nulls_last → Less (NULL at start)
            // !l_null + nulls_last → Less (non-NULL before NULL)
            // !l_null + !nulls_last → Greater (non-NULL after NULL)
            return if l_null == nulls_last {
                Ordering::Greater
            } else {
                Ordering::Less
            };
        }

        let coll = collations.get(idx).and_then(|c| c.as_deref());
        let mut ord = cmp_values_collated(lhs_value, rhs_value, coll, collation_registry);
        if ord == Ordering::Equal {
            continue;
        }

        if is_desc {
            ord = ord.reverse();
        }
        return ord;
    }

    // Rust's sort_by is stable, so equal-key rows stay in insertion order,
    // matching C SQLite's behavior (especially for COLLATE NOCASE DISTINCT).
    Ordering::Equal
}

fn opcode_trace_enabled() -> bool {
    let env_enabled = std::env::var(VDBE_TRACE_ENV).is_ok_and(|value| {
        let normalized = value.trim().to_ascii_lowercase();
        !normalized.is_empty() && normalized != "0" && normalized != "false" && normalized != "off"
    });
    env_enabled || cfg!(test)
}

// ── Arithmetic helpers ──────────────────────────────────────────────────────

/// Mirrors C SQLite `numericType()` (SQLite VDBE:496): returns true if BOTH
/// operands should be treated as integers for arithmetic purposes.
/// Text/Blob that parse as i64 are integer-typed; Float is not.
fn both_integer_numeric_type(a: &SqliteValue, b: &SqliteValue) -> bool {
    a.is_integer_numeric_type() && b.is_integer_numeric_type()
}

/// SQL division with NULL propagation and division-by-zero handling.
#[allow(clippy::cast_precision_loss)]
fn sql_div(dividend: &SqliteValue, divisor: &SqliteValue) -> SqliteValue {
    if dividend.is_null() || divisor.is_null() {
        return SqliteValue::Null;
    }
    // C SQLite numericType() coercion (SQLite VDBE:1932-1934): if both operands
    // are integer-typed (including text that parses as integer), use int math.
    let both_int = both_integer_numeric_type(dividend, divisor);
    if both_int {
        let a = dividend.to_integer();
        let b = divisor.to_integer();
        if b == 0 {
            SqliteValue::Null
        } else {
            match a.checked_div(b) {
                Some(result) => SqliteValue::Integer(result),
                // i64::MIN / -1 overflows; C SQLite promotes to float via
                // `goto fp_math` (SQLite VDBE:1916), NOT wrapping.
                #[allow(clippy::cast_precision_loss)]
                None => {
                    let result = a as f64 / b as f64;
                    if result.is_nan() {
                        SqliteValue::Null
                    } else {
                        SqliteValue::Float(result)
                    }
                }
            }
        }
    } else {
        let b = divisor.to_float();
        if b == 0.0 {
            SqliteValue::Null
        } else {
            let result = dividend.to_float() / b;
            if result.is_nan() {
                SqliteValue::Null
            } else {
                SqliteValue::Float(result)
            }
        }
    }
}

/// SQL remainder with NULL propagation and division-by-zero handling.
///
/// C SQLite (SQLite VDBE:1920): when both operands are MEM_Int, result is Integer.
/// When either is Float/Text/Blob, fp_math path casts to integer for the
/// modulo but stores the result as Float (MEM_Real).
/// C SQLite `numericType()` coercion: text that parses as integer is treated
/// as integer for the both-int check (SQLite VDBE:1932-1934).
#[allow(clippy::cast_precision_loss)]
fn sql_rem(dividend: &SqliteValue, divisor: &SqliteValue) -> SqliteValue {
    if dividend.is_null() || divisor.is_null() {
        return SqliteValue::Null;
    }
    let both_int = both_integer_numeric_type(dividend, divisor);
    let a = dividend.to_integer();
    let b = divisor.to_integer();
    if b == 0 {
        return SqliteValue::Null;
    }
    // i64::MIN % -1 = 0 mathematically.
    let result = a.checked_rem(b).unwrap_or_default();
    if both_int {
        SqliteValue::Integer(result)
    } else {
        SqliteValue::Float(result as f64)
    }
}

/// SQL shift left (SQLite semantics: negative shift = shift right).
fn sql_shift_left(val: i64, amount: i64) -> SqliteValue {
    if amount < 0 {
        return sql_shift_right(val, amount.saturating_neg());
    }
    if amount >= 64 {
        return SqliteValue::Integer(0);
    }
    // amount is in [0, 63] so the cast is safe.
    #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
    let shift = amount as u32;
    SqliteValue::Integer(val << shift)
}

/// SQL shift right (SQLite semantics: negative shift = shift left).
fn sql_shift_right(val: i64, amount: i64) -> SqliteValue {
    if amount < 0 {
        return sql_shift_left(val, amount.saturating_neg());
    }
    if amount >= 64 {
        return SqliteValue::Integer(if val < 0 { -1 } else { 0 });
    }
    // amount is in [0, 63] so the cast is safe.
    #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
    let shift = amount as u32;
    SqliteValue::Integer(val >> shift)
}

/// Three-valued SQL AND.
fn sql_and(a: &SqliteValue, b: &SqliteValue) -> SqliteValue {
    // C SQLite compiles AND/OR using OP_If/OP_IfNot which use
    // sqlite3VdbeRealValue() != 0.0, so 0.5 is truthy.
    let a_val = if a.is_null() {
        None
    } else {
        Some(vdbe_real_is_truthy(a))
    };
    let b_val = if b.is_null() {
        None
    } else {
        Some(vdbe_real_is_truthy(b))
    };

    match (a_val, b_val) {
        (Some(false), _) | (_, Some(false)) => SqliteValue::Integer(0),
        (Some(true), Some(true)) => SqliteValue::Integer(1),
        _ => SqliteValue::Null,
    }
}

/// Three-valued SQL OR.
fn sql_or(a: &SqliteValue, b: &SqliteValue) -> SqliteValue {
    let a_val = if a.is_null() {
        None
    } else {
        Some(vdbe_real_is_truthy(a))
    };
    let b_val = if b.is_null() {
        None
    } else {
        Some(vdbe_real_is_truthy(b))
    };

    match (a_val, b_val) {
        (Some(true), _) | (_, Some(true)) => SqliteValue::Integer(1),
        (Some(false), Some(false)) => SqliteValue::Integer(0),
        _ => SqliteValue::Null,
    }
}

/// Scan the leading numeric prefix from a byte slice.
///
/// Recognises `[+-]? [0-9]* ('.' [0-9]*)? ([eE] [+-]? [0-9]+)?`.
/// Returns the byte offset where the prefix ends (0 if no prefix).
fn scan_numeric_prefix(bytes: &[u8]) -> usize {
    if bytes.is_empty() {
        return 0;
    }
    let mut i = 0;
    // Optional leading sign.
    if bytes[i] == b'+' || bytes[i] == b'-' {
        i += 1;
    }
    let digit_start = i;
    // Integer digits.
    while i < bytes.len() && bytes[i].is_ascii_digit() {
        i += 1;
    }
    // Optional decimal part.
    if i < bytes.len() && bytes[i] == b'.' {
        i += 1;
        while i < bytes.len() && bytes[i].is_ascii_digit() {
            i += 1;
        }
    }
    // Must have consumed at least one digit.
    if i == digit_start {
        return 0;
    }
    // Optional exponent (e.g. e+10, E-3, e5).
    if i < bytes.len() && (bytes[i] == b'e' || bytes[i] == b'E') {
        let exp_start = i;
        i += 1;
        if i < bytes.len() && (bytes[i] == b'+' || bytes[i] == b'-') {
            i += 1;
        }
        if i < bytes.len() && bytes[i].is_ascii_digit() {
            while i < bytes.len() && bytes[i].is_ascii_digit() {
                i += 1;
            }
        } else {
            // No digits after 'e' — revert to before exponent.
            i = exp_start;
        }
    }
    i
}

/// Parse the text/blob prefix used by `CAST(... AS INTEGER)`.
///
/// SQLite only consumes an optional sign followed by decimal digits here.
/// Decimal points and exponents terminate the parse instead of contributing to
/// the numeric value.
fn parse_cast_integer_prefix(s: &str) -> i64 {
    let trimmed = s.trim();
    if trimmed.is_empty() {
        return 0;
    }

    let bytes = trimmed.as_bytes();
    let mut end = if matches!(bytes.first(), Some(b'+' | b'-')) {
        1
    } else {
        0
    };
    let digit_start = end;
    while end < bytes.len() && bytes[end].is_ascii_digit() {
        end += 1;
    }
    if end == digit_start {
        return 0;
    }

    let prefix = &trimmed[..end];
    match prefix.parse::<i64>() {
        Ok(value) => value,
        Err(_) if prefix.starts_with('-') => i64::MIN,
        Err(_) => i64::MAX,
    }
}

/// SQL CAST operation (p2 encodes target type).
fn sql_cast(val: SqliteValue, target: i32) -> SqliteValue {
    if val.is_null() {
        return SqliteValue::Null;
    }
    // Target type encoding matches SQLite:
    // 'A' (65) = BLOB, 'B' (66) = TEXT, 'C' (67) = NUMERIC,
    // 'D' (68) = INTEGER, 'E' (69) = REAL
    // But more commonly p2 is used as an affinity character.
    #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
    let target_byte = target as u8;
    // C SQLite interprets blob bytes as UTF-8 text before numeric casts.
    let val = match (val, target_byte) {
        (SqliteValue::Blob(b), b'C' | b'c' | b'D' | b'd' | b'E' | b'e') => {
            SqliteValue::Text(String::from_utf8_lossy(&b).into_owned().into())
        }
        (other, _) => other,
    };
    match target_byte {
        b'A' | b'a' => SqliteValue::Blob(match val {
            SqliteValue::Blob(b) => b,
            SqliteValue::Text(s) => Arc::from(s.as_bytes()),
            other => Arc::from(other.to_text().into_bytes()),
        }),
        b'B' | b'b' => {
            // C SQLite: CAST(blob AS TEXT) decodes bytes as UTF-8,
            // not as hex literal.
            match val {
                SqliteValue::Blob(b) => {
                    SqliteValue::Text(String::from_utf8_lossy(&b).into_owned().into())
                }
                other => SqliteValue::Text(other.to_text().into()),
            }
        }
        b'C' | b'c' => val.cast_to_numeric(),
        b'D' | b'd' => {
            // C SQLite integer casts from text/blob consume only the signed
            // integer prefix; decimal/exponent syntax is ignored.
            match &val {
                SqliteValue::Text(s) => SqliteValue::Integer(parse_cast_integer_prefix(s)),
                _ => SqliteValue::Integer(val.to_integer()),
            }
        }
        b'E' | b'e' => {
            // C SQLite: CAST('3.14abc' AS REAL) extracts leading numeric prefix.
            // C SQLite allows Inf from "1e999" but not from literal "inf" text.
            match &val {
                SqliteValue::Text(s) => {
                    let trimmed = s.trim();
                    let end = scan_numeric_prefix(trimmed.as_bytes());
                    // Full-string numeric match (rejects Rust's "nan"/"inf" parsing).
                    if end == trimmed.len() && end > 0 {
                        if let Ok(f) = trimmed.parse::<f64>() {
                            return SqliteValue::Float(f);
                        }
                    }
                    // Prefix match for strings with trailing non-numeric text.
                    if end > 0 {
                        if let Ok(f) = trimmed[..end].parse::<f64>() {
                            return SqliteValue::Float(f);
                        }
                    }
                    SqliteValue::Float(0.0)
                }
                _ => SqliteValue::Float(val.to_float()),
            }
        }
        _ => val, // unknown: no-op
    }
}

/// Convert affinity character to `TypeAffinity`.
fn char_to_affinity(ch: char) -> fsqlite_types::TypeAffinity {
    match ch {
        'B' | 'b' => fsqlite_types::TypeAffinity::Text,
        'C' | 'c' => fsqlite_types::TypeAffinity::Numeric,
        'D' | 'd' => fsqlite_types::TypeAffinity::Integer,
        'E' | 'e' => fsqlite_types::TypeAffinity::Real,
        _ => fsqlite_types::TypeAffinity::Blob,
    }
}

// ── Tests ───────────────────────────────────────────────────────────────────

#[cfg(test)]
mod tests {
    use std::sync::Arc;
    use std::time::Instant;

    use super::*;
    use crate::{Label, ProgramBuilder};
    use fsqlite_func::vtab::{IndexInfo, VirtualTable, VirtualTableCursor};
    use fsqlite_func::{FunctionRegistry, ScalarFunction, register_builtins};
    use fsqlite_mvcc::ConcurrentRegistry;
    use fsqlite_types::limits::MAX_COLUMN;
    use fsqlite_types::opcode::{IndexCursorMeta, Opcode, P4, VdbeOp};
    use fsqlite_types::record::{parse_record, parse_record_into, serialize_record_iter_into};
    use fsqlite_types::serial_type::{
        serial_type_for_blob, serial_type_for_integer, serial_type_for_text, varint_len,
        write_varint,
    };
    use fsqlite_types::{SmallText, Snapshot};
    use proptest::prelude::*;
    use proptest::test_runner::{Config as ProptestConfig, TestCaseError, TestRunner};
    use rusqlite::params_from_iter;
    use rusqlite::types::Value as RusqliteValue;

    struct CancelExecutionFunc {
        cx: Cx,
    }

    impl ScalarFunction for CancelExecutionFunc {
        fn invoke(&self, _args: &[SqliteValue]) -> Result<SqliteValue> {
            self.cx.cancel();
            Ok(SqliteValue::Null)
        }

        fn num_args(&self) -> i32 {
            0
        }

        fn name(&self) -> &str {
            "cancel_exec"
        }
    }

    /// Build and execute a program, returning results.
    fn run_program(build: impl FnOnce(&mut ProgramBuilder)) -> Vec<Vec<SqliteValue>> {
        let mut b = ProgramBuilder::new();
        build(&mut b);
        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        engine
            .take_results()
            .into_iter()
            .map(|v| v.into_vec())
            .collect()
    }

    #[test]
    fn test_payload_includes_rowid_alias_does_not_false_positive_when_first_stored_value_matches_rowid()
     {
        let payload_values = vec![
            SqliteValue::Integer(1),
            SqliteValue::Text("local".into()),
            SqliteValue::Text("dup-session".into()),
        ];

        assert!(
            !payload_includes_rowid_alias(&payload_values, 1, 0, Some(4), Some(1)),
            "omitted INTEGER PRIMARY KEY aliases must be detected by payload width, not by a coincidental value match"
        );
        assert!(
            !payload_includes_rowid_alias(&payload_values, 1, 0, None, Some(1)),
            "countless fallback must not treat a matching first stored integer as proof that the rowid alias is present"
        );
    }

    #[test]
    fn test_payload_includes_rowid_alias_lazy_does_not_false_positive_when_first_stored_value_matches_rowid()
     {
        let record = serialize_record(&[
            SqliteValue::Integer(1),
            SqliteValue::Text("local".into()),
            SqliteValue::Text("dup-session".into()),
        ]);
        let mut scratch = fsqlite_types::record::RecordDecodeScratch::default();
        scratch
            .prepare_for_record(&record)
            .expect("payload should decode");

        assert!(
            !payload_includes_rowid_alias_lazy(&scratch, &record, 1, 0, Some(4), Some(1)),
            "lazy rowid-alias detection must not shift columns when the first stored value happens to equal the rowid"
        );
        assert!(
            !payload_includes_rowid_alias_lazy(&scratch, &record, 1, 0, None, Some(1)),
            "countless lazy fallback must stay conservative instead of trusting value equality"
        );
    }

    #[test]
    fn test_payload_includes_rowid_alias_lazy_accepts_explicit_null_placeholder() {
        let record = serialize_record(&[
            SqliteValue::Null,
            SqliteValue::Integer(1),
            SqliteValue::Text("local".into()),
        ]);
        let mut scratch = fsqlite_types::record::RecordDecodeScratch::default();
        scratch
            .prepare_for_record(&record)
            .expect("payload should decode");

        assert!(payload_includes_rowid_alias_lazy(
            &scratch,
            &record,
            1,
            0,
            Some(3),
            Some(1)
        ));
        assert!(payload_includes_rowid_alias_lazy(
            &scratch,
            &record,
            1,
            0,
            None,
            Some(1)
        ));
        assert!(payload_includes_rowid_alias_lazy(
            &scratch,
            &record,
            1,
            0,
            Some(4),
            Some(1)
        ));
    }

    #[test]
    fn test_payload_includes_rowid_alias_without_rowid_resolves_common_width_cases() {
        let record = serialize_record(&[
            SqliteValue::Integer(1),
            SqliteValue::Text("local".into()),
            SqliteValue::Text("dup-session".into()),
        ]);
        let mut scratch = fsqlite_types::record::RecordDecodeScratch::default();
        scratch
            .prepare_for_record(&record)
            .expect("payload should decode");

        assert!(
            payload_includes_rowid_alias_without_rowid(&scratch, 0, Some(3), Some(1)),
            "matching payload/table width proves the payload already carries the IPK alias"
        );
        assert!(
            !payload_includes_rowid_alias_without_rowid(&scratch, 0, Some(4), Some(1)),
            "one-column-short integer rows must not compare against rowid because the first stored user column may match by coincidence"
        );
    }

    #[test]
    fn test_payload_includes_rowid_alias_without_rowid_accepts_short_null_placeholder() {
        let record = serialize_record(&[
            SqliteValue::Null,
            SqliteValue::Text("local".into()),
            SqliteValue::Text("dup-session".into()),
        ]);
        let mut scratch = fsqlite_types::record::RecordDecodeScratch::default();
        scratch
            .prepare_for_record(&record)
            .expect("payload should decode");

        assert!(
            payload_includes_rowid_alias_without_rowid(&scratch, 0, Some(4), Some(1)),
            "SQLite-format rows keep a NULL IPK placeholder when later ALTER TABLE ADD COLUMN makes the payload one column short"
        );
    }

    #[test]
    fn test_payload_includes_rowid_alias_without_rowid_rejects_ambiguous_integer_match() {
        let record = serialize_record(&[
            SqliteValue::Integer(2),
            SqliteValue::Text("local".into()),
            SqliteValue::Text("dup-session".into()),
        ]);
        let mut scratch = fsqlite_types::record::RecordDecodeScratch::default();
        scratch
            .prepare_for_record(&record)
            .expect("payload should decode");

        assert!(
            !payload_includes_rowid_alias_without_rowid(&scratch, 0, Some(5), Some(1)),
            "ambiguous integer-at-IPK rows must not be accepted by rowid equality"
        );
        assert!(
            !payload_includes_rowid_alias_without_rowid(&scratch, 0, None, Some(1)),
            "the metadata-free fallback remains conservative"
        );
    }

    #[test]
    fn test_payload_includes_rowid_alias_lazy_rejects_multi_column_short_integer_match() {
        let record = serialize_record(&[
            SqliteValue::Integer(1),
            SqliteValue::Text("local".into()),
            SqliteValue::Text("dup-session".into()),
        ]);
        let mut scratch = fsqlite_types::record::RecordDecodeScratch::default();
        scratch
            .prepare_for_record(&record)
            .expect("payload should decode");

        assert!(
            !payload_includes_rowid_alias_lazy(&scratch, &record, 1, 0, Some(5), Some(1)),
            "multi-column-short rows must not treat a shifted user integer equal to rowid as an IPK placeholder"
        );
    }

    #[test]
    fn test_payload_includes_rowid_alias_detects_short_alter_row_via_not_null_column_metadata() {
        let payload_values = vec![
            SqliteValue::Null,
            SqliteValue::Integer(2),
            SqliteValue::Integer(2),
            SqliteValue::Text("remote-ci".into()),
            SqliteValue::Text("/sessions/b.jsonl".into()),
            SqliteValue::Integer(1_700_000_001_000),
        ];

        assert!(payload_includes_rowid_alias(
            &payload_values,
            2,
            0,
            Some(7),
            Some(1)
        ));
    }

    #[test]
    fn test_payload_includes_rowid_alias_lazy_detects_short_alter_row_via_not_null_column_metadata()
    {
        let record = serialize_record(&[
            SqliteValue::Null,
            SqliteValue::Integer(2),
            SqliteValue::Integer(2),
            SqliteValue::Text("remote-ci".into()),
            SqliteValue::Text("/sessions/b.jsonl".into()),
            SqliteValue::Integer(1_700_000_001_000),
        ]);
        let mut scratch = fsqlite_types::record::RecordDecodeScratch::default();
        scratch
            .prepare_for_record(&record)
            .expect("payload should decode");

        assert!(payload_includes_rowid_alias_lazy(
            &scratch,
            &record,
            2,
            0,
            Some(7),
            Some(1)
        ));
    }

    #[test]
    fn test_payload_includes_rowid_alias_accepts_short_null_placeholder() {
        let payload_values = vec![
            SqliteValue::Null,
            SqliteValue::Integer(5),
            SqliteValue::Text("tail".into()),
        ];

        assert!(payload_includes_rowid_alias(
            &payload_values,
            9,
            0,
            Some(4),
            Some(2)
        ));
    }

    #[test]
    fn test_payload_includes_rowid_alias_accepts_multi_column_short_null_placeholder() {
        let payload_values = vec![SqliteValue::Null, SqliteValue::Text("old".into())];

        assert!(payload_includes_rowid_alias(
            &payload_values,
            9,
            0,
            Some(4),
            None
        ));
    }

    #[test]
    fn test_payload_includes_rowid_alias_lazy_accepts_short_null_placeholder() {
        let record = serialize_record(&[
            SqliteValue::Null,
            SqliteValue::Integer(5),
            SqliteValue::Text("tail".into()),
        ]);
        let mut scratch = fsqlite_types::record::RecordDecodeScratch::default();
        scratch
            .prepare_for_record(&record)
            .expect("payload should decode");

        assert!(payload_includes_rowid_alias_lazy(
            &scratch,
            &record,
            9,
            0,
            Some(4),
            Some(2)
        ));
    }

    fn install_duplicate_prefix_index_fixture(
        engine: &mut VdbeEngine,
        cursor_id: i32,
    ) -> (Vec<u8>, Vec<u8>) {
        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();

        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);

        assert!(
            engine.open_storage_cursor(cursor_id, index_root, true),
            "index storage cursor should open"
        );

        let sc = engine
            .storage_cursors
            .get_mut(&cursor_id)
            .expect("storage cursor should exist");
        for rowid in 1_i64..=128 {
            let key = encode_record(&[SqliteValue::Integer(7), SqliteValue::Integer(rowid)]);
            sc.cursor
                .index_insert(&sc.cx, &key)
                .expect("duplicate-prefix index key should insert");
        }
        sc.cursor
            .index_insert(
                &sc.cx,
                &encode_record(&[SqliteValue::Integer(8), SqliteValue::Integer(999)]),
            )
            .expect("next-prefix index key should insert");

        let conflict_key = encode_record(&[SqliteValue::Integer(7)]);
        let miss_key = encode_record(&[SqliteValue::Integer(9)]);

        (conflict_key, miss_key)
    }

    fn build_storage_index_engine_with_duplicate_prefixes() -> (VdbeEngine, Vec<u8>, Vec<u8>) {
        let mut engine = VdbeEngine::new(8);
        let (conflict_key, miss_key) = install_duplicate_prefix_index_fixture(&mut engine, 0);
        (engine, conflict_key, miss_key)
    }

    fn build_storage_index_engine_with_nocase_prefixes() -> (VdbeEngine, Vec<u8>, Vec<u8>) {
        let mut engine = VdbeEngine::new(8);
        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();

        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        engine.set_index_collations_by_root_page(HashMap::from([(
            index_root,
            vec![Some("NOCASE".to_owned())],
        )]));

        assert!(
            engine.open_storage_cursor(0, index_root, true),
            "NOCASE index storage cursor should open"
        );

        let sc = engine
            .storage_cursors
            .get_mut(&0)
            .expect("NOCASE storage cursor should exist");
        for (text, rowid) in [("alpha", 1_i64), ("beta", 2_i64)] {
            let key = encode_record(&[
                SqliteValue::Text(text.to_owned().into()),
                SqliteValue::Integer(rowid),
            ]);
            sc.cursor
                .index_insert(&sc.cx, &key)
                .expect("NOCASE fixture key should insert");
        }

        let conflict_key = encode_record(&[SqliteValue::Text("ALPHA".into())]);
        let miss_key = encode_record(&[SqliteValue::Text("gamma".into())]);

        (engine, conflict_key, miss_key)
    }

    fn build_storage_index_engine_with_nocase_mixed_case_ordering() -> VdbeEngine {
        let mut engine = VdbeEngine::new(8);
        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();

        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        engine.set_index_collations_by_root_page(HashMap::from([(
            index_root,
            vec![Some("NOCASE".to_owned())],
        )]));

        assert!(
            engine.open_storage_cursor(0, index_root, true),
            "NOCASE index storage cursor should open"
        );

        let sc = engine
            .storage_cursors
            .get_mut(&0)
            .expect("NOCASE storage cursor should exist");
        for key in [
            encode_record(&[SqliteValue::Text("alpha".into()), SqliteValue::Integer(1)]),
            encode_record(&[SqliteValue::Text("ALPHA".into()), SqliteValue::Integer(2)]),
            encode_record(&[SqliteValue::Text("beta".into()), SqliteValue::Integer(3)]),
        ] {
            sc.cursor
                .index_insert(&sc.cx, &key)
                .expect("mixed-case NOCASE fixture key should insert");
        }

        engine
    }

    #[test]
    fn test_find_conflicting_rowid_rejects_index_entry_without_rowid_suffix() {
        let mut engine = VdbeEngine::new(8);
        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();

        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        assert!(
            engine.open_storage_cursor(0, index_root, true),
            "index storage cursor should open"
        );

        let sc = engine
            .storage_cursors
            .get_mut(&0)
            .expect("storage cursor should exist");
        sc.cursor
            .index_insert(&sc.cx, &encode_record(&[SqliteValue::Integer(7)]))
            .expect("malformed index fixture should insert for corruption regression");

        let probe_key = encode_record(&[SqliteValue::Integer(7), SqliteValue::Integer(42)]);
        let err = find_conflicting_rowid_in_index(sc, &probe_key, 1)
            .expect_err("missing rowid suffix should be reported as corruption");

        assert!(matches!(
            err,
            FrankenError::DatabaseCorrupt { detail }
                if detail.contains("integer rowid suffix")
        ));
    }

    #[test]
    fn test_find_conflicting_rowid_rejects_index_entry_with_extra_suffix_fields() {
        let mut engine = VdbeEngine::new(8);
        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();

        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        assert!(
            engine.open_storage_cursor(0, index_root, true),
            "index storage cursor should open"
        );

        let sc = engine
            .storage_cursors
            .get_mut(&0)
            .expect("storage cursor should exist");
        sc.cursor
            .index_insert(
                &sc.cx,
                &encode_record(&[
                    SqliteValue::Integer(7),
                    SqliteValue::Integer(1),
                    SqliteValue::Text("extra".into()),
                ]),
            )
            .expect("malformed index fixture should insert for corruption regression");

        let probe_key = encode_record(&[SqliteValue::Integer(7), SqliteValue::Integer(42)]);
        let err = find_conflicting_rowid_in_index(sc, &probe_key, 1)
            .expect_err("extra suffix fields should be reported as corruption");

        assert!(matches!(
            err,
            FrankenError::DatabaseCorrupt { detail }
                if detail.contains("integer rowid suffix")
        ));
    }

    #[test]
    fn test_collated_conflicting_rowid_rejects_index_entry_without_rowid_suffix() {
        let mut engine = VdbeEngine::new(8);
        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();

        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        engine.set_index_collations_by_root_page(HashMap::from([(
            index_root,
            vec![Some("NOCASE".to_owned())],
        )]));
        assert!(
            engine.open_storage_cursor(0, index_root, true),
            "NOCASE index storage cursor should open"
        );

        let sc = engine
            .storage_cursors
            .get_mut(&0)
            .expect("NOCASE storage cursor should exist");
        sc.cursor
            .index_insert(&sc.cx, &encode_record(&[SqliteValue::Text("alpha".into())]))
            .expect("malformed NOCASE index fixture should insert for corruption regression");

        let probe_key =
            encode_record(&[SqliteValue::Text("ALPHA".into()), SqliteValue::Integer(42)]);
        let err = find_conflicting_rowid_in_index_collated(
            sc,
            &probe_key,
            1,
            &[false],
            &[Some("NOCASE".to_owned())],
            &BUILTIN_COLLATION_REGISTRY,
        )
        .expect_err("missing rowid suffix should be reported as corruption");

        assert!(matches!(
            err,
            FrankenError::DatabaseCorrupt { detail }
                if detail.contains("integer rowid suffix")
        ));
    }

    #[test]
    fn test_collated_conflicting_rowid_rejects_index_entry_with_extra_suffix_fields() {
        let mut engine = VdbeEngine::new(8);
        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();

        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        engine.set_index_collations_by_root_page(HashMap::from([(
            index_root,
            vec![Some("NOCASE".to_owned())],
        )]));
        assert!(
            engine.open_storage_cursor(0, index_root, true),
            "NOCASE index storage cursor should open"
        );

        let sc = engine
            .storage_cursors
            .get_mut(&0)
            .expect("NOCASE storage cursor should exist");
        sc.cursor
            .index_insert(
                &sc.cx,
                &encode_record(&[
                    SqliteValue::Text("alpha".into()),
                    SqliteValue::Integer(1),
                    SqliteValue::Text("extra".into()),
                ]),
            )
            .expect("malformed NOCASE index fixture should insert for corruption regression");

        let probe_key =
            encode_record(&[SqliteValue::Text("ALPHA".into()), SqliteValue::Integer(42)]);
        let err = find_conflicting_rowid_in_index_collated(
            sc,
            &probe_key,
            1,
            &[false],
            &[Some("NOCASE".to_owned())],
            &BUILTIN_COLLATION_REGISTRY,
        )
        .expect_err("extra suffix fields should be reported as corruption");

        assert!(matches!(
            err,
            FrankenError::DatabaseCorrupt { detail }
                if detail.contains("integer rowid suffix")
        ));
    }

    #[test]
    fn test_idxinsert_consumes_make_record_sideband_for_storage_cursor() {
        let mut engine = VdbeEngine::new(8);
        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        assert!(
            engine.open_storage_cursor(0, index_root, true),
            "index storage cursor should open"
        );

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let found = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_insert_val = b.alloc_reg();
        let r_insert_key = b.alloc_reg();
        let r_probe_val = b.alloc_reg();
        let r_probe_key = b.alloc_reg();
        let r_out = b.alloc_reg();

        b.emit_op(Opcode::Integer, 7, r_insert_val, 0, P4::None, 0);
        b.emit_op(
            Opcode::MakeRecord,
            r_insert_val,
            1,
            r_insert_key,
            P4::None,
            0,
        );
        b.emit_op(Opcode::IdxInsert, 0, r_insert_key, 0, P4::None, 0);

        b.emit_op(Opcode::Integer, 7, r_probe_val, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, r_probe_val, 1, r_probe_key, P4::None, 0);
        b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
        b.emit_jump_to_label(Opcode::Found, 0, r_probe_key, found, P4::None, 0);
        b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
        b.resolve_label(found);
        b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
        b.resolve_label(done);

        b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let program = b.finish().expect("program should build");
        let rows = execute_program_with_engine(&mut engine, &program);
        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
    }

    #[test]
    fn test_idxinsert_conflict_skip_preserves_make_record_sideband_buffer() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        reset_vdbe_test_sideband_materialization_count();

        let mut builder = ProgramBuilder::new();
        let end = builder.emit_label();
        builder.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_value = builder.alloc_reg();
        let r_record = builder.alloc_reg();
        builder.emit_op(
            Opcode::String8,
            0,
            r_value,
            0,
            P4::Str("idx-skip-sideband".repeat(32)),
            0,
        );
        builder.emit_op(Opcode::MakeRecord, r_value, 1, r_record, P4::None, 0);
        builder.emit_op(Opcode::IdxInsert, 0, r_record, 0, P4::None, 0);
        builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        builder.resolve_label(end);

        let program = builder.finish().expect("program should build");
        let expected_capacity = estimate_make_record_buffer_capacity(&program, PageSize::DEFAULT);
        let mut engine = VdbeEngine::new(program.register_count());
        engine.set_conflict_skip_idx(true);

        let before = vdbe_test_sideband_materialization_count_snapshot();
        let outcome = engine.execute(&program).expect("program should execute");
        let after = vdbe_test_sideband_materialization_count_snapshot();

        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(
            after - before,
            0,
            "skipped IdxInsert should not materialize a sideband-backed key"
        );
        assert!(
            engine.make_record_lookaside.sideband_is_armed_for(r_record),
            "skipped IdxInsert should leave the MakeRecord sideband attached to its register"
        );
        assert!(
            !engine.make_record_lookaside.is_empty(),
            "skipped IdxInsert should retain the sideband bytes for reuse/materialization"
        );
        assert!(
            engine.make_record_lookaside.capacity() >= expected_capacity,
            "skipped IdxInsert should not drop the reusable sideband allocation"
        );
    }

    #[test]
    fn test_idxinsert_returns_make_record_sideband_buffer_after_storage_insert() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        reset_vdbe_test_sideband_materialization_count();

        let index_value = "idx-insert-sideband".repeat(32);
        let expected_key = encode_record(&[SqliteValue::Text(index_value.clone().into())]);

        let mut engine = VdbeEngine::new(8);
        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        assert!(
            engine.open_storage_cursor(0, index_root, true),
            "index storage cursor should open"
        );

        let mut builder = ProgramBuilder::new();
        let end = builder.emit_label();
        builder.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_value = builder.alloc_reg();
        let r_record = builder.alloc_reg();
        builder.emit_op(Opcode::String8, 0, r_value, 0, P4::Str(index_value), 0);
        builder.emit_op(Opcode::MakeRecord, r_value, 1, r_record, P4::None, 0);
        builder.emit_op(Opcode::IdxInsert, 0, r_record, 0, P4::None, 0);
        builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        builder.resolve_label(end);

        let program = builder.finish().expect("program should build");
        let expected_capacity = estimate_make_record_buffer_capacity(&program, PageSize::DEFAULT);
        let before = vdbe_test_sideband_materialization_count_snapshot();
        let outcome = engine.execute(&program).expect("program should execute");
        let after = vdbe_test_sideband_materialization_count_snapshot();

        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(
            after - before,
            0,
            "IdxInsert should consume MakeRecord sideband bytes without materializing"
        );
        assert!(
            engine.make_record_lookaside.is_empty(),
            "successful IdxInsert should return a cleared scratch buffer"
        );
        assert!(
            engine.make_record_lookaside.capacity() >= expected_capacity,
            "successful IdxInsert should keep the reusable sideband allocation"
        );

        let index_cursor = engine
            .storage_cursors
            .get_mut(&0)
            .expect("index cursor should remain available");
        assert!(
            index_cursor
                .cursor
                .index_move_to(&index_cursor.cx, &expected_key)
                .expect("index seek should succeed")
                .is_found(),
            "IdxInsert should still insert the key while recycling the sideband buffer"
        );
    }

    #[test]
    fn test_idxinsert_unique_abort_returns_make_record_sideband_buffer_on_error() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        reset_vdbe_test_sideband_materialization_count();

        let conflict_value = "idx-abort-sideband".repeat(32);
        let existing_key = encode_record(&[
            SqliteValue::Text(conflict_value.clone().into()),
            SqliteValue::Integer(1),
        ]);

        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();
        let table_root = db.create_table(1);
        let mut engine = VdbeEngine::new(8);
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        assert!(engine.open_storage_cursor(0, index_root, true));
        assert!(engine.open_storage_cursor(1, table_root, true));

        {
            let index_cursor = engine
                .storage_cursors
                .get_mut(&0)
                .expect("index cursor should exist");
            index_cursor
                .cursor
                .index_insert(&index_cursor.cx, &existing_key)
                .expect("existing unique index key should insert");
        }

        let provisional_payload = encode_record(&[SqliteValue::Integer(99)]);
        {
            let table_cursor = engine
                .storage_cursors
                .get_mut(&1)
                .expect("table cursor should exist");
            table_cursor
                .cursor
                .table_insert(&table_cursor.cx, 2, &provisional_payload)
                .expect("provisional table row should insert");
        }
        engine.changes = 1;
        engine.set_pending_insert_rollback(Some(PendingInsertRollback {
            cursor_id: 1,
            rowid: 2,
            previous_last_insert_rowid: 0,
            previous_last_insert_rowid_valid: false,
            update_restore: None,
        }));

        let mut builder = ProgramBuilder::new();
        let end = builder.emit_label();
        builder.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_value = builder.alloc_reg();
        let r_record = builder.alloc_reg();
        builder.emit_op(Opcode::String8, 0, r_value, 0, P4::Str(conflict_value), 0);
        builder.emit_op(Opcode::MakeRecord, r_value, 1, r_record, P4::None, 0);
        builder.emit_op(
            Opcode::IdxInsert,
            0,
            r_record,
            1,
            P4::Table("idx_col".to_owned()),
            1,
        );
        builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        builder.resolve_label(end);

        let program = builder.finish().expect("program should build");
        let expected_capacity = estimate_make_record_buffer_capacity(&program, PageSize::DEFAULT);
        let before = vdbe_test_sideband_materialization_count_snapshot();
        let error = engine
            .execute(&program)
            .expect_err("unique conflict should abort the statement");
        let after = vdbe_test_sideband_materialization_count_snapshot();

        assert!(
            matches!(error, FrankenError::UniqueViolation { .. }),
            "expected unique violation, got {error:?}"
        );
        assert_eq!(
            after - before,
            0,
            "unique abort should not materialize a sideband-backed key"
        );
        assert!(
            engine.make_record_lookaside.is_empty(),
            "aborted IdxInsert should return a cleared scratch buffer"
        );
        assert!(
            engine.make_record_lookaside.capacity() >= expected_capacity,
            "aborted IdxInsert should keep the reusable sideband allocation"
        );
    }

    #[test]
    fn test_idxinsert_ignore_restores_sideband_when_rollback_errors() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        reset_vdbe_test_sideband_materialization_count();

        let conflict_value = "idx-ignore-rollback-error-sideband".repeat(16);
        let existing_key = encode_record(&[
            SqliteValue::Text(conflict_value.clone().into()),
            SqliteValue::Integer(1),
        ]);

        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();
        let mut engine = VdbeEngine::new(8);
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        assert!(engine.open_storage_cursor(0, index_root, true));

        {
            let index_cursor = engine
                .storage_cursors
                .get_mut(&0)
                .expect("index cursor should exist");
            index_cursor
                .cursor
                .index_insert(&index_cursor.cx, &existing_key)
                .expect("existing unique index key should insert");
        }

        let mut builder = ProgramBuilder::new();
        let end = builder.emit_label();
        builder.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_value = builder.alloc_reg();
        let r_record = builder.alloc_reg();
        builder.emit_op(Opcode::String8, 0, r_value, 0, P4::Str(conflict_value), 0);
        builder.emit_op(Opcode::MakeRecord, r_value, 1, r_record, P4::None, 0);
        builder.emit_op(
            Opcode::IdxInsert,
            0,
            r_record,
            1,
            P4::Table("idx_col".to_owned()),
            1 | (4 << 1),
        );
        builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        builder.resolve_label(end);

        let program = builder.finish().expect("program should build");
        let expected_capacity = estimate_make_record_buffer_capacity(&program, PageSize::DEFAULT);
        let before = vdbe_test_sideband_materialization_count_snapshot();
        let error = engine
            .execute(&program)
            .expect_err("missing provisional insert rollback should error");
        let after = vdbe_test_sideband_materialization_count_snapshot();

        assert!(
            matches!(&error, FrankenError::Internal(message) if message.contains("secondary-index conflict without pending table insert")),
            "expected rollback bookkeeping error, got {error:?}"
        );
        assert_eq!(
            after - before,
            0,
            "rollback error path should not materialize a sideband-backed key"
        );
        assert!(
            engine.make_record_lookaside.is_empty(),
            "failed IGNORE rollback should return a cleared scratch buffer"
        );
        assert!(
            engine.make_record_lookaside.capacity() >= expected_capacity,
            "failed IGNORE rollback should keep the reusable sideband allocation"
        );
    }

    #[test]
    fn test_no_conflict_consumes_make_record_sideband_probe_key() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let (mut engine, _, _) = build_storage_index_engine_with_duplicate_prefixes();

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let no_conflict = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_probe = b.alloc_reg();
        let r_key = b.alloc_reg();
        let r_out = b.alloc_reg();

        b.emit_op(Opcode::Integer, 7, r_probe, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, r_probe, 1, r_key, P4::None, 0);
        b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
        b.emit_jump_to_label(Opcode::NoConflict, 0, r_key, no_conflict, P4::None, 0);
        b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
        b.resolve_label(no_conflict);
        b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
        b.resolve_label(done);

        b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let program = b.finish().expect("program should build");
        let rows = execute_program_with_engine(&mut engine, &program);

        assert_eq!(rows, vec![vec![SqliteValue::Integer(0)]]);
    }

    fn build_no_conflict_opcode_probe_program(
        conflict_key: Vec<u8>,
        miss_key: Vec<u8>,
        malformed_key: Vec<u8>,
    ) -> crate::VdbeProgram {
        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_key = b.alloc_reg();
        let r_out = b.alloc_reg();

        for (probe_key, expected_insertable) in [
            (conflict_key, false),
            (miss_key, true),
            (malformed_key, true),
        ] {
            let no_conflict = b.emit_label();
            let done = b.emit_label();
            b.emit_op(Opcode::Blob, 0, r_key, 0, P4::Blob(probe_key), 0);
            b.emit_jump_to_label(Opcode::NoConflict, 0, r_key, no_conflict, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(no_conflict);
            b.emit_op(
                Opcode::Integer,
                i32::from(expected_insertable),
                r_out,
                0,
                P4::None,
                0,
            );
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
        }

        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        b.finish().expect("program should build")
    }

    fn build_found_opcode_probe_program(
        conflict_key: Vec<u8>,
        miss_key: Vec<u8>,
    ) -> crate::VdbeProgram {
        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_key = b.alloc_reg();
        let r_out = b.alloc_reg();

        for (probe_key, expected_found) in [(conflict_key, true), (miss_key, false)] {
            let found = b.emit_label();
            let done = b.emit_label();
            b.emit_op(Opcode::Blob, 0, r_key, 0, P4::Blob(probe_key), 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::Found, 0, r_key, found, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(found);
            b.emit_op(
                Opcode::Integer,
                i32::from(expected_found),
                r_out,
                0,
                P4::None,
                0,
            );
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
        }

        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        b.finish().expect("program should build")
    }

    fn build_not_found_opcode_probe_program(
        conflict_key: Vec<u8>,
        miss_key: Vec<u8>,
    ) -> crate::VdbeProgram {
        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_key = b.alloc_reg();
        let r_out = b.alloc_reg();

        for probe_key in [conflict_key, miss_key] {
            let missing = b.emit_label();
            let done = b.emit_label();
            b.emit_op(Opcode::Blob, 0, r_key, 0, P4::Blob(probe_key), 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::NotFound, 0, r_key, missing, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(missing);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
        }

        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        b.finish().expect("program should build")
    }

    fn execute_program_with_engine(
        engine: &mut VdbeEngine,
        program: &crate::VdbeProgram,
    ) -> Vec<Vec<SqliteValue>> {
        let outcome = engine.execute(program).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        engine
            .take_results()
            .into_iter()
            .map(|row| row.into_vec())
            .collect::<Vec<_>>()
    }

    fn legacy_storage_cursor_no_conflict_prefix_match(
        cursor: &mut StorageCursor,
        key_bytes: &[u8],
    ) -> Result<bool> {
        let probe_fields = match parse_record(key_bytes) {
            Some(fields) => fields,
            None => return Ok(false),
        };
        if probe_fields.iter().any(SqliteValue::is_null) {
            return Ok(false);
        }
        cursor.cursor.index_move_to(&cursor.cx, key_bytes)?;
        if let Ok(entry_bytes) = cursor.cursor.payload(&cursor.cx) {
            if let Some(entry_fields) = parse_record(&entry_bytes) {
                let prefix_len = probe_fields.len();
                return Ok(entry_fields.len() >= prefix_len
                    && entry_fields[..prefix_len] == probe_fields[..]);
            }
        }
        Ok(false)
    }

    #[test]
    fn test_take_results_preserves_result_buffer_capacity_for_reuse() {
        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        let r = b.alloc_reg();
        b.emit_op(Opcode::Integer, 1, r, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        let program = b.finish().expect("program should build");

        let mut engine = VdbeEngine::new(program.register_count());
        assert_eq!(
            engine.result_buffer_capacity(),
            64,
            "new engines should keep the preallocated result-row buffer"
        );

        let outcome = engine.execute(&program).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        let rows = engine.take_results();
        assert_eq!(rows.len(), 1);
        assert_eq!(
            engine.result_buffer_capacity(),
            64,
            "taking results should preserve buffer capacity for the next execution"
        );
    }

    #[test]
    fn test_take_exactly_one_result_row_preserves_result_buffer_capacity_for_reuse() {
        let mut one_row_builder = ProgramBuilder::new();
        let one_row_end = one_row_builder.emit_label();
        one_row_builder.emit_jump_to_label(Opcode::Init, 0, 0, one_row_end, P4::None, 0);
        let one_row_reg = one_row_builder.alloc_reg();
        one_row_builder.emit_op(Opcode::Integer, 7, one_row_reg, 0, P4::None, 0);
        one_row_builder.emit_op(Opcode::ResultRow, one_row_reg, 1, 0, P4::None, 0);
        one_row_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        one_row_builder.resolve_label(one_row_end);
        let one_row_program = one_row_builder.finish().expect("program should build");

        let mut engine = VdbeEngine::new(one_row_program.register_count());
        let initial_capacity = engine.result_buffer_capacity();
        let outcome = engine
            .execute(&one_row_program)
            .expect("one-row execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(
            engine.take_exactly_one_result_row(),
            ExactResultRowOutcome::Row(Box::new(smallvec::smallvec![SqliteValue::Integer(7)])),
        );
        assert_eq!(
            engine.result_buffer_capacity(),
            initial_capacity,
            "exact-one-row drain should preserve the reusable result buffer"
        );

        let mut many_rows_builder = ProgramBuilder::new();
        let many_rows_end = many_rows_builder.emit_label();
        many_rows_builder.emit_jump_to_label(Opcode::Init, 0, 0, many_rows_end, P4::None, 0);
        let many_rows_reg = many_rows_builder.alloc_reg();
        many_rows_builder.emit_op(Opcode::Integer, 1, many_rows_reg, 0, P4::None, 0);
        many_rows_builder.emit_op(Opcode::ResultRow, many_rows_reg, 1, 0, P4::None, 0);
        many_rows_builder.emit_op(Opcode::Integer, 2, many_rows_reg, 0, P4::None, 0);
        many_rows_builder.emit_op(Opcode::ResultRow, many_rows_reg, 1, 0, P4::None, 0);
        many_rows_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        many_rows_builder.resolve_label(many_rows_end);
        let many_rows_program = many_rows_builder.finish().expect("program should build");

        let outcome = engine
            .execute(&many_rows_program)
            .expect("multi-row execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(
            engine.take_exactly_one_result_row(),
            ExactResultRowOutcome::MultipleRows,
        );
        assert_eq!(
            engine.result_buffer_capacity(),
            initial_capacity,
            "multiple-row drain should also preserve the reusable result buffer"
        );
    }

    #[test]
    fn test_disabling_result_row_collection_still_clears_result_registers() {
        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        let r1 = b.alloc_reg();
        let r2 = b.alloc_reg();
        b.emit_op(Opcode::Integer, 7, r1, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r1, 1, 0, P4::None, 0);
        b.emit_op(Opcode::IntCopy, r1, r2, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r2, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        let program = b.finish().expect("program should build");

        let mut engine = VdbeEngine::new(program.register_count());
        engine.set_collect_result_rows(false);
        let outcome = engine.execute(&program).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        assert!(
            engine.results().is_empty(),
            "rowless execution should not retain ResultRow payloads"
        );
        assert_eq!(
            engine.get_reg(r1),
            &SqliteValue::Null,
            "discarded ResultRow should still clear its source registers"
        );
        assert_eq!(
            engine.get_reg(r2),
            &SqliteValue::Null,
            "later ResultRow opcodes should observe the same cleared-register semantics"
        );
    }

    #[test]
    fn test_result_row_collection_cap_discards_rows_after_limit_and_clears_registers() {
        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        let r1 = b.alloc_reg();
        let r2 = b.alloc_reg();
        let r3 = b.alloc_reg();
        let r4 = b.alloc_reg();
        let r5 = b.alloc_reg();
        let r6 = b.alloc_reg();
        b.emit_op(Opcode::Integer, 7, r1, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r1, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 8, r2, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r2, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 9, r3, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r3, 1, 0, P4::None, 0);
        b.emit_op(Opcode::IntCopy, r1, r4, 0, P4::None, 0);
        b.emit_op(Opcode::IntCopy, r2, r5, 0, P4::None, 0);
        b.emit_op(Opcode::IntCopy, r3, r6, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r4, 3, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        let program = b.finish().expect("program should build");

        let mut engine = VdbeEngine::new(program.register_count());
        engine.set_max_collected_result_rows(Some(2));
        let outcome = engine.execute(&program).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(
            engine
                .results()
                .iter()
                .map(|row| row.clone().into_vec())
                .collect::<Vec<_>>(),
            vec![vec![SqliteValue::Integer(7)], vec![SqliteValue::Integer(8)],],
            "rows after the retention cap should be discarded"
        );
        assert_eq!(engine.get_reg(r3), &SqliteValue::Null);
        assert_eq!(engine.get_reg(r4), &SqliteValue::Null);
        assert_eq!(engine.get_reg(r5), &SqliteValue::Null);
        assert_eq!(engine.get_reg(r6), &SqliteValue::Null);
    }

    #[test]
    fn test_result_row_handler_streams_rows_without_retaining_results() {
        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        let r1 = b.alloc_reg();
        let r2 = b.alloc_reg();
        b.emit_op(Opcode::Integer, 7, r1, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r1, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 8, r2, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r2, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        let program = b.finish().expect("program should build");

        let mut engine = VdbeEngine::new(program.register_count());
        let mut streamed_rows = Vec::new();
        let outcome = engine
            .execute_with_borrowed_bindings_and_row_handler(&program, None, &mut |row| {
                streamed_rows.push(row.into_vec());
                Ok(())
            })
            .expect("execution should succeed");

        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(
            streamed_rows,
            vec![vec![SqliteValue::Integer(7)], vec![SqliteValue::Integer(8)],],
            "row handler should observe result rows in program order"
        );
        assert!(
            engine.results().is_empty(),
            "streaming callback should avoid retaining result rows in the engine buffer"
        );
        assert_eq!(engine.get_reg(r1), &SqliteValue::Null);
        assert_eq!(engine.get_reg(r2), &SqliteValue::Null);
    }

    #[test]
    fn test_execute_swaps_shared_table_index_meta_per_program() {
        let first_program = {
            let mut b = ProgramBuilder::new();
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
            b.register_table_indexes(
                7,
                vec![IndexCursorMeta {
                    cursor_id: 8,
                    column_indices: vec![0, 2],
                }],
            );
            b.finish().expect("first program should build")
        };
        let second_program = {
            let mut b = ProgramBuilder::new();
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
            b.finish().expect("second program should build")
        };

        let mut engine = VdbeEngine::new(
            first_program
                .register_count()
                .max(second_program.register_count()),
        );
        assert!(engine.table_index_meta.is_empty());

        let first_outcome = engine
            .execute(&first_program)
            .expect("first execution should succeed");
        assert_eq!(first_outcome, ExecOutcome::Done);
        assert!(Arc::ptr_eq(
            &engine.table_index_meta,
            first_program.shared_table_index_meta()
        ));
        let first_meta = engine
            .table_index_meta
            .get(&7)
            .expect("first program metadata should be visible to the engine");
        assert_eq!(first_meta.len(), 1);
        assert_eq!(first_meta[0].cursor_id, 8);
        assert_eq!(first_meta[0].column_indices, vec![0, 2]);

        let second_outcome = engine
            .execute(&second_program)
            .expect("second execution should succeed");
        assert_eq!(second_outcome, ExecOutcome::Done);
        assert!(Arc::ptr_eq(
            &engine.table_index_meta,
            second_program.shared_table_index_meta()
        ));
        assert!(
            engine.table_index_meta.is_empty(),
            "executing a program without REPLACE metadata must clear prior program metadata"
        );
    }

    /// Build and execute a program with bound SQL parameters.
    fn run_program_with_bindings(
        build: impl FnOnce(&mut ProgramBuilder),
        bindings: Vec<SqliteValue>,
    ) -> Vec<Vec<SqliteValue>> {
        let mut b = ProgramBuilder::new();
        build(&mut b);
        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.set_bindings(bindings);
        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        engine
            .take_results()
            .into_iter()
            .map(|v| v.into_vec())
            .collect()
    }

    fn track_r_log_metrics(
        test_name: &str,
        alloc_count: usize,
        scratch_capacity_bytes: usize,
        encode_time_ns: u128,
    ) {
        eprintln!(
            "track_r test={test_name} alloc_count={alloc_count} scratch_capacity_bytes={scratch_capacity_bytes} encode_time_ns={encode_time_ns}"
        );
    }

    fn track_r_values_bitwise_eq(left: &SqliteValue, right: &SqliteValue) -> bool {
        match (left, right) {
            (SqliteValue::Null, SqliteValue::Null) => true,
            (SqliteValue::Integer(lhs), SqliteValue::Integer(rhs)) => lhs == rhs,
            (SqliteValue::Float(lhs), SqliteValue::Float(rhs)) => lhs.to_bits() == rhs.to_bits(),
            (SqliteValue::Text(lhs), SqliteValue::Text(rhs)) => lhs == rhs,
            (SqliteValue::Blob(lhs), SqliteValue::Blob(rhs)) => lhs == rhs,
            _ => false,
        }
    }

    fn track_r_assert_rows_eq(actual: &[SqliteValue], expected: &[SqliteValue], context: &str) {
        assert_eq!(
            actual.len(),
            expected.len(),
            "{context}: column count mismatch"
        );
        for (idx, (actual_value, expected_value)) in actual.iter().zip(expected.iter()).enumerate()
        {
            assert!(
                track_r_values_bitwise_eq(actual_value, expected_value),
                "{context}: mismatch at column {idx}: actual={actual_value:?} expected={expected_value:?}",
            );
        }
    }

    fn track_r_serialized_value_layout(value: &SqliteValue) -> (u64, usize) {
        match value {
            SqliteValue::Null => (0, 0),
            SqliteValue::Integer(integer) => {
                let serial_type = serial_type_for_integer(*integer);
                let payload_len = match serial_type {
                    8 | 9 => 0,
                    1 => 1,
                    2 => 2,
                    3 => 3,
                    4 => 4,
                    5 => 6,
                    6 => 8,
                    _ => unreachable!("integer serial type must be in 1..=9"),
                };
                (serial_type, payload_len)
            }
            SqliteValue::Float(float) => {
                if float.is_nan() {
                    (0, 0)
                } else {
                    (7, 8)
                }
            }
            SqliteValue::Text(text) => {
                let len = text.len();
                (
                    serial_type_for_text(u64::try_from(len).unwrap_or(u64::MAX)),
                    len,
                )
            }
            SqliteValue::Blob(blob) => {
                let len = blob.len();
                (
                    serial_type_for_blob(u64::try_from(len).unwrap_or(u64::MAX)),
                    len,
                )
            }
        }
    }

    fn track_r_compute_header_size(content_size: usize) -> usize {
        let mut header_size = content_size + 1;
        loop {
            let needed = varint_len(header_size as u64) + content_size;
            if needed <= header_size {
                return header_size;
            }
            header_size = needed;
        }
    }

    fn track_r_encode_serialized_value(value: &SqliteValue, payload_len: usize, buf: &mut [u8]) {
        match value {
            SqliteValue::Null => {}
            SqliteValue::Integer(integer) => {
                if payload_len == 0 {
                    return;
                }
                let bytes = integer.to_be_bytes();
                buf.copy_from_slice(&bytes[8 - payload_len..]);
            }
            SqliteValue::Float(float) => {
                if float.is_nan() {
                    return;
                }
                buf.copy_from_slice(&float.to_bits().to_be_bytes());
            }
            SqliteValue::Text(text) => buf.copy_from_slice(text.as_bytes()),
            SqliteValue::Blob(blob) => buf.copy_from_slice(blob),
        }
    }

    fn track_r_reference_three_pass_record(values: &[SqliteValue]) -> Vec<u8> {
        let layouts: Vec<_> = values.iter().map(track_r_serialized_value_layout).collect();
        let header_content_size = layouts
            .iter()
            .map(|(serial_type, _)| varint_len(*serial_type))
            .sum::<usize>();
        let body_size = layouts
            .iter()
            .map(|(_, payload_len)| *payload_len)
            .sum::<usize>();
        let header_size = track_r_compute_header_size(header_content_size);
        let total_size = header_size + body_size;
        let mut buf = vec![0; total_size];

        let mut header_offset = write_varint(
            buf.as_mut_slice(),
            u64::try_from(header_size).unwrap_or(u64::MAX),
        );
        let mut body_offset = header_size;
        for (value, (serial_type, payload_len)) in values.iter().zip(layouts.iter().copied()) {
            header_offset += write_varint(&mut buf[header_offset..], serial_type);
            track_r_encode_serialized_value(
                value,
                payload_len,
                &mut buf[body_offset..body_offset + payload_len],
            );
            body_offset += payload_len;
        }

        assert_eq!(header_offset, header_size, "reference header width drifted");
        assert_eq!(body_offset, total_size, "reference body width drifted");
        buf
    }

    fn track_r_arb_sqlite_value() -> BoxedStrategy<SqliteValue> {
        prop_oneof![
            4 => Just(SqliteValue::Null),
            8 => any::<i64>().prop_map(SqliteValue::Integer),
            4 => (-1.0e12_f64..1.0e12_f64).prop_map(SqliteValue::Float),
            4 => proptest::collection::vec(any::<char>(), 0..64).prop_map(|chars| {
                let text = chars.into_iter().collect::<String>();
                SqliteValue::Text(SmallText::from_string(text))
            }),
            4 => proptest::collection::vec(any::<u8>(), 0..96)
                .prop_map(|bytes| SqliteValue::Blob(Arc::<[u8]>::from(bytes))),
        ]
        .boxed()
    }

    fn track_r_generated_record(seed: usize) -> Vec<SqliteValue> {
        let integer = i64::try_from(seed).unwrap_or(i64::MAX) - 5_000;
        let tail = match seed % 5 {
            0 => SqliteValue::Null,
            1 => SqliteValue::Integer(0),
            2 => SqliteValue::Integer(1),
            3 => SqliteValue::Integer(-1),
            _ => SqliteValue::Integer(i64::try_from(seed % 97).unwrap_or(0) - 48),
        };
        let text = match seed % 4 {
            0 => String::new(),
            1 => format!("row-{seed:05}"),
            2 => "x".repeat((seed % 32) + 1),
            _ => format!("record-{seed:05}-tail"),
        };
        let blob_len = match seed % 6 {
            0 => 0,
            1 => 1,
            2 => 7,
            3 => 19,
            4 => 33,
            _ => (seed % 48) + 1,
        };
        let blob = (0..blob_len)
            .map(|offset| {
                let byte = (seed.wrapping_mul(17)).wrapping_add(offset.wrapping_mul(29)) % 251;
                u8::try_from(byte).unwrap_or(0)
            })
            .collect::<Vec<_>>();

        vec![
            SqliteValue::Integer(integer),
            SqliteValue::Float((seed as f64).mul_add(1.625, -777.25)),
            SqliteValue::Text(SmallText::from_string(text)),
            SqliteValue::Blob(Arc::<[u8]>::from(blob)),
            tail,
        ]
    }

    fn track_r_large_scratch_record(iteration: usize) -> Vec<SqliteValue> {
        if iteration == 0 {
            return vec![
                SqliteValue::Integer(-9_223_372_036_854_775_000),
                SqliteValue::Text(SmallText::from_string("x".repeat(2_048))),
                SqliteValue::Blob(Arc::<[u8]>::from(vec![0xAB; 4_096])),
            ];
        }

        vec![
            SqliteValue::Integer(i64::try_from(iteration).unwrap_or(i64::MAX)),
            SqliteValue::Text(SmallText::from_string(format!("row-{iteration:04}"))),
            SqliteValue::Blob(Arc::<[u8]>::from(vec![
                u8::try_from(iteration % 251)
                    .unwrap_or(0);
                (iteration % 24) + 1
            ])),
        ]
    }

    fn track_r_make_record_op(source_reg: i32, column_count: usize, target_reg: i32) -> VdbeOp {
        VdbeOp {
            opcode: Opcode::MakeRecord,
            p1: source_reg,
            p2: i32::try_from(column_count).expect("column count should fit in i32"),
            p3: target_reg,
            p4: P4::None,
            p5: 0,
        }
    }

    fn track_r_decode_make_record_buf(engine: &VdbeEngine) -> Vec<SqliteValue> {
        parse_record(engine.make_record_lookaside.as_slice())
            .expect("MakeRecord sideband should decode")
    }

    fn track_r_emit_value(builder: &mut ProgramBuilder, reg: i32, value: &SqliteValue) {
        match value {
            SqliteValue::Null => {
                builder.emit_op(Opcode::Null, 0, reg, 0, P4::None, 0);
            }
            SqliteValue::Integer(integer) => {
                builder.emit_op(Opcode::Int64, 0, reg, 0, P4::Int64(*integer), 0);
            }
            SqliteValue::Float(float) => {
                builder.emit_op(Opcode::Real, 0, reg, 0, P4::Real(*float), 0);
            }
            SqliteValue::Text(text) => {
                builder.emit_op(
                    Opcode::String8,
                    0,
                    reg,
                    0,
                    P4::Str(text.as_str().to_owned()),
                    0,
                );
            }
            SqliteValue::Blob(blob) => {
                builder.emit_op(Opcode::Blob, 0, reg, 0, P4::Blob(blob.to_vec()), 0);
            }
        }
    }

    fn track_r_run_write_with_memdb(
        db: MemDatabase,
        build: impl FnOnce(&mut ProgramBuilder),
    ) -> (Vec<Vec<SqliteValue>>, MemDatabase) {
        let mut builder = ProgramBuilder::new();
        build(&mut builder);
        let program = builder.finish().expect("program should build");
        let mut engine = VdbeEngine::new(program.register_count());
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        let outcome = engine.execute(&program).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        let results = engine
            .take_results()
            .into_iter()
            .map(|row| row.into_vec())
            .collect::<Vec<_>>();
        let final_db = engine.take_database().expect("database should exist");
        (results, final_db)
    }

    fn track_r_to_rusqlite_value(value: &SqliteValue) -> RusqliteValue {
        match value {
            SqliteValue::Null => RusqliteValue::Null,
            SqliteValue::Integer(integer) => RusqliteValue::Integer(*integer),
            SqliteValue::Float(float) => RusqliteValue::Real(*float),
            SqliteValue::Text(text) => RusqliteValue::Text(text.as_str().to_owned()),
            SqliteValue::Blob(blob) => RusqliteValue::Blob(blob.to_vec()),
        }
    }

    fn track_r_from_rusqlite_value(value: RusqliteValue) -> SqliteValue {
        match value {
            RusqliteValue::Null => SqliteValue::Null,
            RusqliteValue::Integer(integer) => SqliteValue::Integer(integer),
            RusqliteValue::Real(float) => SqliteValue::Float(float),
            RusqliteValue::Text(text) => SqliteValue::Text(SmallText::from_string(text)),
            RusqliteValue::Blob(blob) => SqliteValue::Blob(Arc::<[u8]>::from(blob)),
        }
    }

    #[test]
    fn test_track_r_make_record_scratch_reuse_1000_records_no_realloc_after_first() {
        let mut engine = VdbeEngine::new(8);
        let op = track_r_make_record_op(1, 3, 4);
        let mut scratch_capacity = None;
        let mut alloc_count = 0usize;
        let start = Instant::now();

        for iteration in 0..1_000 {
            let values = track_r_large_scratch_record(iteration);
            for (offset, value) in values.iter().enumerate() {
                let reg = i32::try_from(offset + 1).expect("register index should fit in i32");
                engine.set_reg(reg, value.clone());
            }

            engine.execute_make_record_hot(&op, false);

            let observed_capacity = engine.make_record_lookaside.capacity();
            if let Some(expected_capacity) = scratch_capacity {
                assert_eq!(
                    observed_capacity, expected_capacity,
                    "MakeRecord scratch reallocated after warmup at iteration {iteration}",
                );
            } else {
                assert!(
                    observed_capacity > 0,
                    "scratch capacity should grow on first encode"
                );
                scratch_capacity = Some(observed_capacity);
                alloc_count += 1;
            }

            let decoded = track_r_decode_make_record_buf(&engine);
            track_r_assert_rows_eq(
                &decoded,
                &values,
                "track_r scratch reuse MakeRecord decode mismatch",
            );
        }

        track_r_log_metrics(
            "test_track_r_make_record_scratch_reuse_1000_records_no_realloc_after_first",
            alloc_count,
            scratch_capacity.unwrap_or(0),
            start.elapsed().as_nanos(),
        );
    }

    #[test]
    fn test_track_r_two_pass_matches_three_pass_proptest_10k_random() {
        let strategy = proptest::collection::vec(track_r_arb_sqlite_value(), 0..48);
        let mut runner = TestRunner::new(ProptestConfig {
            cases: 10_000,
            ..ProptestConfig::default()
        });
        let two_pass = std::cell::RefCell::new(Vec::new());
        let alloc_count = std::cell::Cell::new(0usize);
        let encode_time_ns = std::cell::Cell::new(0_u128);
        let scratch_capacity_bytes = std::cell::Cell::new(0usize);

        runner
            .run(&strategy, |values| {
                let start = Instant::now();
                let mut two_pass_buf = two_pass.borrow_mut();
                serialize_record_iter_into(values.iter(), &mut two_pass_buf);
                let current_capacity = two_pass_buf.capacity();
                if current_capacity != scratch_capacity_bytes.get() {
                    alloc_count.set(alloc_count.get().saturating_add(1));
                    scratch_capacity_bytes.set(current_capacity);
                }
                let three_pass = track_r_reference_three_pass_record(&values);
                encode_time_ns.set(
                    encode_time_ns
                        .get()
                        .saturating_add(start.elapsed().as_nanos()),
                );
                if *two_pass_buf != three_pass {
                    return Err(TestCaseError::fail(format!(
                        "two-pass and three-pass record encoders diverged for values={values:?}"
                    )));
                }
                Ok(())
            })
            .expect("two-pass encoder should match the three-pass reference");

        track_r_log_metrics(
            "test_track_r_two_pass_matches_three_pass_proptest_10k_random",
            alloc_count.get(),
            scratch_capacity_bytes
                .get()
                .max(two_pass.borrow().capacity()),
            encode_time_ns.get(),
        );
    }

    #[test]
    fn test_track_r_make_record_all_sqlite_types() {
        let values = vec![
            SqliteValue::Null,
            SqliteValue::Integer(-9_223_372_036_854_775_000),
            SqliteValue::Float(-1234.5),
            SqliteValue::Text(SmallText::from_string("hello-track-r".to_owned())),
            SqliteValue::Blob(Arc::<[u8]>::from(vec![0x00, 0x01, 0xFE, 0xFF])),
        ];
        let mut engine = VdbeEngine::new(8);
        let op = track_r_make_record_op(1, values.len(), 7);
        let start = Instant::now();

        for (offset, value) in values.iter().enumerate() {
            let reg = i32::try_from(offset + 1).expect("register index should fit in i32");
            engine.set_reg(reg, value.clone());
        }
        engine.execute_make_record_hot(&op, false);

        let decoded = track_r_decode_make_record_buf(&engine);
        track_r_assert_rows_eq(
            &decoded,
            &values,
            "track_r all-types MakeRecord decode mismatch",
        );
        track_r_log_metrics(
            "test_track_r_make_record_all_sqlite_types",
            1,
            engine.make_record_lookaside.capacity(),
            start.elapsed().as_nanos(),
        );
    }

    #[test]
    fn test_track_r_make_record_empty_record() {
        let mut engine = VdbeEngine::new(2);
        let op = track_r_make_record_op(1, 0, 1);
        let start = Instant::now();

        engine.execute_make_record_hot(&op, false);

        let decoded = track_r_decode_make_record_buf(&engine);
        assert!(
            decoded.is_empty(),
            "empty MakeRecord should decode to no columns"
        );
        track_r_log_metrics(
            "test_track_r_make_record_empty_record",
            1,
            engine.make_record_lookaside.capacity(),
            start.elapsed().as_nanos(),
        );
    }

    #[test]
    fn test_track_r_make_record_max_columns() {
        let max_columns = usize::from(MAX_COLUMN);
        let mut engine = VdbeEngine::new(i32::try_from(max_columns + 2).expect("register count"));
        let op = track_r_make_record_op(1, max_columns, i32::try_from(max_columns + 1).unwrap());
        let expected = (0..max_columns)
            .map(|idx| SqliteValue::Integer(i64::try_from(idx).unwrap_or(i64::MAX) - 1_000))
            .collect::<Vec<_>>();
        let start = Instant::now();

        for (offset, value) in expected.iter().enumerate() {
            let reg = i32::try_from(offset + 1).expect("register index should fit in i32");
            engine.set_reg(reg, value.clone());
        }
        engine.execute_make_record_hot(&op, false);

        let decoded = track_r_decode_make_record_buf(&engine);
        track_r_assert_rows_eq(
            &decoded,
            &expected,
            "track_r max-column MakeRecord decode mismatch",
        );
        track_r_log_metrics(
            "test_track_r_make_record_max_columns",
            1,
            engine.make_record_lookaside.capacity(),
            start.elapsed().as_nanos(),
        );
    }

    #[test]
    fn test_track_r_make_record_insert_10k_oracle() {
        let row_count = 10_000usize;
        let expected_rows = (0..row_count)
            .map(track_r_generated_record)
            .collect::<Vec<_>>();
        let mut db = MemDatabase::new();
        let root = db.create_table(5);
        let start = Instant::now();

        let (observed_rows, _) = track_r_run_write_with_memdb(db, |b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(5), 0);

            for (idx, row) in expected_rows.iter().enumerate() {
                let rowid = i64::try_from(idx + 1).expect("rowid should fit in i64");
                b.emit_op(Opcode::Int64, 0, 1, 0, P4::Int64(rowid), 0);
                for (offset, value) in row.iter().enumerate() {
                    let reg = i32::try_from(offset + 2).expect("register index should fit in i32");
                    track_r_emit_value(b, reg, value);
                }
                b.emit_op(Opcode::MakeRecord, 2, 5, 7, P4::None, 0);
                b.emit_op(Opcode::Insert, 0, 7, 1, P4::None, 0);
            }

            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);
            let body = b.emit_label();
            b.resolve_label(body);
            for column in 0_i32..5 {
                let target_reg = column + 1;
                b.emit_op(Opcode::Column, 0, column, target_reg, P4::None, 0);
            }
            b.emit_op(Opcode::ResultRow, 1, 5, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::Next, 0, 0, body, P4::None, 0);

            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            observed_rows.len(),
            row_count,
            "MakeRecord INSERT path should persist every generated row",
        );

        let oracle = rusqlite::Connection::open_in_memory().expect("oracle should open");
        oracle
            .execute(
                "CREATE TABLE t (c0 INTEGER, c1 REAL, c2 TEXT, c3 BLOB, c4 NUMERIC)",
                [],
            )
            .expect("oracle schema should create");
        let mut insert = oracle
            .prepare("INSERT INTO t (c0, c1, c2, c3, c4) VALUES (?1, ?2, ?3, ?4, ?5)")
            .expect("oracle insert should prepare");
        for row in &expected_rows {
            let params = row
                .iter()
                .map(track_r_to_rusqlite_value)
                .collect::<Vec<_>>();
            insert
                .execute(params_from_iter(params))
                .expect("oracle insert should succeed");
        }

        let mut query = oracle
            .prepare("SELECT c0, c1, c2, c3, c4 FROM t ORDER BY rowid")
            .expect("oracle query should prepare");
        let oracle_rows = query
            .query_map([], |row| {
                (0..5)
                    .map(|idx| {
                        row.get::<_, RusqliteValue>(idx)
                            .map(track_r_from_rusqlite_value)
                    })
                    .collect::<rusqlite::Result<Vec<_>>>()
            })
            .expect("oracle query should execute")
            .collect::<rusqlite::Result<Vec<_>>>()
            .expect("oracle rows should collect");

        assert_eq!(
            observed_rows.len(),
            oracle_rows.len(),
            "VDBE and oracle should expose the same row count",
        );
        for (idx, (observed, oracle_row)) in
            observed_rows.iter().zip(oracle_rows.iter()).enumerate()
        {
            track_r_assert_rows_eq(
                observed,
                oracle_row,
                &format!("track_r 10k insert oracle row {idx} mismatch"),
            );
        }

        track_r_log_metrics(
            "test_track_r_make_record_insert_10k_oracle",
            1,
            0,
            start.elapsed().as_nanos(),
        );
    }

    #[test]
    fn test_track_r_make_record_roundtrip_10k_records() {
        let row_count = 10_000usize;
        let mut encoded = Vec::new();
        let mut decoded = Vec::new();
        let mut alloc_count = 0usize;
        let mut encoded_capacity = 0usize;
        let start = Instant::now();

        for seed in 0..row_count {
            let values = track_r_generated_record(seed);
            serialize_record_iter_into(values.iter(), &mut encoded);
            parse_record_into(&encoded, &mut decoded).expect("roundtrip decode should succeed");
            track_r_assert_rows_eq(&decoded, &values, "track_r roundtrip decode mismatch");

            let current_capacity = encoded.capacity();
            if current_capacity != encoded_capacity {
                encoded_capacity = current_capacity;
                alloc_count = alloc_count.saturating_add(1);
            }
        }

        track_r_log_metrics(
            "test_track_r_make_record_roundtrip_10k_records",
            alloc_count,
            encoded_capacity.max(encoded.capacity()),
            start.elapsed().as_nanos(),
        );
    }

    #[test]
    fn test_set_bindings_slice_keeps_small_binding_sets_inline() {
        let mut engine = VdbeEngine::new(1);
        engine.set_bindings_slice(&[SqliteValue::Integer(7)]);

        assert_eq!(engine.bindings.len(), 1);
        assert!(
            !engine.bindings.spilled(),
            "single-parameter statements should keep bindings inline"
        );
        assert_eq!(engine.bindings[0], SqliteValue::Integer(7));
    }

    #[test]
    fn test_execute_with_borrowed_bindings_uses_override_for_single_run() {
        let mut builder = ProgramBuilder::new();
        builder.emit_op(Opcode::Variable, 1, 1, 0, P4::None, 0);
        builder.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
        builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let program = builder.finish().expect("program should build");

        let mut engine = VdbeEngine::new(program.register_count());
        engine.set_bindings_slice(&[SqliteValue::Integer(7)]);

        let outcome = engine
            .execute_with_borrowed_bindings(&program, Some(&[SqliteValue::Integer(11)]))
            .expect("execution should succeed");

        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(
            engine.take_results().into_iter().next().unwrap().into_vec(),
            vec![SqliteValue::Integer(11)]
        );
        assert_eq!(
            engine.bindings[0],
            SqliteValue::Integer(7),
            "borrowed bindings should not overwrite the cached owned binding set"
        );
    }

    #[test]
    fn test_register_value_i64_store_and_retrieve() {
        let mut engine = VdbeEngine::new(2);
        engine.set_reg_int(1, -9_876_543_210);

        assert_eq!(engine.get_reg(1), &SqliteValue::Integer(-9_876_543_210));
        assert_eq!(engine.take_reg(1), SqliteValue::Integer(-9_876_543_210));
        assert_eq!(engine.get_reg(1), &SqliteValue::Null);
    }

    #[test]
    fn test_register_text_cache_stays_valid_while_storage_cursor_row_is_pinned() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_metrics_enabled = vdbe_metrics_enabled();
        reset_vdbe_metrics();
        set_vdbe_metrics_enabled(true);

        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        db.get_table_mut(root)
            .expect("table should exist")
            .insert(1, vec![SqliteValue::Text("alpha-track-s".into())]);

        let mut engine = VdbeEngine::new(2);
        engine.collect_vdbe_metrics = true;
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        assert!(engine.open_storage_cursor(0, root, false));
        engine.cursor_root_pages.insert(0, root);
        {
            let sc = engine
                .storage_cursors
                .get_mut(&0)
                .expect("storage cursor should exist");
            assert!(sc.cursor.first(&sc.cx).expect("cursor should rewind"));
        }

        let before = vdbe_metrics_snapshot();
        let first = engine
            .cursor_column(0, 0)
            .expect("first text decode should succeed");
        let second = engine
            .cursor_column(0, 0)
            .expect("pinned-row cache hit should succeed");
        let after = vdbe_metrics_snapshot();
        let sc = engine
            .storage_cursors
            .get(&0)
            .expect("storage cursor should exist");

        assert_eq!(first, SqliteValue::Text("alpha-track-s".into()));
        assert_eq!(second, first);
        assert!(sc.row_decode.cached_value_ready(0));
        assert_eq!(
            sc.row_decode.cached_value(0).and_then(SqliteValue::as_text),
            Some("alpha-track-s")
        );
        assert_eq!(
            after.decode_cache_misses_total - before.decode_cache_misses_total,
            1
        );
        assert_eq!(
            after.decode_cache_hits_total - before.decode_cache_hits_total,
            1
        );
        assert_eq!(
            after.decode_cache_invalidations_position_total
                - before.decode_cache_invalidations_position_total,
            0
        );
        assert_eq!(
            after.decode_cache_invalidations_write_total
                - before.decode_cache_invalidations_write_total,
            0
        );

        set_vdbe_metrics_enabled(prev_metrics_enabled);
    }

    #[test]
    fn test_register_value_make_record_sideband_materializes_into_sqlite_blob_value() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        reset_vdbe_test_sideband_materialization_count();

        let mut engine = VdbeEngine::new(4);
        engine.set_reg_int(1, 7);
        engine.set_reg(2, SqliteValue::Text("payload-track-s".into()));

        let make_record = VdbeOp {
            opcode: Opcode::MakeRecord,
            p1: 1,
            p2: 2,
            p3: 3,
            p4: P4::None,
            p5: 0,
        };
        engine.execute_make_record_hot(&make_record, false);

        assert_eq!(
            engine.get_reg(3),
            &SqliteValue::Null,
            "MakeRecord keeps bytes in sideband until a consumer asks for an owned SqliteValue"
        );

        let before = vdbe_test_sideband_materialization_count_snapshot();
        let converted = engine.clone_reg_materialized(3);
        let after = vdbe_test_sideband_materialization_count_snapshot();

        assert_eq!(after - before, 1);
        assert_eq!(
            decode_record(&converted).expect("sideband value should decode"),
            vec![
                SqliteValue::Integer(7),
                SqliteValue::Text("payload-track-s".into()),
            ]
        );
        assert_eq!(
            decode_record(engine.get_reg(3)).expect("register should now hold the blob"),
            vec![
                SqliteValue::Integer(7),
                SqliteValue::Text("payload-track-s".into()),
            ]
        );
    }

    #[test]
    fn test_copy_materializes_make_record_sideband_before_copying() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        reset_vdbe_test_sideband_materialization_count();

        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::Integer, 33, 1, 0, P4::None, 0);
            b.emit_op(
                Opcode::String8,
                0,
                2,
                0,
                P4::Str("copy-sideband".to_owned()),
                0,
            );
            b.emit_op(Opcode::MakeRecord, 1, 2, 3, P4::None, 0);
            b.emit_op(Opcode::Copy, 3, 4, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 4, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(vdbe_test_sideband_materialization_count_snapshot(), 1);
        assert_eq!(
            decode_record(&rows[0][0]).expect("copied record blob should decode"),
            vec![
                SqliteValue::Integer(33),
                SqliteValue::Text("copy-sideband".into()),
            ]
        );
    }

    #[test]
    fn test_scopy_materializes_make_record_sideband_before_copying() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        reset_vdbe_test_sideband_materialization_count();

        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::Integer, 44, 1, 0, P4::None, 0);
            b.emit_op(
                Opcode::String8,
                0,
                2,
                0,
                P4::Str("scopy-sideband".to_owned()),
                0,
            );
            b.emit_op(Opcode::MakeRecord, 1, 2, 3, P4::None, 0);
            b.emit_op(Opcode::SCopy, 3, 4, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 4, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(vdbe_test_sideband_materialization_count_snapshot(), 1);
        assert_eq!(
            decode_record(&rows[0][0]).expect("shallow-copied record blob should decode"),
            vec![
                SqliteValue::Integer(44),
                SqliteValue::Text("scopy-sideband".into()),
            ]
        );
    }

    #[test]
    fn test_second_make_record_preserves_unconsumed_prior_sideband() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        reset_vdbe_test_sideband_materialization_count();

        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_first_value = b.alloc_reg();
            let r_first_record = b.alloc_reg();
            let r_second_value = b.alloc_reg();
            let r_second_record = b.alloc_reg();
            b.emit_op(Opcode::Integer, 11, r_first_value, 0, P4::None, 0);
            b.emit_op(
                Opcode::MakeRecord,
                r_first_value,
                1,
                r_first_record,
                P4::None,
                0,
            );
            b.emit_op(Opcode::Integer, 22, r_second_value, 0, P4::None, 0);
            b.emit_op(
                Opcode::MakeRecord,
                r_second_value,
                1,
                r_second_record,
                P4::None,
                0,
            );
            b.emit_op(Opcode::ResultRow, r_first_record, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_second_record, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(vdbe_test_sideband_materialization_count_snapshot(), 2);
        assert_eq!(
            decode_record(&rows[0][0]).expect("first pending record should decode"),
            vec![SqliteValue::Integer(11)]
        );
        assert_eq!(
            decode_record(&rows[1][0]).expect("second pending record should decode"),
            vec![SqliteValue::Integer(22)]
        );
    }

    #[test]
    fn test_make_record_materializes_sideband_when_source_overlaps_target() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        reset_vdbe_test_sideband_materialization_count();

        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_value = b.alloc_reg();
            let r_record = b.alloc_reg();
            b.emit_op(Opcode::Integer, 33, r_value, 0, P4::None, 0);
            b.emit_op(Opcode::MakeRecord, r_value, 1, r_record, P4::None, 0);
            b.emit_op(Opcode::MakeRecord, r_record, 1, r_record, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_record, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(vdbe_test_sideband_materialization_count_snapshot(), 2);
        let outer = decode_record(&rows[0][0]).expect("outer record should decode");
        assert_eq!(outer.len(), 1);
        let inner = decode_record(&outer[0]).expect("nested record should decode");
        assert_eq!(inner, vec![SqliteValue::Integer(33)]);
    }

    #[test]
    fn test_copy_range_uses_source_snapshot_when_ranges_overlap() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::Integer, 10, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 20, 2, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 30, 3, 0, P4::None, 0);
            b.emit_op(Opcode::Copy, 1, 2, 2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 1, 4, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows[0],
            vec![
                SqliteValue::Integer(10),
                SqliteValue::Integer(10),
                SqliteValue::Integer(20),
                SqliteValue::Integer(30),
            ]
        );
    }

    #[test]
    fn test_register_value_take_reg_materializes_sideband_once_and_clears_slot() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        reset_vdbe_test_sideband_materialization_count();

        let mut engine = VdbeEngine::new(4);
        engine.set_reg_int(1, 17);
        engine.set_reg(2, SqliteValue::Text("take-track-s".into()));

        let make_record = VdbeOp {
            opcode: Opcode::MakeRecord,
            p1: 1,
            p2: 2,
            p3: 3,
            p4: P4::None,
            p5: 0,
        };
        engine.execute_make_record_hot(&make_record, false);

        let before = vdbe_test_sideband_materialization_count_snapshot();
        let taken = engine.take_reg(3);
        let after = vdbe_test_sideband_materialization_count_snapshot();

        assert_eq!(
            after - before,
            1,
            "taking a sideband-backed register should materialize it exactly once"
        );
        assert_eq!(
            decode_record(&taken).expect("taken register value should decode"),
            vec![
                SqliteValue::Integer(17),
                SqliteValue::Text("take-track-s".into()),
            ]
        );
        assert_eq!(
            engine.get_reg(3),
            &SqliteValue::Null,
            "take_reg must clear the register slot after moving the value out"
        );
        assert_eq!(
            engine.take_reg(3),
            SqliteValue::Null,
            "subsequent reads after take_reg should observe the cleared slot"
        );
        assert_eq!(
            vdbe_test_sideband_materialization_count_snapshot(),
            after,
            "cleared register should not trigger a second sideband materialization"
        );
    }

    #[test]
    fn test_register_value_insert_avoids_make_record_blob_materialization() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        reset_vdbe_test_sideband_materialization_count();

        let mut db = MemDatabase::new();
        let root = db.create_table(2);
        let before = vdbe_test_sideband_materialization_count_snapshot();
        let (rows, _) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(2), 0);
            b.emit_op(Opcode::NewRowid, 0, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 42, 2, 0, P4::None, 0);
            b.emit_op(Opcode::String8, 0, 3, 0, P4::Str("alpha".to_owned()), 0);
            b.emit_op(Opcode::MakeRecord, 2, 2, 4, P4::None, 0);
            b.emit_op(Opcode::Insert, 0, 4, 1, P4::None, 0);
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);

            let body = b.current_addr();
            b.emit_op(Opcode::Column, 0, 0, 5, P4::None, 0);
            b.emit_op(Opcode::Column, 0, 1, 6, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 5, 2, 0, P4::None, 0);

            let next_target =
                i32::try_from(body).expect("program counter should fit into i32 for tests");
            b.emit_op(Opcode::Next, 0, next_target, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        let after = vdbe_test_sideband_materialization_count_snapshot();

        assert_eq!(
            rows,
            vec![vec![
                SqliteValue::Integer(42),
                SqliteValue::Text("alpha".into()),
            ]]
        );
        assert_eq!(
            after - before,
            0,
            "INSERT should consume MakeRecord sideband bytes directly instead of materializing an Arc-backed record blob"
        );
    }

    #[test]
    fn test_register_value_cursor_move_invalidates_cached_text() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_metrics_enabled = vdbe_metrics_enabled();
        reset_vdbe_metrics();
        set_vdbe_metrics_enabled(true);

        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).expect("table should exist");
        table.insert(1, vec![SqliteValue::Text("alpha-track-s".into())]);
        table.insert(2, vec![SqliteValue::Text("beta-track-s".into())]);

        let mut engine = VdbeEngine::new(2);
        engine.collect_vdbe_metrics = true;
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        assert!(engine.open_storage_cursor(0, root, false));
        engine.cursor_root_pages.insert(0, root);
        {
            let sc = engine
                .storage_cursors
                .get_mut(&0)
                .expect("storage cursor should exist");
            assert!(sc.cursor.first(&sc.cx).expect("cursor should rewind"));
        }

        let before = vdbe_metrics_snapshot();
        assert_eq!(
            engine
                .cursor_column(0, 0)
                .expect("first text decode should succeed"),
            SqliteValue::Text("alpha-track-s".into())
        );
        {
            let sc = engine
                .storage_cursors
                .get_mut(&0)
                .expect("storage cursor should exist");
            assert!(sc.cursor.next(&sc.cx).expect("cursor should advance"));
        }
        assert_eq!(
            engine
                .cursor_column(0, 0)
                .expect("post-move text decode should succeed"),
            SqliteValue::Text("beta-track-s".into())
        );
        let after = vdbe_metrics_snapshot();
        let sc = engine
            .storage_cursors
            .get(&0)
            .expect("storage cursor should exist");

        assert!(sc.row_decode.cached_value_ready(0));
        assert_eq!(
            sc.row_decode.cached_value(0).and_then(SqliteValue::as_text),
            Some("beta-track-s")
        );
        assert_eq!(
            after.decode_cache_misses_total - before.decode_cache_misses_total,
            2
        );
        assert_eq!(
            after.decode_cache_hits_total - before.decode_cache_hits_total,
            0
        );
        assert_eq!(
            after.decode_cache_invalidations_position_total
                - before.decode_cache_invalidations_position_total,
            1
        );
        assert_eq!(
            after.decode_cache_invalidations_write_total
                - before.decode_cache_invalidations_write_total,
            0
        );
        assert_eq!(
            after.decode_cache_invalidations_pseudo_total
                - before.decode_cache_invalidations_pseudo_total,
            0
        );

        set_vdbe_metrics_enabled(prev_metrics_enabled);
    }

    #[test]
    fn test_storage_cursor_same_slot_write_mutation_invalidates_cached_text() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_metrics_enabled = vdbe_metrics_enabled();
        reset_vdbe_metrics();
        set_vdbe_metrics_enabled(true);

        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).expect("table should exist");
        table.insert(1, vec![SqliteValue::Text("alpha-track-j".into())]);
        table.insert(2, vec![SqliteValue::Text("beta-track-j".into())]);

        let mut engine = VdbeEngine::new(2);
        engine.collect_vdbe_metrics = true;
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        assert!(engine.open_storage_cursor(0, root, true));
        engine.cursor_root_pages.insert(0, root);
        {
            let sc = engine
                .storage_cursors
                .get_mut(&0)
                .expect("storage cursor should exist");
            assert!(sc.cursor.first(&sc.cx).expect("cursor should rewind"));
        }

        let before = vdbe_metrics_snapshot();
        assert_eq!(
            engine
                .cursor_column(0, 0)
                .expect("first text decode should succeed"),
            SqliteValue::Text("alpha-track-j".into())
        );
        {
            let sc = engine
                .storage_cursors
                .get_mut(&0)
                .expect("storage cursor should exist");
            sc.cursor
                .delete(&sc.cx)
                .expect("direct delete should succeed without helper invalidation");
        }
        assert_eq!(
            engine
                .cursor_column(0, 0)
                .expect("same-slot successor should force a fresh decode"),
            SqliteValue::Text("beta-track-j".into())
        );
        let after = vdbe_metrics_snapshot();
        let sc = engine
            .storage_cursors
            .get(&0)
            .expect("storage cursor should exist");

        assert!(sc.row_decode.cached_value_ready(0));
        assert_eq!(
            sc.row_decode.cached_value(0).and_then(SqliteValue::as_text),
            Some("beta-track-j")
        );
        assert_eq!(
            after.decode_cache_invalidations_write_total
                - before.decode_cache_invalidations_write_total,
            1
        );
        assert_eq!(
            after.decode_cache_invalidations_position_total
                - before.decode_cache_invalidations_position_total,
            0
        );
        assert_eq!(
            after.decode_cache_hits_total - before.decode_cache_hits_total,
            0
        );
        assert_eq!(
            after.decode_cache_misses_total - before.decode_cache_misses_total,
            2
        );

        set_vdbe_metrics_enabled(prev_metrics_enabled);
    }

    #[test]
    fn test_execute_honors_cancelled_execution_context() {
        let mut builder = ProgramBuilder::new();
        for _ in 0..=VDBE_EXECUTION_CHECKPOINT_INTERVAL {
            builder.emit_op(Opcode::Noop, 0, 0, 0, P4::None, 0);
        }
        builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let program = builder.finish().expect("program should build");

        let cx = Cx::new();
        cx.transition_to_running();
        cx.cancel_with_reason(fsqlite_types::cx::CancelReason::UserInterrupt);

        let mut engine =
            VdbeEngine::new_with_execution_cx(program.register_count(), &cx, PageSize::DEFAULT);
        let err = engine.execute(&program).unwrap_err();
        assert!(matches!(err, FrankenError::Abort));
        assert!(
            engine.results().is_empty(),
            "cancelled execute should not emit rows"
        );
    }

    #[test]
    fn test_execute_reuse_clears_prior_results() {
        let mut first_builder = ProgramBuilder::new();
        first_builder.emit_op(Opcode::Integer, 11, 1, 0, P4::None, 0);
        first_builder.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
        first_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let first_program = first_builder.finish().expect("first program should build");

        let mut second_builder = ProgramBuilder::new();
        second_builder.emit_op(Opcode::Integer, 22, 1, 0, P4::None, 0);
        second_builder.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
        second_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let second_program = second_builder
            .finish()
            .expect("second program should build");

        let mut engine = VdbeEngine::new(
            first_program
                .register_count()
                .max(second_program.register_count()),
        );
        assert_eq!(
            engine.execute(&first_program).expect("first execution"),
            ExecOutcome::Done
        );
        assert_eq!(
            engine.execute(&second_program).expect("second execution"),
            ExecOutcome::Done
        );

        let results = engine
            .results()
            .iter()
            .map(|row| row.clone().into_vec())
            .collect::<Vec<_>>();
        assert_eq!(results, vec![vec![SqliteValue::Integer(22)]]);
    }

    #[test]
    fn test_execute_reuse_resets_statement_accounting() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let mut insert_builder = ProgramBuilder::new();
        let insert_end = insert_builder.emit_label();
        insert_builder.emit_jump_to_label(Opcode::Init, 0, 0, insert_end, P4::None, 0);
        insert_builder.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
        insert_builder.emit_op(Opcode::Integer, 1, 1, 0, P4::None, 0);
        insert_builder.emit_op(Opcode::Integer, 42, 2, 0, P4::None, 0);
        insert_builder.emit_op(Opcode::MakeRecord, 2, 1, 3, P4::None, 0);
        insert_builder.emit_op(Opcode::Insert, 0, 3, 1, P4::None, 0);
        insert_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        insert_builder.resolve_label(insert_end);
        let insert_program = insert_builder
            .finish()
            .expect("insert program should build");

        let mut noop_builder = ProgramBuilder::new();
        noop_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let noop_program = noop_builder.finish().expect("noop program should build");

        let mut engine = VdbeEngine::new(
            insert_program
                .register_count()
                .max(noop_program.register_count()),
        );
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);

        assert_eq!(
            engine.execute(&insert_program).expect("insert execution"),
            ExecOutcome::Done
        );
        assert_eq!(engine.changes(), 1);
        assert_eq!(engine.last_insert_rowid(), Some(1));

        assert_eq!(
            engine.execute(&noop_program).expect("noop execution"),
            ExecOutcome::Done
        );
        assert_eq!(engine.changes(), 0);
        assert_eq!(engine.last_insert_rowid(), None);
    }

    #[test]
    fn test_execute_reuse_empty_program_clears_prior_statement_state() {
        let mut first_builder = ProgramBuilder::new();
        first_builder.emit_op(Opcode::Integer, 11, 1, 0, P4::None, 0);
        first_builder.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
        first_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let first_program = first_builder.finish().expect("first program should build");

        let empty_program = ProgramBuilder::new()
            .finish()
            .expect("empty program should build");

        let mut engine = VdbeEngine::new(
            first_program
                .register_count()
                .max(empty_program.register_count()),
        );
        assert_eq!(
            engine.execute(&first_program).expect("first execution"),
            ExecOutcome::Done
        );
        assert_eq!(engine.results().len(), 1);

        assert_eq!(
            engine.execute(&empty_program).expect("empty execution"),
            ExecOutcome::Done
        );
        assert!(engine.results().is_empty());
        assert_eq!(engine.changes(), 0);
        assert_eq!(engine.last_insert_rowid(), None);
    }

    #[test]
    fn test_reset_for_reuse_keeps_cached_engine_results_clean() {
        let mut first_builder = ProgramBuilder::new();
        let first_reg = first_builder.alloc_reg();
        first_builder.emit_op(Opcode::Integer, 11, first_reg, 0, P4::None, 0);
        first_builder.emit_op(Opcode::ResultRow, first_reg, 1, 0, P4::None, 0);
        first_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let first_program = first_builder.finish().expect("first program should build");

        let mut second_builder = ProgramBuilder::new();
        let second_reg = second_builder.alloc_reg();
        second_builder.emit_op(Opcode::Integer, 22, second_reg, 0, P4::None, 0);
        second_builder.emit_op(Opcode::ResultRow, second_reg, 1, 0, P4::None, 0);
        second_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let second_program = second_builder
            .finish()
            .expect("second program should build");

        let mut engine = VdbeEngine::new(
            first_program
                .register_count()
                .max(second_program.register_count()),
        );
        assert_eq!(
            engine.execute(&first_program).expect("first execution"),
            ExecOutcome::Done
        );

        let reset_cx = Cx::new();
        engine.reset_for_reuse(
            first_program
                .register_count()
                .max(second_program.register_count()),
            &reset_cx,
            PageSize::DEFAULT,
        );

        assert_eq!(
            engine.execute(&second_program).expect("second execution"),
            ExecOutcome::Done
        );
        assert_eq!(
            engine
                .results()
                .iter()
                .map(|row| row.clone().into_vec())
                .collect::<Vec<_>>(),
            vec![vec![SqliteValue::Integer(22)]]
        );
    }

    #[test]
    fn test_reset_for_reuse_clears_result_row_collection_cap() {
        let mut first_builder = ProgramBuilder::new();
        let first_reg = first_builder.alloc_reg();
        first_builder.emit_op(Opcode::Integer, 11, first_reg, 0, P4::None, 0);
        first_builder.emit_op(Opcode::ResultRow, first_reg, 1, 0, P4::None, 0);
        first_builder.emit_op(Opcode::Integer, 12, first_reg, 0, P4::None, 0);
        first_builder.emit_op(Opcode::ResultRow, first_reg, 1, 0, P4::None, 0);
        first_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let first_program = first_builder.finish().expect("first program should build");

        let mut second_builder = ProgramBuilder::new();
        let second_reg = second_builder.alloc_reg();
        second_builder.emit_op(Opcode::Integer, 21, second_reg, 0, P4::None, 0);
        second_builder.emit_op(Opcode::ResultRow, second_reg, 1, 0, P4::None, 0);
        second_builder.emit_op(Opcode::Integer, 22, second_reg, 0, P4::None, 0);
        second_builder.emit_op(Opcode::ResultRow, second_reg, 1, 0, P4::None, 0);
        second_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let second_program = second_builder
            .finish()
            .expect("second program should build");

        let mut engine = VdbeEngine::new(
            first_program
                .register_count()
                .max(second_program.register_count()),
        );
        engine.set_max_collected_result_rows(Some(1));
        assert_eq!(
            engine.execute(&first_program).expect("first execution"),
            ExecOutcome::Done
        );
        assert_eq!(engine.results().len(), 1);

        let reset_cx = Cx::new();
        engine.reset_for_reuse(
            first_program
                .register_count()
                .max(second_program.register_count()),
            &reset_cx,
            PageSize::DEFAULT,
        );

        assert_eq!(
            engine.execute(&second_program).expect("second execution"),
            ExecOutcome::Done
        );
        assert_eq!(
            engine
                .results()
                .iter()
                .map(|row| row.clone().into_vec())
                .collect::<Vec<_>>(),
            vec![
                vec![SqliteValue::Integer(21)],
                vec![SqliteValue::Integer(22)],
            ],
            "reset_for_reuse should restore uncapped row collection"
        );
    }

    #[test]
    fn test_reusable_table_execution_state_skips_identical_rebinds_after_preserving_reset() {
        let mut engine = VdbeEngine::new(4);
        let func_registry = Arc::new(FunctionRegistry::new());
        let collation_registry = Arc::new(std::sync::Mutex::new(CollationRegistry::new()));
        let state = ReusableTableExecutionState {
            func_registry: Arc::clone(&func_registry),
            collation_registry: Arc::clone(&collation_registry),
            schema_cookie: 7,
            autoincrement_seq_by_root_page: HashMap::from([(5, 11)]),
            rowid_alias_col_by_root_page: Arc::new(HashMap::from([(5, 0)])),
            table_column_count_by_root_page: Arc::new(HashMap::from([(5, 2)])),
            first_not_null_non_ipk_col_by_root_page: Arc::new(HashMap::from([(5, 1)])),
            column_defaults_by_root_page: Arc::new(HashMap::new()),
            index_desc_flags_by_root_page: Arc::new(HashMap::from([(9, vec![false, true])])),
            index_collations_by_root_page: Arc::new(HashMap::from([(
                9,
                vec![Some("BINARY".to_owned()), None],
            )])),
            reject_mem_fallback: false,
            memdb_rows_loaded: true,
            storage_cursor_memdb_count_shortcuts_safe: true,
            version_store: None,
            collect_result_rows: false,
            max_collected_result_rows: Some(1),
        };

        let first_outcome = engine.apply_reusable_table_execution_state(state.clone());
        assert!(
            first_outcome.metadata_rebind_count > 0,
            "fresh engines should bind the reusable execution state"
        );

        let reset_cx = Cx::new();
        engine.reset_for_reuse_preserving_runtime_setup(4, &reset_cx, PageSize::DEFAULT, false);
        let second_outcome = engine.apply_reusable_table_execution_state(state.clone());
        assert_eq!(
            second_outcome.metadata_rebind_count, 0,
            "preserving reset should let identical reusable table state skip rebinding"
        );

        engine.reset_for_reuse(4, &reset_cx, PageSize::DEFAULT);
        let third_outcome = engine.apply_reusable_table_execution_state(state);
        assert!(
            third_outcome.metadata_rebind_count > 0,
            "legacy reset_for_reuse should still clear runtime bindings"
        );
    }

    #[test]
    fn test_w1_function_cache_preserved_when_func_registry_unchanged() {
        // W1 optimization: function caches should be preserved across reusable-lane
        // resets when the func_registry pointer is unchanged, avoiding unnecessary
        // cache rebuilds on every prepared-statement execution.
        let mut engine = VdbeEngine::new(4);
        let func_registry = Arc::new(FunctionRegistry::new());
        let collation_registry = Arc::new(std::sync::Mutex::new(CollationRegistry::new()));
        let state = ReusableTableExecutionState {
            func_registry: Arc::clone(&func_registry),
            collation_registry: Arc::clone(&collation_registry),
            schema_cookie: 1,
            autoincrement_seq_by_root_page: HashMap::new(),
            rowid_alias_col_by_root_page: Arc::new(HashMap::new()),
            table_column_count_by_root_page: Arc::new(HashMap::new()),
            first_not_null_non_ipk_col_by_root_page: Arc::new(HashMap::new()),
            column_defaults_by_root_page: Arc::new(HashMap::new()),
            index_desc_flags_by_root_page: Arc::new(HashMap::new()),
            index_collations_by_root_page: Arc::new(HashMap::new()),
            reject_mem_fallback: false,
            memdb_rows_loaded: false,
            storage_cursor_memdb_count_shortcuts_safe: false,
            version_store: None,
            collect_result_rows: true,
            max_collected_result_rows: None,
        };

        // First apply: fresh engine, caches will be cleared (func_registry was None)
        let first_outcome = engine.apply_reusable_table_execution_state(state.clone());
        assert!(
            first_outcome.function_cache_cleared,
            "first apply should clear function caches (func_registry was None)"
        );

        // Reset preserving runtime setup, then re-apply with same func_registry
        let reset_cx = Cx::new();
        engine.reset_for_reuse_preserving_runtime_setup(4, &reset_cx, PageSize::DEFAULT, false);
        let second_outcome = engine.apply_reusable_table_execution_state(state.clone());
        assert!(
            !second_outcome.function_cache_cleared,
            "W1: function caches should NOT be cleared when func_registry is unchanged"
        );

        // Change the func_registry and verify caches ARE cleared
        let new_func_registry = Arc::new(FunctionRegistry::new());
        let changed_state = ReusableTableExecutionState {
            func_registry: Arc::clone(&new_func_registry),
            ..state
        };
        engine.reset_for_reuse_preserving_runtime_setup(4, &reset_cx, PageSize::DEFAULT, false);
        let third_outcome = engine.apply_reusable_table_execution_state(changed_state);
        assert!(
            third_outcome.function_cache_cleared,
            "function caches should be cleared when func_registry changes"
        );

        // Legacy reset should clear everything
        engine.reset_for_reuse(4, &reset_cx, PageSize::DEFAULT);
        let fourth_outcome =
            engine.apply_reusable_table_execution_state(ReusableTableExecutionState {
                func_registry: Arc::clone(&new_func_registry),
                collation_registry: Arc::clone(&collation_registry),
                schema_cookie: 1,
                autoincrement_seq_by_root_page: HashMap::new(),
                rowid_alias_col_by_root_page: Arc::new(HashMap::new()),
                table_column_count_by_root_page: Arc::new(HashMap::new()),
                first_not_null_non_ipk_col_by_root_page: Arc::new(HashMap::new()),
                column_defaults_by_root_page: Arc::new(HashMap::new()),
                index_desc_flags_by_root_page: Arc::new(HashMap::new()),
                index_collations_by_root_page: Arc::new(HashMap::new()),
                reject_mem_fallback: false,
                memdb_rows_loaded: false,
                storage_cursor_memdb_count_shortcuts_safe: false,
                version_store: None,
                collect_result_rows: true,
                max_collected_result_rows: None,
            });
        assert!(
            fourth_outcome.function_cache_cleared,
            "legacy reset_for_reuse clears caches, so next apply should clear them again"
        );
    }

    #[test]
    fn test_execute_clears_cold_subtype_state_between_statements() {
        let mut subtype_builder = ProgramBuilder::new();
        let subtype_reg = subtype_builder.alloc_reg();
        let tagged_value_reg = subtype_builder.alloc_reg();
        subtype_builder.emit_op(Opcode::Integer, 74, subtype_reg, 0, P4::None, 0);
        subtype_builder.emit_op(
            Opcode::SetSubtype,
            subtype_reg,
            tagged_value_reg,
            0,
            P4::None,
            0,
        );
        subtype_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let subtype_program = subtype_builder
            .finish()
            .expect("subtype program should build");

        let mut probe_builder = ProgramBuilder::new();
        let probe_result_reg = probe_builder.alloc_reg();
        let probe_value_reg = probe_builder.alloc_reg();
        probe_builder.emit_op(
            Opcode::GetSubtype,
            probe_value_reg,
            probe_result_reg,
            0,
            P4::None,
            0,
        );
        probe_builder.emit_op(Opcode::ResultRow, probe_result_reg, 1, 0, P4::None, 0);
        probe_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let probe_program = probe_builder.finish().expect("probe program should build");

        let mut engine = VdbeEngine::new(
            subtype_program
                .register_count()
                .max(probe_program.register_count()),
        );
        assert_eq!(
            engine.execute(&subtype_program).expect("subtype execution"),
            ExecOutcome::Done
        );
        assert_eq!(engine.register_subtype(tagged_value_reg), Some(74));
        assert!(
            engine
                .statement_cold_state
                .contains(StatementColdState::REGISTER_SUBTYPES)
        );

        assert_eq!(
            engine.execute(&probe_program).expect("probe execution"),
            ExecOutcome::Done
        );
        assert!(engine.register_subtype(tagged_value_reg).is_none());
        assert!(engine.statement_cold_state.is_empty());
        assert_eq!(
            engine
                .results()
                .iter()
                .map(|row| row.clone().into_vec())
                .collect::<Vec<_>>(),
            vec![vec![SqliteValue::Integer(0)]]
        );
    }

    #[test]
    fn test_secondary_index_rollback_removes_tracked_replace_entry() {
        let mut db = MemDatabase::new();
        let table_root = db.create_table(1);
        let index_root = db.allocate_root_page();

        let mut engine = VdbeEngine::new(8);
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);

        assert!(engine.open_storage_cursor(0, table_root, true));
        assert!(engine.open_storage_cursor(1, index_root, true));

        let payload = encode_record(&[SqliteValue::Integer(99)]);
        {
            let table_cursor = engine.storage_cursors.get_mut(&0).unwrap();
            table_cursor
                .cursor
                .table_insert(&table_cursor.cx, 2, &payload)
                .expect("provisional table row should insert");
            assert!(
                table_cursor
                    .cursor
                    .table_move_to(&table_cursor.cx, 2)
                    .expect("table seek should succeed")
                    .is_found()
            );
        }

        let index_key = encode_record(&[SqliteValue::Integer(7), SqliteValue::Integer(2)]);
        {
            let index_cursor = engine.storage_cursors.get_mut(&1).unwrap();
            index_cursor
                .cursor
                .index_insert(&index_cursor.cx, &index_key)
                .expect("index entry should insert");
            assert!(
                index_cursor
                    .cursor
                    .index_move_to(&index_cursor.cx, &index_key)
                    .expect("index seek should succeed")
                    .is_found()
            );
        }

        engine.changes = 1;
        engine.set_pending_insert_rollback(Some(PendingInsertRollback {
            cursor_id: 0,
            rowid: 2,
            previous_last_insert_rowid: 0,
            previous_last_insert_rowid_valid: false,
            update_restore: None,
        }));
        engine.push_pending_idx_entry(1, index_key.clone());

        engine
            .rollback_pending_insert_after_index_conflict()
            .expect("rollback should remove provisional row and tracked index entries");

        let table_cursor = engine.storage_cursors.get_mut(&0).unwrap();
        assert!(
            !table_cursor
                .cursor
                .table_move_to(&table_cursor.cx, 2)
                .expect("post-rollback table seek should succeed")
                .is_found()
        );

        let index_cursor = engine.storage_cursors.get_mut(&1).unwrap();
        assert!(
            !index_cursor
                .cursor
                .index_move_to(&index_cursor.cx, &index_key)
                .expect("post-rollback index seek should succeed")
                .is_found()
        );
        assert_eq!(engine.changes(), 0);
    }

    #[test]
    fn test_execute_insert_with_explicit_rowid_zero_tracks_last_insert_rowid() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let mut builder = ProgramBuilder::new();
        let end = builder.emit_label();
        builder.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        builder.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
        builder.emit_op(Opcode::Integer, 0, 1, 0, P4::None, 0);
        builder.emit_op(Opcode::Integer, 42, 2, 0, P4::None, 0);
        builder.emit_op(Opcode::MakeRecord, 2, 1, 3, P4::None, 0);
        builder.emit_op(Opcode::Insert, 0, 3, 1, P4::None, 0);
        builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        builder.resolve_label(end);
        let program = builder.finish().expect("program should build");

        let mut engine = VdbeEngine::new(program.register_count());
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);

        assert_eq!(
            engine.execute(&program).expect("insert execution"),
            ExecOutcome::Done
        );
        assert_eq!(engine.changes(), 1);
        assert_eq!(engine.last_insert_rowid(), Some(0));
    }

    #[test]
    fn test_execute_observes_cancelled_execution_context_before_first_opcode() {
        let cx = Cx::new();
        cx.cancel();

        let mut builder = ProgramBuilder::new();
        builder.emit_op(Opcode::Integer, 7, 1, 0, P4::None, 0);
        builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let program = builder.finish().expect("program should build");

        let mut engine =
            VdbeEngine::new_with_execution_cx(program.register_count(), &cx, PageSize::DEFAULT);
        let err = engine
            .execute(&program)
            .expect_err("cancelled execution context should abort before opcode dispatch");

        assert!(matches!(err, FrankenError::Abort));
    }

    #[test]
    fn test_execute_observes_execution_cx_cancellation_immediately_after_function_opcode() {
        let root_cx = Cx::new();

        let mut builder = ProgramBuilder::new();
        let result_reg = builder.alloc_reg();
        builder.emit_op(
            Opcode::Function,
            0,
            0,
            result_reg,
            P4::FuncName("cancel_exec".to_owned()),
            0,
        );
        builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let program = builder.finish().expect("program should build");

        let mut engine = VdbeEngine::new_with_execution_cx(
            program.register_count(),
            &root_cx,
            PageSize::DEFAULT,
        );
        let mut registry = FunctionRegistry::new();
        registry.register_scalar(CancelExecutionFunc {
            cx: root_cx.clone(),
        });
        engine.set_function_registry(Arc::new(registry));

        let err = engine
            .execute(&program)
            .expect_err("cancellation should be observed before dispatch continues");
        assert!(matches!(err, FrankenError::Abort));
        assert!(engine.results().is_empty());
    }

    /// Build and execute a program with the builtin function registry attached.
    fn run_program_with_functions(
        build: impl FnOnce(&mut ProgramBuilder),
    ) -> Vec<Vec<SqliteValue>> {
        let mut b = ProgramBuilder::new();
        build(&mut b);
        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        let mut registry = FunctionRegistry::new();
        register_builtins(&mut registry);
        engine.set_function_registry(Arc::new(registry));
        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        engine
            .take_results()
            .into_iter()
            .map(|v| v.into_vec())
            .collect()
    }

    #[test]
    fn test_opcode_register_spans_for_variable() {
        let op = VdbeOp {
            opcode: Opcode::Variable,
            p1: 2,
            p2: 9,
            p3: 0,
            p4: P4::None,
            p5: 0,
        };
        let spans = opcode_register_spans(&op);
        assert_eq!(spans.read_start, -1);
        assert_eq!(spans.read_len, 0);
        assert_eq!(spans.write_start, 9);
        assert_eq!(spans.write_len, 1);
    }

    #[test]
    fn test_opcode_register_spans_for_result_row() {
        let op = VdbeOp {
            opcode: Opcode::ResultRow,
            p1: 4,
            p2: 3,
            p3: 0,
            p4: P4::None,
            p5: 0,
        };
        let spans = opcode_register_spans(&op);
        assert_eq!(spans.read_start, 4);
        assert_eq!(spans.read_len, 3);
        assert_eq!(spans.write_start, -1);
        assert_eq!(spans.write_len, 0);
    }

    #[test]
    fn test_opcode_register_spans_for_copy_range() {
        let op = VdbeOp {
            opcode: Opcode::Copy,
            p1: 4,
            p2: 9,
            p3: 2,
            p4: P4::None,
            p5: 0,
        };
        let spans = opcode_register_spans(&op);
        assert_eq!(spans.read_start, 4);
        assert_eq!(spans.read_len, 3);
        assert_eq!(spans.write_start, 9);
        assert_eq!(spans.write_len, 3);
    }

    #[test]
    fn test_variable_uses_bound_parameter_value() {
        let rows = run_program_with_bindings(
            |b| {
                let end = b.emit_label();
                b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
                let r1 = b.alloc_reg();
                b.emit_op(Opcode::Variable, 2, r1, 0, P4::None, 0);
                b.emit_op(Opcode::ResultRow, r1, 1, 0, P4::None, 0);
                b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
                b.resolve_label(end);
            },
            vec![SqliteValue::Integer(11), SqliteValue::Text("bound".into())],
        );
        assert_eq!(rows, vec![vec![SqliteValue::Text("bound".into())]]);
    }

    #[test]
    fn test_variable_unbound_parameter_defaults_to_null() {
        let rows = run_program_with_bindings(
            |b| {
                let end = b.emit_label();
                b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
                let r1 = b.alloc_reg();
                b.emit_op(Opcode::Variable, 3, r1, 0, P4::None, 0);
                b.emit_op(Opcode::ResultRow, r1, 1, 0, P4::None, 0);
                b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
                b.resolve_label(end);
            },
            vec![SqliteValue::Integer(11)],
        );
        assert_eq!(rows, vec![vec![SqliteValue::Null]]);
    }

    // ── test_select_integer_literal ─────────────────────────────────────
    #[test]
    fn test_select_integer_literal() {
        // SELECT 42 → [(42,)]
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            b.emit_op(Opcode::Integer, 42, r1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0], vec![SqliteValue::Integer(42)]);
    }

    // ── test_select_arithmetic ──────────────────────────────────────────
    #[test]
    fn test_select_arithmetic() {
        // SELECT 1+2 → [(3,)]
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r1 = b.alloc_reg(); // 1
            let r2 = b.alloc_reg(); // 2
            let r3 = b.alloc_reg(); // result

            b.emit_op(Opcode::Integer, 1, r1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 2, r2, 0, P4::None, 0);
            b.emit_op(Opcode::Add, r1, r2, r3, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0], vec![SqliteValue::Integer(3)]);
    }

    // ── test_select_expression_eval ─────────────────────────────────────
    #[test]
    fn test_select_expression_eval() {
        // SELECT 1+2, 'abc'||'def' → [(3, "abcdef")]
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r3 = b.alloc_reg(); // 1+2 result
            let r4 = b.alloc_reg();
            let r5 = b.alloc_reg();
            let r6 = b.alloc_reg(); // concat result

            // 1 + 2
            b.emit_op(Opcode::Integer, 1, r1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 2, r2, 0, P4::None, 0);
            b.emit_op(Opcode::Add, r1, r2, r3, P4::None, 0);

            // 'abc' || 'def'
            b.emit_op(Opcode::String8, 0, r4, 0, P4::Str("abc".to_owned()), 0);
            b.emit_op(Opcode::String8, 0, r5, 0, P4::Str("def".to_owned()), 0);
            b.emit_op(Opcode::Concat, r5, r4, r6, P4::None, 0);

            b.emit_op(Opcode::ResultRow, r3, 1, 0, P4::None, 0);
            // Also emit second column as separate row for now
            b.emit_op(Opcode::ResultRow, r6, 1, 0, P4::None, 0);

            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows.len(), 2);
        assert_eq!(rows[0], vec![SqliteValue::Integer(3)]);
        assert_eq!(rows[1], vec![SqliteValue::Text("abcdef".into())]);
    }

    // ── test_select_multi_column ────────────────────────────────────────
    #[test]
    fn test_select_multi_column() {
        // SELECT 1+2, 'abc'||'def' as a single row
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let out_start = b.alloc_regs(2);
            let r_tmp1 = b.alloc_reg();
            let r_tmp2 = b.alloc_reg();

            // 1 + 2 → out_start
            b.emit_op(Opcode::Integer, 1, r_tmp1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 2, r_tmp2, 0, P4::None, 0);
            b.emit_op(Opcode::Add, r_tmp1, r_tmp2, out_start, P4::None, 0);

            // 'abc' || 'def' → out_start+1
            b.emit_op(Opcode::String8, 0, r_tmp1, 0, P4::Str("abc".to_owned()), 0);
            b.emit_op(Opcode::String8, 0, r_tmp2, 0, P4::Str("def".to_owned()), 0);
            b.emit_op(Opcode::Concat, r_tmp2, r_tmp1, out_start + 1, P4::None, 0);

            b.emit_op(Opcode::ResultRow, out_start, 2, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows.len(), 1);
        assert_eq!(
            rows[0],
            vec![SqliteValue::Integer(3), SqliteValue::Text("abcdef".into()),]
        );
    }

    // ── test_vdbe_null_handling ──────────────────────────────────────────
    #[test]
    fn test_vdbe_null_handling() {
        // NULL + 1 = NULL, NULL = NULL is NULL (no jump)
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_null = b.alloc_reg();
            let r_one = b.alloc_reg();
            let r_result = b.alloc_reg();
            let r_is_null = b.alloc_reg();

            // NULL
            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            // 1
            b.emit_op(Opcode::Integer, 1, r_one, 0, P4::None, 0);
            // NULL + 1
            b.emit_op(Opcode::Add, r_null, r_one, r_result, P4::None, 0);
            // Check: result IS NULL → set r_is_null=1
            b.emit_op(Opcode::Integer, 0, r_is_null, 0, P4::None, 0);
            let skip = b.emit_label();
            b.emit_jump_to_label(Opcode::NotNull, r_result, 0, skip, P4::None, 0);
            b.emit_op(Opcode::Integer, 1, r_is_null, 0, P4::None, 0);
            b.resolve_label(skip);

            b.emit_op(Opcode::ResultRow, r_result, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_is_null, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows.len(), 2);
        assert_eq!(rows[0], vec![SqliteValue::Null]); // NULL + 1 = NULL
        assert_eq!(rows[1], vec![SqliteValue::Integer(1)]); // IS NULL = true
    }

    // ── test_vdbe_comparison_affinity ────────────────────────────────────
    #[test]
    fn test_vdbe_comparison_affinity() {
        // Test: 5 > 3 → jump taken (result 1), 3 > 5 → not taken (result 0)
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_5 = b.alloc_reg();
            let r_3 = b.alloc_reg();
            let r_out = b.alloc_reg();

            b.emit_op(Opcode::Integer, 5, r_5, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 3, r_3, 0, P4::None, 0);

            // Test 5 > 3: if r_5 (p3) > r_3 (p1), jump.
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let gt_taken = b.emit_label();
            b.emit_jump_to_label(Opcode::Gt, r_3, r_5, gt_taken, P4::None, 0);
            // Not taken path:
            let done1 = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done1, P4::None, 0);
            // Taken path:
            b.resolve_label(gt_taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done1);

            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);

            // Test 3 > 5: should NOT jump
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let gt_taken2 = b.emit_label();
            // p3=r_3 (3), p1=r_5 (5): is 3 > 5? No.
            b.emit_jump_to_label(Opcode::Gt, r_5, r_3, gt_taken2, P4::None, 0);
            let done2 = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done2, P4::None, 0);
            b.resolve_label(gt_taken2);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done2);

            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows.len(), 2);
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]); // 5 > 3 = true
        assert_eq!(rows[1], vec![SqliteValue::Integer(0)]); // 3 > 5 = false
    }

    #[test]
    fn test_compare_opcode_uses_per_field_collation_list() {
        let mut b = ProgramBuilder::new();
        let left_key = b.alloc_regs(2);
        let right_key = b.alloc_regs(2);

        b.emit_op(
            Opcode::String8,
            0,
            left_key,
            0,
            P4::Str("Apple".to_owned()),
            0,
        );
        b.emit_op(
            Opcode::String8,
            0,
            left_key + 1,
            0,
            P4::Str("beta".to_owned()),
            0,
        );
        b.emit_op(
            Opcode::String8,
            0,
            right_key,
            0,
            P4::Str("apple".to_owned()),
            0,
        );
        b.emit_op(
            Opcode::String8,
            0,
            right_key + 1,
            0,
            P4::Str("Beta".to_owned()),
            0,
        );
        b.emit_op(
            Opcode::Compare,
            left_key,
            right_key,
            2,
            P4::Str("NOCASE,BINARY".to_owned()),
            0,
        );
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(
            engine.last_compare_result,
            Some(std::cmp::Ordering::Greater)
        );
    }

    #[test]
    fn test_compare_opcode_parses_trimmed_collation_list() {
        let mut b = ProgramBuilder::new();
        let left_key = b.alloc_regs(2);
        let right_key = b.alloc_regs(2);

        b.emit_op(
            Opcode::String8,
            0,
            left_key,
            0,
            P4::Str("Alpha".to_owned()),
            0,
        );
        b.emit_op(
            Opcode::String8,
            0,
            left_key + 1,
            0,
            P4::Str("tail   ".to_owned()),
            0,
        );
        b.emit_op(
            Opcode::String8,
            0,
            right_key,
            0,
            P4::Str("alpha".to_owned()),
            0,
        );
        b.emit_op(
            Opcode::String8,
            0,
            right_key + 1,
            0,
            P4::Str("tail".to_owned()),
            0,
        );
        b.emit_op(
            Opcode::Compare,
            left_key,
            right_key,
            2,
            P4::Str(" NOCASE | RTRIM ".to_owned()),
            0,
        );
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(engine.last_compare_result, Some(std::cmp::Ordering::Equal));
    }

    // ── test_vdbe_division_by_zero ──────────────────────────────────────
    #[test]
    fn test_vdbe_division_by_zero() {
        // SELECT 10 / 0 → NULL
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r3 = b.alloc_reg();

            b.emit_op(Opcode::Integer, 0, r1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 10, r2, 0, P4::None, 0);
            b.emit_op(Opcode::Divide, r1, r2, r3, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0], vec![SqliteValue::Null]); // div by zero → NULL
    }

    #[test]
    fn test_vdbe_nan_arithmetic_normalized_to_null() {
        // +Inf - +Inf and 0 * +Inf both produce NaN at IEEE-754 level.
        // VDBE register writes must normalize NaN to SQL NULL.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_inf = b.alloc_reg();
            let r_zero = b.alloc_reg();
            let r_sub = b.alloc_reg();
            let r_mul = b.alloc_reg();

            b.emit_op(Opcode::Real, 0, r_inf, 0, P4::Real(f64::INFINITY), 0);
            b.emit_op(Opcode::Real, 0, r_zero, 0, P4::Real(0.0), 0);
            b.emit_op(Opcode::Subtract, r_inf, r_inf, r_sub, P4::None, 0); // Inf - Inf
            b.emit_op(Opcode::Multiply, r_inf, r_zero, r_mul, P4::None, 0); // 0 * Inf
            b.emit_op(Opcode::ResultRow, r_sub, 2, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0], vec![SqliteValue::Null, SqliteValue::Null]);
    }

    // ── test_vdbe_string_concat_null ────────────────────────────────────
    #[test]
    fn test_vdbe_string_concat_null() {
        // 'abc' || NULL → NULL
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r3 = b.alloc_reg();

            b.emit_op(Opcode::String8, 0, r1, 0, P4::Str("abc".to_owned()), 0);
            b.emit_op(Opcode::Null, 0, r2, 0, P4::None, 0);
            b.emit_op(Opcode::Concat, r2, r1, r3, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0], vec![SqliteValue::Null]);
    }

    // ── test_vdbe_boolean_logic ─────────────────────────────────────────
    #[test]
    fn test_vdbe_boolean_logic() {
        // TRUE AND FALSE → 0, TRUE OR FALSE → 1, NOT TRUE → 0
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_true = b.alloc_reg();
            let r_false = b.alloc_reg();
            let r_and = b.alloc_reg();
            let r_or = b.alloc_reg();
            let r_not = b.alloc_reg();

            b.emit_op(Opcode::Integer, 1, r_true, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_false, 0, P4::None, 0);
            b.emit_op(Opcode::And, r_true, r_false, r_and, P4::None, 0);
            b.emit_op(Opcode::Or, r_true, r_false, r_or, P4::None, 0);
            b.emit_op(Opcode::Not, r_true, r_not, 0, P4::None, 0);

            b.emit_op(Opcode::ResultRow, r_and, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_or, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_not, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows.len(), 3);
        assert_eq!(rows[0], vec![SqliteValue::Integer(0)]); // T AND F = F
        assert_eq!(rows[1], vec![SqliteValue::Integer(1)]); // T OR F = T
        assert_eq!(rows[2], vec![SqliteValue::Integer(0)]); // NOT T = F
    }

    // ── test_vdbe_three_valued_logic ────────────────────────────────────
    #[test]
    fn test_vdbe_three_valued_logic() {
        // NULL AND FALSE → 0, NULL AND TRUE → NULL
        // NULL OR TRUE → 1, NULL OR FALSE → NULL
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_null = b.alloc_reg();
            let r_true = b.alloc_reg();
            let r_false = b.alloc_reg();
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r3 = b.alloc_reg();
            let r4 = b.alloc_reg();

            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 1, r_true, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_false, 0, P4::None, 0);

            b.emit_op(Opcode::And, r_null, r_false, r1, P4::None, 0); // NULL AND F
            b.emit_op(Opcode::And, r_null, r_true, r2, P4::None, 0); // NULL AND T
            b.emit_op(Opcode::Or, r_null, r_true, r3, P4::None, 0); // NULL OR T
            b.emit_op(Opcode::Or, r_null, r_false, r4, P4::None, 0); // NULL OR F

            b.emit_op(Opcode::ResultRow, r1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r2, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r4, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(0)]); // NULL AND F = F
        assert_eq!(rows[1], vec![SqliteValue::Null]); // NULL AND T = NULL
        assert_eq!(rows[2], vec![SqliteValue::Integer(1)]); // NULL OR T = T
        assert_eq!(rows[3], vec![SqliteValue::Null]); // NULL OR F = NULL
    }

    // ── test_vdbe_gosub_return ──────────────────────────────────────────
    #[test]
    fn test_vdbe_gosub_return() {
        // Use Gosub/Return to call a subroutine that sets r2=99.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_return = b.alloc_reg(); // return address storage
            let r_val = b.alloc_reg(); // output

            // Main: call subroutine, then output r_val.
            let sub_label = b.emit_label();
            b.emit_jump_to_label(Opcode::Gosub, r_return, 0, sub_label, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_val, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);

            // Subroutine: set r_val=99, return.
            b.resolve_label(sub_label);
            b.emit_op(Opcode::Integer, 99, r_val, 0, P4::None, 0);
            b.emit_op(Opcode::Return, r_return, 0, 0, P4::None, 0);

            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0], vec![SqliteValue::Integer(99)]);
    }

    // ── test_vdbe_is_null_comparison ─────────────────────────────────────
    #[test]
    fn test_vdbe_is_null_comparison() {
        // NULL IS NULL → true (using Eq with NULLEQ flag)
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_null = b.alloc_reg();
            let r_out = b.alloc_reg();

            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);

            // Eq with p5=0x80 (SQLITE_NULLEQ): NULL IS NULL → jump
            let is_null_label = b.emit_label();
            // p1=r_null, p3=r_null (compare same register)
            b.emit_jump_to_label(Opcode::Eq, r_null, 0, is_null_label, P4::None, 0x80);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(is_null_label);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);

            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]); // NULL IS NULL = true
    }

    // ── test_vdbe_coroutine ─────────────────────────────────────────────
    #[test]
    fn test_vdbe_coroutine() {
        // Test coroutine: producer yields values 10, 20, 30.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_co = b.alloc_reg(); // coroutine state register
            let r_val = b.alloc_reg(); // value register

            let init_addr = b.emit_op(Opcode::InitCoroutine, r_co, 0, 0, P4::None, 0);

            #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
            let consumer_start = b.current_addr() as i32;
            b.emit_op(Opcode::Yield, r_co, 0, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_val, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Yield, r_co, 0, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_val, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Yield, r_co, 0, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_val, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);

            #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
            let producer_start = b.current_addr() as i32;
            b.emit_op(Opcode::Integer, 10, r_val, 0, P4::None, 0);
            b.emit_op(Opcode::Yield, r_co, 0, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 20, r_val, 0, P4::None, 0);
            b.emit_op(Opcode::Yield, r_co, 0, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 30, r_val, 0, P4::None, 0);
            b.emit_op(Opcode::Yield, r_co, 0, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);

            if let Some(init_op) = b.op_at_mut(init_addr) {
                init_op.p2 = consumer_start;
                init_op.p3 = producer_start;
            }

            b.resolve_label(end);
        });
        assert_eq!(rows.len(), 3);
        assert_eq!(rows[0], vec![SqliteValue::Integer(10)]);
        assert_eq!(rows[1], vec![SqliteValue::Integer(20)]);
        assert_eq!(rows[2], vec![SqliteValue::Integer(30)]);
    }

    // ── test_vdbe_halt_with_error ───────────────────────────────────────
    #[test]
    fn test_vdbe_halt_with_error() {
        let mut b = ProgramBuilder::new();
        b.emit_op(
            Opcode::Halt,
            1,
            0,
            0,
            P4::Str("constraint failed".to_owned()),
            0,
        );
        let prog = b.finish().unwrap();
        let mut engine = VdbeEngine::new(prog.register_count());
        let outcome = engine.execute(&prog).unwrap();
        assert_eq!(
            outcome,
            ExecOutcome::Error {
                code: 1,
                message: "constraint failed".to_owned(),
            }
        );
    }

    // ── test_vdbe_disassemble_and_exec ──────────────────────────────────
    #[test]
    fn test_vdbe_disassemble_and_exec() {
        // Build a program, disassemble it, and verify output.
        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        let r1 = b.alloc_reg();
        let r2 = b.alloc_reg();
        let r3 = b.alloc_reg();
        b.emit_op(Opcode::Integer, 10, r1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 20, r2, 0, P4::None, 0);
        b.emit_op(Opcode::Multiply, r1, r2, r3, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r3, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().unwrap();
        let asm = prog.disassemble();
        assert!(asm.contains("Init"));
        assert!(asm.contains("Integer"));
        assert!(asm.contains("Multiply"));
        assert!(asm.contains("ResultRow"));
        assert!(asm.contains("Halt"));

        let mut engine = VdbeEngine::new(prog.register_count());
        let outcome = engine.execute(&prog).unwrap();
        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(engine.results().len(), 1);
        assert_eq!(
            engine.results()[0].to_vec(),
            vec![SqliteValue::Integer(200)]
        );
    }

    #[test]
    fn test_sorter_opcodes_sort_and_emit_rows() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            let loop_start = b.emit_label();
            let empty = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_value = b.alloc_reg();
            let r_record = b.alloc_reg();
            let r_sorted = b.alloc_reg();

            b.emit_op(Opcode::SorterOpen, 0, 1, 0, P4::None, 0);

            for value in [30, 10, 20] {
                b.emit_op(Opcode::Integer, value, r_value, 0, P4::None, 0);
                b.emit_op(Opcode::MakeRecord, r_value, 1, r_record, P4::None, 0);
                b.emit_op(Opcode::SorterInsert, 0, r_record, 0, P4::None, 0);
            }

            b.emit_jump_to_label(Opcode::SorterSort, 0, 0, empty, P4::None, 0);
            b.resolve_label(loop_start);
            b.emit_op(Opcode::SorterData, 0, r_sorted, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_sorted, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SorterNext, 0, 0, loop_start, P4::None, 0);
            b.resolve_label(empty);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        let decoded: Vec<i64> = rows
            .into_iter()
            .map(|row| decode_record(&row[0]).unwrap()[0].to_integer())
            .collect();
        assert_eq!(decoded, vec![10, 20, 30]);
    }

    #[test]
    fn test_sorter_insert_consumes_make_record_sideband_without_materializing() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        reset_vdbe_test_sideband_materialization_count();

        let before = vdbe_test_sideband_materialization_count_snapshot();
        let rows = run_program(|b| {
            let end = b.emit_label();
            let loop_start = b.emit_label();
            let empty = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_value = b.alloc_reg();
            let r_record = b.alloc_reg();
            let r_sorted = b.alloc_reg();

            b.emit_op(Opcode::SorterOpen, 0, 1, 0, P4::None, 0);

            for value in [3, 1, 2] {
                b.emit_op(Opcode::Integer, value, r_value, 0, P4::None, 0);
                b.emit_op(Opcode::MakeRecord, r_value, 1, r_record, P4::None, 0);
                b.emit_op(Opcode::SorterInsert, 0, r_record, 0, P4::None, 0);
            }

            b.emit_jump_to_label(Opcode::SorterSort, 0, 0, empty, P4::None, 0);
            b.resolve_label(loop_start);
            b.emit_op(Opcode::SorterData, 0, r_sorted, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_sorted, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SorterNext, 0, 0, loop_start, P4::None, 0);
            b.resolve_label(empty);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        let after = vdbe_test_sideband_materialization_count_snapshot();

        assert_eq!(
            after - before,
            0,
            "SorterInsert should move MakeRecord sideband bytes directly into the sorter"
        );
        let decoded: Vec<i64> = rows
            .into_iter()
            .map(|row| decode_record(&row[0]).unwrap()[0].to_integer())
            .collect();
        assert_eq!(decoded, vec![1, 2, 3]);
    }

    #[test]
    fn test_sorter_compare_jumps_on_key_difference() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            let diff = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_value = b.alloc_reg();
            let r_record = b.alloc_reg();
            let r_probe = b.alloc_reg();
            let r_probe_record = b.alloc_reg();
            let r_out = b.alloc_reg();

            b.emit_op(Opcode::SorterOpen, 0, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 10, r_value, 0, P4::None, 0);
            b.emit_op(Opcode::MakeRecord, r_value, 1, r_record, P4::None, 0);
            b.emit_op(Opcode::SorterInsert, 0, r_record, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SorterSort, 0, 0, diff, P4::None, 0);

            b.emit_op(Opcode::Integer, 20, r_probe, 0, P4::None, 0);
            b.emit_op(Opcode::MakeRecord, r_probe, 1, r_probe_record, P4::None, 0);
            b.emit_jump_to_label(Opcode::SorterCompare, 0, r_probe_record, diff, P4::None, 0);

            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);

            b.resolve_label(diff);
            b.emit_op(Opcode::Integer, 2, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Integer(2)]]);
    }

    #[test]
    fn test_sorter_column_reads_decode_non_key_payload_columns() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            let loop_start = b.emit_label();
            let empty = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_key = b.alloc_reg();
            let r_payload = b.alloc_reg();
            let r_record = b.alloc_reg();
            let r_out = b.alloc_reg();

            b.emit_op(Opcode::SorterOpen, 0, 1, 0, P4::None, 0);

            for (key, payload) in [(30, 300), (10, 100), (20, 200)] {
                b.emit_op(Opcode::Integer, key, r_key, 0, P4::None, 0);
                b.emit_op(Opcode::Integer, payload, r_payload, 0, P4::None, 0);
                b.emit_op(Opcode::MakeRecord, r_key, 2, r_record, P4::None, 0);
                b.emit_op(Opcode::SorterInsert, 0, r_record, 0, P4::None, 0);
            }

            b.emit_jump_to_label(Opcode::SorterSort, 0, 0, empty, P4::None, 0);
            b.resolve_label(loop_start);
            b.emit_op(Opcode::Column, 0, 1, r_out, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SorterNext, 0, 0, loop_start, P4::None, 0);
            b.resolve_label(empty);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Integer(100)],
                vec![SqliteValue::Integer(200)],
                vec![SqliteValue::Integer(300)],
            ]
        );
    }

    #[test]
    fn test_reset_sorter_clears_entries() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            let empty = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_value = b.alloc_reg();
            let r_record = b.alloc_reg();
            let r_out = b.alloc_reg();

            b.emit_op(Opcode::SorterOpen, 0, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 7, r_value, 0, P4::None, 0);
            b.emit_op(Opcode::MakeRecord, r_value, 1, r_record, P4::None, 0);
            b.emit_op(Opcode::SorterInsert, 0, r_record, 0, P4::None, 0);
            b.emit_op(Opcode::ResetSorter, 0, 0, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SorterSort, 0, 0, empty, P4::None, 0);

            // If ResetSorter failed, this row would be emitted.
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.resolve_label(empty);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert!(rows.is_empty());
    }

    // ── Codegen → Engine Integration Tests ──────────────────────────────

    mod codegen_integration {
        use super::*;
        use crate::codegen::{
            CodegenContext, ColumnInfo, TableSchema, codegen_delete, codegen_insert,
            codegen_select, codegen_update,
        };
        use fsqlite_ast::{
            Assignment, AssignmentTarget, BinaryOp as AstBinaryOp, ColumnRef, DeleteStatement,
            Distinctness, Expr, FromClause, InsertSource, InsertStatement, Literal,
            PlaceholderType, QualifiedName, QualifiedTableRef, ResultColumn, SelectBody,
            SelectCore, SelectStatement, Span, TableOrSubquery, UpdateStatement,
        };

        fn test_schema() -> Vec<TableSchema> {
            vec![TableSchema {
                name: "t".to_owned(),
                root_page: 2,
                columns: vec![
                    ColumnInfo {
                        name: "a".to_owned(),
                        affinity: 'd',
                        is_ipk: false,
                        type_name: None,
                        notnull: false,
                        unique: false,
                        default_value: None,
                        strict_type: None,
                        generated_expr: None,
                        generated_stored: None,
                        collation: None,
                    },
                    ColumnInfo {
                        name: "b".to_owned(),
                        affinity: 'C',
                        is_ipk: false,
                        type_name: None,
                        notnull: false,
                        unique: false,
                        default_value: None,
                        strict_type: None,
                        generated_expr: None,
                        generated_stored: None,
                        collation: None,
                    },
                ],
                indexes: vec![],
                strict: false,
                without_rowid: false,
                primary_key_constraints: Vec::new(),
                foreign_keys: Vec::new(),
                check_constraints: Vec::new(),
            }]
        }

        fn from_table(name: &str) -> FromClause {
            FromClause {
                source: TableOrSubquery::Table {
                    name: QualifiedName {
                        schema: None,
                        name: name.to_owned(),
                    },
                    alias: None,
                    index_hint: None,
                    time_travel: None,
                },
                joins: Vec::new(),
            }
        }

        fn span() -> Span {
            Span { start: 0, end: 0 }
        }

        /// Verify codegen_insert produces a program that executes without panic.
        #[test]
        fn test_codegen_insert_executes() {
            let schema = test_schema();
            let ctx = CodegenContext::default();

            let stmt = InsertStatement {
                with: None,
                or_conflict: None,
                table: QualifiedName {
                    schema: None,
                    name: "t".to_owned(),
                },
                alias: None,
                columns: vec![],
                source: InsertSource::Values(vec![vec![
                    Expr::Literal(Literal::Integer(42), span()),
                    Expr::Literal(Literal::String("hello".to_owned()), span()),
                ]]),
                upsert: vec![],
                returning: vec![],
            };

            let mut b = ProgramBuilder::new();
            codegen_insert(&mut b, &stmt, &schema, &ctx).expect("codegen should succeed");
            let prog = b.finish().expect("program should build");

            let mut engine = VdbeEngine::new(prog.register_count());
            engine.set_reject_mem_fallback(false);
            let outcome = engine.execute(&prog).expect("execution should succeed");
            assert_eq!(outcome, ExecOutcome::Done);
        }

        /// Verify codegen_select (full scan) produces a program that executes.
        #[test]
        fn test_codegen_select_full_scan_executes() {
            let schema = test_schema();
            let ctx = CodegenContext::default();

            let stmt = SelectStatement {
                with: None,
                body: SelectBody {
                    select: SelectCore::Select {
                        distinct: Distinctness::All,
                        columns: vec![ResultColumn::Star],
                        from: Some(from_table("t")),
                        where_clause: None,
                        group_by: vec![],
                        having: None,
                        windows: vec![],
                    },
                    compounds: vec![],
                },
                order_by: vec![],
                limit: None,
            };

            let mut b = ProgramBuilder::new();
            codegen_select(&mut b, &stmt, &schema, &ctx).expect("codegen should succeed");
            let prog = b.finish().expect("program should build");

            // Engine should execute without panic (cursor ops are stubbed).
            let mut engine = VdbeEngine::new(prog.register_count());
            engine.set_reject_mem_fallback(false);
            let outcome = engine.execute(&prog).expect("execution should succeed");
            assert_eq!(outcome, ExecOutcome::Done);
        }

        /// Verify `OpenRead` can route through the storage-backed cursor path.
        #[test]
        fn test_openread_uses_storage_cursor_backend_when_enabled() {
            let mut b = ProgramBuilder::new();
            b.emit_op(Opcode::OpenRead, 0, 2, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            let prog = b.finish().expect("program should build");

            let mut db = MemDatabase::new();
            let root = db.create_table(1);
            assert_eq!(root, 2);
            if let Some(table) = db.get_table_mut(root) {
                table.insert(1, vec![SqliteValue::Integer(99)]);
            }

            let mut engine = VdbeEngine::new(prog.register_count());
            engine.enable_storage_read_cursors(true);
            engine.set_database(db);
            engine.set_reject_mem_fallback(false);
            let outcome = engine.execute(&prog).expect("execution should succeed");
            assert_eq!(outcome, ExecOutcome::Done);
            assert!(engine.storage_cursors.contains_key(&0));
            assert!(!engine.cursors.contains_key(&0));
        }

        /// Verify codegen_update produces a program that executes.
        #[test]
        fn test_codegen_update_executes() {
            let schema = test_schema();
            let ctx = CodegenContext::default();

            let stmt = UpdateStatement {
                with: None,
                or_conflict: None,
                table: QualifiedTableRef {
                    name: QualifiedName {
                        schema: None,
                        name: "t".to_owned(),
                    },
                    alias: None,
                    index_hint: None,
                    time_travel: None,
                },
                assignments: vec![Assignment {
                    target: AssignmentTarget::Column("b".to_owned()),
                    value: Expr::Placeholder(PlaceholderType::Numbered(1), span()),
                }],
                from: None,
                where_clause: Some(Expr::BinaryOp {
                    left: Box::new(Expr::Column(
                        ColumnRef {
                            table: None,
                            column: "rowid".into(),
                        },
                        span(),
                    )),
                    op: AstBinaryOp::Eq,
                    right: Box::new(Expr::Placeholder(PlaceholderType::Numbered(2), span())),
                    span: span(),
                }),
                returning: vec![],
                order_by: vec![],
                limit: None,
            };

            let mut b = ProgramBuilder::new();
            codegen_update(&mut b, &stmt, &schema, &ctx).expect("codegen should succeed");
            let prog = b.finish().expect("program should build");

            let mut engine = VdbeEngine::new(prog.register_count());
            engine.set_reject_mem_fallback(false);
            let outcome = engine.execute(&prog).expect("execution should succeed");
            assert_eq!(outcome, ExecOutcome::Done);
        }

        /// Verify codegen_delete produces a program that executes.
        #[test]
        fn test_codegen_delete_executes() {
            let schema = test_schema();
            let ctx = CodegenContext::default();

            let stmt = DeleteStatement {
                with: None,
                table: QualifiedTableRef {
                    name: QualifiedName {
                        schema: None,
                        name: "t".to_owned(),
                    },
                    alias: None,
                    index_hint: None,
                    time_travel: None,
                },
                where_clause: Some(Expr::BinaryOp {
                    left: Box::new(Expr::Column(
                        ColumnRef {
                            table: None,
                            column: "rowid".into(),
                        },
                        span(),
                    )),
                    op: AstBinaryOp::Eq,
                    right: Box::new(Expr::Placeholder(PlaceholderType::Numbered(1), span())),
                    span: span(),
                }),
                returning: vec![],
                order_by: vec![],
                limit: None,
            };

            let mut b = ProgramBuilder::new();
            codegen_delete(&mut b, &stmt, &schema, &ctx).expect("codegen should succeed");
            let prog = b.finish().expect("program should build");

            let mut engine = VdbeEngine::new(prog.register_count());
            engine.set_reject_mem_fallback(false);
            let outcome = engine.execute(&prog).expect("execution should succeed");
            assert_eq!(outcome, ExecOutcome::Done);
        }

        /// Verify codegen_insert with RETURNING produces a ResultRow.
        #[test]
        fn test_codegen_insert_returning_produces_result() {
            let schema = test_schema();
            let ctx = CodegenContext::default();

            let stmt = InsertStatement {
                with: None,
                or_conflict: None,
                table: QualifiedName {
                    schema: None,
                    name: "t".to_owned(),
                },
                alias: None,
                columns: vec![],
                source: InsertSource::Values(vec![vec![
                    Expr::Literal(Literal::Integer(7), span()),
                    Expr::Literal(Literal::String("world".to_owned()), span()),
                ]]),
                upsert: vec![],
                returning: vec![ResultColumn::Star],
            };

            let mut b = ProgramBuilder::new();
            codegen_insert(&mut b, &stmt, &schema, &ctx).expect("codegen should succeed");
            let prog = b.finish().expect("program should build");

            // Provide a MemDatabase so Insert stores the row and SeekRowid
            // (used by emit_returning) can find it.
            let mut db = MemDatabase::new();
            let root = db.create_table(2);
            assert_eq!(root, 2);

            let mut engine = VdbeEngine::new(prog.register_count());
            engine.set_database(db);
            engine.set_reject_mem_fallback(false);
            let outcome = engine.execute(&prog).expect("execution should succeed");
            assert_eq!(outcome, ExecOutcome::Done);
            // RETURNING * emits a ResultRow with all columns.
            assert_eq!(engine.results().len(), 1);
        }

        /// Verify INSERT with literal values emits the correct value registers.
        #[test]
        fn test_codegen_insert_literal_values_disassemble() {
            let schema = test_schema();
            let ctx = CodegenContext::default();

            let stmt = InsertStatement {
                with: None,
                or_conflict: None,
                table: QualifiedName {
                    schema: None,
                    name: "t".to_owned(),
                },
                alias: None,
                columns: vec![],
                source: InsertSource::Values(vec![vec![
                    Expr::Literal(Literal::Integer(99), span()),
                    Expr::Literal(Literal::String("test".to_owned()), span()),
                ]]),
                upsert: vec![],
                returning: vec![],
            };

            let mut b = ProgramBuilder::new();
            codegen_insert(&mut b, &stmt, &schema, &ctx).expect("codegen should succeed");
            let prog = b.finish().expect("program should build");

            let asm = prog.disassemble();
            let insert = prog
                .ops()
                .iter()
                .find(|op| op.opcode == Opcode::Insert)
                .expect("program should contain Insert");
            assert!(asm.contains("Init"), "should have Init opcode");
            assert!(asm.contains("OpenWrite"), "should have OpenWrite opcode");
            assert!(asm.contains("NewRowid"), "should have NewRowid opcode");
            assert!(
                asm.contains("Integer"),
                "should have Integer opcode for literal 99"
            );
            assert!(
                asm.contains("String8"),
                "should have String8 opcode for literal 'test'"
            );
            assert!(
                !prog.ops().iter().any(|op| op.opcode == Opcode::Blob),
                "preformatted table records should avoid a standalone Blob opcode"
            );
            assert!(
                !asm.contains("MakeRecord"),
                "literal-only INSERT should not execute MakeRecord for the table record"
            );
            assert!(
                matches!(&insert.p4, P4::Blob(record) if !record.is_empty()),
                "Insert should carry the preformatted table record directly in P4"
            );
            assert!(asm.contains("Insert"), "should have Insert opcode");
            assert!(asm.contains("Halt"), "should have Halt opcode");
        }

        #[test]
        fn test_codegen_insert_literal_values_execute_without_make_record_opcode() {
            let schema = test_schema();
            let ctx = CodegenContext::default();
            let stmt = InsertStatement {
                with: None,
                or_conflict: None,
                table: QualifiedName {
                    schema: None,
                    name: "t".to_owned(),
                },
                alias: None,
                columns: vec![],
                source: InsertSource::Values(vec![vec![
                    Expr::Literal(Literal::Integer(99), span()),
                    Expr::Literal(Literal::String("test".to_owned()), span()),
                ]]),
                upsert: vec![],
                returning: vec![ResultColumn::Star],
            };

            let mut b = ProgramBuilder::new();
            codegen_insert(&mut b, &stmt, &schema, &ctx).expect("codegen should succeed");
            let prog = b.finish().expect("program should build");
            assert!(
                !prog.ops().iter().any(|op| op.opcode == Opcode::MakeRecord),
                "preformatted INSERT should skip table-row MakeRecord execution"
            );

            let mut db = MemDatabase::new();
            let _root = db.create_table(2);

            let mut engine = VdbeEngine::new(prog.register_count());
            engine.enable_storage_cursors(true);
            engine.set_database(db);
            engine.set_reject_mem_fallback(false);
            let outcome = engine.execute(&prog).expect("execution should succeed");
            assert_eq!(outcome, ExecOutcome::Done);

            let rows: Vec<_> = engine
                .take_results()
                .into_iter()
                .map(|row| row.into_vec())
                .collect();

            assert_eq!(
                rows,
                vec![vec![
                    SqliteValue::Integer(99),
                    SqliteValue::Text("test".into())
                ]]
            );
        }

        #[test]
        fn test_codegen_insert_large_literal_blob_round_trips_via_preformatted_insert() {
            let schema = vec![TableSchema {
                name: "t".to_owned(),
                root_page: 2,
                columns: vec![
                    ColumnInfo {
                        name: "a".to_owned(),
                        affinity: 'd',
                        is_ipk: false,
                        type_name: None,
                        notnull: false,
                        unique: false,
                        default_value: None,
                        strict_type: None,
                        generated_expr: None,
                        generated_stored: None,
                        collation: None,
                    },
                    ColumnInfo {
                        name: "b".to_owned(),
                        affinity: 'A',
                        is_ipk: false,
                        type_name: None,
                        notnull: false,
                        unique: false,
                        default_value: None,
                        strict_type: None,
                        generated_expr: None,
                        generated_stored: None,
                        collation: None,
                    },
                ],
                indexes: vec![],
                strict: false,
                without_rowid: false,
                primary_key_constraints: Vec::new(),
                foreign_keys: Vec::new(),
                check_constraints: Vec::new(),
            }];
            let large_blob = vec![0xAB; 5_000];
            let stmt = InsertStatement {
                with: None,
                or_conflict: None,
                table: QualifiedName {
                    schema: None,
                    name: "t".to_owned(),
                },
                alias: None,
                columns: vec![],
                source: InsertSource::Values(vec![vec![
                    Expr::Literal(Literal::Integer(7), span()),
                    Expr::Literal(Literal::Blob(large_blob.clone()), span()),
                ]]),
                upsert: vec![],
                returning: vec![ResultColumn::Star],
            };
            let ctx = CodegenContext::default();

            let mut b = ProgramBuilder::new();
            codegen_insert(&mut b, &stmt, &schema, &ctx).expect("codegen should succeed");
            let prog = b.finish().expect("program should build");

            let insert = prog
                .ops()
                .iter()
                .find(|op| op.opcode == Opcode::Insert)
                .expect("program should contain Insert");
            assert!(
                !prog
                    .ops()
                    .iter()
                    .any(|op| op.opcode == Opcode::Blob && op.p2 == insert.p2),
                "preformatted literal rows should not materialize the table record into a Blob register"
            );
            assert!(
                matches!(&insert.p4, P4::Blob(record) if record.len() > 4_096),
                "Insert should carry the full preformatted record, including large overflow-bound payloads"
            );

            let mut db = MemDatabase::new();
            let root = db.create_table(2);
            assert_eq!(root, 2);

            let mut engine = VdbeEngine::new(prog.register_count());
            engine.enable_storage_cursors(true);
            engine.set_database(db);
            engine.set_reject_mem_fallback(false);
            let outcome = engine.execute(&prog).expect("execution should succeed");
            assert_eq!(outcome, ExecOutcome::Done);

            let rows: Vec<_> = engine
                .take_results()
                .into_iter()
                .map(|row| row.into_vec())
                .collect();
            assert_eq!(
                rows,
                vec![vec![
                    SqliteValue::Integer(7),
                    SqliteValue::Blob(large_blob.into()),
                ]],
                "large literal blob should round-trip through storage even when the record is preformatted on Insert"
            );
        }

        #[test]
        fn test_codegen_insert_current_timestamp_uses_runtime_make_record() {
            let schema = test_schema();
            let ctx = CodegenContext::default();
            let stmt = InsertStatement {
                with: None,
                or_conflict: None,
                table: QualifiedName {
                    schema: None,
                    name: "t".to_owned(),
                },
                alias: None,
                columns: vec![],
                source: InsertSource::Values(vec![vec![
                    Expr::Literal(Literal::CurrentTimestamp, span()),
                    Expr::Literal(Literal::String("runtime".to_owned()), span()),
                ]]),
                upsert: vec![],
                returning: vec![],
            };

            let mut b = ProgramBuilder::new();
            codegen_insert(&mut b, &stmt, &schema, &ctx).expect("codegen should succeed");
            let prog = b.finish().expect("program should build");
            let insert = prog
                .ops()
                .iter()
                .find(|op| op.opcode == Opcode::Insert)
                .expect("program should contain Insert");

            assert!(
                prog.ops().iter().any(|op| op.opcode == Opcode::MakeRecord),
                "volatile CURRENT_TIMESTAMP values must stay on the runtime record assembly path"
            );
            assert!(
                !matches!(&insert.p4, P4::Blob(_)),
                "volatile CURRENT_TIMESTAMP values must not be captured in a preformatted Insert payload"
            );
        }

        /// Verify emit_expr handles arithmetic BinaryOp in INSERT values.
        #[test]
        fn test_codegen_insert_arithmetic_expr() {
            let schema = test_schema();
            let ctx = CodegenContext::default();

            // INSERT INTO t VALUES (2 + 3, 'hi')
            let stmt = InsertStatement {
                with: None,
                or_conflict: None,
                table: QualifiedName {
                    schema: None,
                    name: "t".to_owned(),
                },
                alias: None,
                columns: vec![],
                source: InsertSource::Values(vec![vec![
                    Expr::BinaryOp {
                        left: Box::new(Expr::Literal(Literal::Integer(2), span())),
                        op: AstBinaryOp::Add,
                        right: Box::new(Expr::Literal(Literal::Integer(3), span())),
                        span: span(),
                    },
                    Expr::Literal(Literal::String("hi".to_owned()), span()),
                ]]),
                upsert: vec![],
                returning: vec![],
            };

            let mut b = ProgramBuilder::new();
            codegen_insert(&mut b, &stmt, &schema, &ctx).expect("codegen should succeed");
            let prog = b.finish().expect("program should build");

            let asm = prog.disassemble();
            assert!(asm.contains("Add"), "should have Add opcode for 2+3");
            assert!(asm.contains("Integer"), "should have Integer opcodes");

            let mut engine = VdbeEngine::new(prog.register_count());
            engine.set_reject_mem_fallback(false);
            let outcome = engine.execute(&prog).expect("execution should succeed");
            assert_eq!(outcome, ExecOutcome::Done);
        }

        /// Verify emit_expr handles UnaryOp (negation) in INSERT values.
        #[test]
        fn test_codegen_insert_negation_expr() {
            use fsqlite_ast::UnaryOp as AstUnaryOp;

            let schema = test_schema();
            let ctx = CodegenContext::default();

            // INSERT INTO t VALUES (-42, 'neg')
            let stmt = InsertStatement {
                with: None,
                or_conflict: None,
                table: QualifiedName {
                    schema: None,
                    name: "t".to_owned(),
                },
                alias: None,
                columns: vec![],
                source: InsertSource::Values(vec![vec![
                    Expr::UnaryOp {
                        op: AstUnaryOp::Negate,
                        expr: Box::new(Expr::Literal(Literal::Integer(42), span())),
                        span: span(),
                    },
                    Expr::Literal(Literal::String("neg".to_owned()), span()),
                ]]),
                upsert: vec![],
                returning: vec![],
            };

            let mut b = ProgramBuilder::new();
            codegen_insert(&mut b, &stmt, &schema, &ctx).expect("codegen should succeed");
            let prog = b.finish().expect("program should build");

            let asm = prog.disassemble();
            assert!(asm.contains("Multiply"), "negation emits Multiply by -1");

            let mut engine = VdbeEngine::new(prog.register_count());
            engine.set_reject_mem_fallback(false);
            let outcome = engine.execute(&prog).expect("execution should succeed");
            assert_eq!(outcome, ExecOutcome::Done);
        }

        /// Verify emit_expr handles CASE expression in INSERT values.
        #[test]
        fn test_codegen_insert_case_expr() {
            let schema = test_schema();
            let ctx = CodegenContext::default();

            // INSERT INTO t VALUES (CASE WHEN TRUE THEN 10 ELSE 20 END, 'case')
            let stmt = InsertStatement {
                with: None,
                or_conflict: None,
                table: QualifiedName {
                    schema: None,
                    name: "t".to_owned(),
                },
                alias: None,
                columns: vec![],
                source: InsertSource::Values(vec![vec![
                    Expr::Case {
                        operand: None,
                        whens: vec![(
                            Expr::Literal(Literal::True, span()),
                            Expr::Literal(Literal::Integer(10), span()),
                        )],
                        else_expr: Some(Box::new(Expr::Literal(Literal::Integer(20), span()))),
                        span: span(),
                    },
                    Expr::Literal(Literal::String("case".to_owned()), span()),
                ]]),
                upsert: vec![],
                returning: vec![],
            };

            let mut b = ProgramBuilder::new();
            codegen_insert(&mut b, &stmt, &schema, &ctx).expect("codegen should succeed");
            let prog = b.finish().expect("program should build");

            let asm = prog.disassemble();
            assert!(asm.contains("IfNot"), "searched CASE emits IfNot");
            assert!(asm.contains("Goto"), "CASE branches with Goto");

            let mut engine = VdbeEngine::new(prog.register_count());
            engine.set_reject_mem_fallback(false);
            let outcome = engine.execute(&prog).expect("execution should succeed");
            assert_eq!(outcome, ExecOutcome::Done);
        }

        /// Verify emit_expr handles comparison expression producing 0/1 result.
        #[test]
        fn test_codegen_insert_comparison_expr() {
            let schema = test_schema();
            let ctx = CodegenContext::default();

            // INSERT INTO t VALUES (3 > 2, 'cmp') — should produce integer 1
            let stmt = InsertStatement {
                with: None,
                or_conflict: None,
                table: QualifiedName {
                    schema: None,
                    name: "t".to_owned(),
                },
                alias: None,
                columns: vec![],
                source: InsertSource::Values(vec![vec![
                    Expr::BinaryOp {
                        left: Box::new(Expr::Literal(Literal::Integer(3), span())),
                        op: AstBinaryOp::Gt,
                        right: Box::new(Expr::Literal(Literal::Integer(2), span())),
                        span: span(),
                    },
                    Expr::Literal(Literal::String("cmp".to_owned()), span()),
                ]]),
                upsert: vec![],
                returning: vec![],
            };

            let mut b = ProgramBuilder::new();
            codegen_insert(&mut b, &stmt, &schema, &ctx).expect("codegen should succeed");
            let prog = b.finish().expect("program should build");

            let asm = prog.disassemble();
            assert!(asm.contains("Gt"), "comparison emits Gt opcode");

            let mut engine = VdbeEngine::new(prog.register_count());
            engine.set_reject_mem_fallback(false);
            let outcome = engine.execute(&prog).expect("execution should succeed");
            assert_eq!(outcome, ExecOutcome::Done);
        }
    }

    // ===================================================================
    // bd-202x §16 Phase 4: Comprehensive VDBE opcode unit tests
    // ===================================================================

    // ── Constants & Register Operations ────────────────────────────────

    #[test]
    fn test_int64_large_value() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::Int64, 0, r, 0, P4::Int64(i64::MAX), 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(i64::MAX)]);
    }

    #[test]
    fn test_int64_negative() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::Int64, 0, r, 0, P4::Int64(-999_999_999_999), 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(-999_999_999_999)]);
    }

    #[test]
    fn test_real_constant() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::Real, 0, r, 0, P4::Real(std::f64::consts::PI), 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Float(std::f64::consts::PI)]);
    }

    #[test]
    fn test_real_negative_zero() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::Real, 0, r, 0, P4::Real(0.0), 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Float(0.0)]);
    }

    #[test]
    fn test_string_opcode() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::String, 5, r, 0, P4::Str("hello".to_owned()), 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Text("hello".into())]);
    }

    #[test]
    fn test_blob_constant() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(
                Opcode::Blob,
                0,
                r,
                0,
                P4::Blob(vec![0xDE, 0xAD, 0xBE, 0xEF]),
                0,
            );
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(
            rows[0],
            vec![SqliteValue::Blob(vec![0xDE, 0xAD, 0xBE, 0xEF].into())]
        );
    }

    #[test]
    fn test_null_range() {
        // Null with p3=2: set registers p2, p2+1, p2+2 to NULL.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r3 = b.alloc_reg();
            // Pre-populate with integers
            b.emit_op(Opcode::Integer, 1, r1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 2, r2, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 3, r3, 0, P4::None, 0);
            // Null range: p2=r1, p3=r3 → set r1..=r3 to NULL (absolute end register).
            b.emit_op(Opcode::Null, 0, r1, r3, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r1, 3, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(
            rows[0],
            vec![SqliteValue::Null, SqliteValue::Null, SqliteValue::Null]
        );
    }

    #[test]
    fn test_soft_null() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::Integer, 42, r, 0, P4::None, 0);
            b.emit_op(Opcode::SoftNull, r, 0, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Null]);
    }

    #[test]
    fn test_move_registers() {
        // Move nullifies source and copies to destination.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let src = b.alloc_reg();
            let dst = b.alloc_reg();
            b.emit_op(Opcode::Integer, 77, src, 0, P4::None, 0);
            // Move 1 register from src to dst
            b.emit_op(Opcode::Move, src, dst, 1, P4::None, 0);
            // dst should be 77, src should be NULL
            b.emit_op(Opcode::ResultRow, dst, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, src, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(77)]);
        assert_eq!(rows[1], vec![SqliteValue::Null]);
    }

    #[test]
    fn test_move_register_range_preserves_forward_overlap() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r3 = b.alloc_reg();
            b.emit_op(Opcode::Integer, 11, r1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 22, r2, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 33, r3, 0, P4::None, 0);
            b.emit_op(Opcode::Move, r1, r2, 2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r1, 3, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(
            rows[0],
            vec![
                SqliteValue::Null,
                SqliteValue::Integer(11),
                SqliteValue::Integer(22),
            ]
        );
    }

    #[test]
    fn test_move_register_range_preserves_backward_overlap() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r3 = b.alloc_reg();
            b.emit_op(Opcode::Integer, 11, r1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 22, r2, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 33, r3, 0, P4::None, 0);
            b.emit_op(Opcode::Move, r2, r1, 2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r1, 3, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(
            rows[0],
            vec![
                SqliteValue::Integer(22),
                SqliteValue::Integer(33),
                SqliteValue::Null,
            ]
        );
    }

    #[test]
    fn test_copy_register() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let src = b.alloc_reg();
            let dst = b.alloc_reg();
            b.emit_op(Opcode::String8, 0, src, 0, P4::Str("copy_me".to_owned()), 0);
            b.emit_op(Opcode::Copy, src, dst, 0, P4::None, 0);
            // Both should be the same value
            b.emit_op(Opcode::ResultRow, src, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, dst, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Text("copy_me".into())]);
        assert_eq!(rows[1], vec![SqliteValue::Text("copy_me".into())]);
    }

    #[test]
    fn test_scopy_register_via_hot_path() {
        // SCopy (shallow copy) lives in `try_execute_hot_opcode`: this test
        // pins the hot-path arm to the same single-register `clone + write`
        // semantics as the main-match arm. Uses a heap-carrying Text so a
        // subsequent mutation of the destination can't alias the source.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let src = b.alloc_reg();
            let dst = b.alloc_reg();
            b.emit_op(
                Opcode::String8,
                0,
                src,
                0,
                P4::Str("shallow_me".to_owned()),
                0,
            );
            b.emit_op(Opcode::SCopy, src, dst, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, src, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, dst, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Text("shallow_me".into())]);
        assert_eq!(rows[1], vec![SqliteValue::Text("shallow_me".into())]);
    }

    #[test]
    fn test_string8_via_hot_path() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            b.emit_op(
                Opcode::String8,
                0,
                r1,
                0,
                P4::Str("hello_hot".to_owned()),
                0,
            );
            b.emit_op(Opcode::String8, 0, r2, 0, P4::Str(String::new()), 0);
            b.emit_op(Opcode::ResultRow, r1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r2, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Text("hello_hot".into())]);
        assert_eq!(rows[1], vec![SqliteValue::Text("".into())]);
    }

    #[test]
    fn test_string8_hot_path_reuses_existing_text_buffer() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(
                Opcode::String8,
                0,
                r,
                0,
                P4::Str("first_value".to_owned()),
                0,
            );
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::String8, 0, r, 0, P4::Str("second".to_owned()), 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Text("first_value".into())]);
        assert_eq!(rows[1], vec![SqliteValue::Text("second".into())]);
    }

    #[test]
    fn test_intcopy_coerces() {
        // IntCopy converts value to integer.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let src = b.alloc_reg();
            let dst = b.alloc_reg();
            b.emit_op(Opcode::Real, 0, src, 0, P4::Real(3.7), 0);
            b.emit_op(Opcode::IntCopy, src, dst, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, dst, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(3)]);
    }

    // ── Arithmetic Edge Cases ──────────────────────────────────────────

    #[test]
    fn test_subtract_integers() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r3 = b.alloc_reg();
            b.emit_op(Opcode::Integer, 10, r1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 3, r2, 0, P4::None, 0);
            // p3 = p2 - p1 → r3 = r1 - r2 if p2=r1, p1=r2 → 10 - 3 = 7
            b.emit_op(Opcode::Subtract, r2, r1, r3, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(7)]);
    }

    #[test]
    fn test_multiply_large() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r3 = b.alloc_reg();
            b.emit_op(Opcode::Integer, 100, r1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 200, r2, 0, P4::None, 0);
            b.emit_op(Opcode::Multiply, r1, r2, r3, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(20_000)]);
    }

    #[test]
    fn test_integer_division_truncates() {
        // 7 / 2 = 3 (integer division truncates)
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_divisor = b.alloc_reg();
            let r_dividend = b.alloc_reg();
            let r_result = b.alloc_reg();
            b.emit_op(Opcode::Integer, 2, r_divisor, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 7, r_dividend, 0, P4::None, 0);
            // p3 = p2 / p1 → r_result = r_dividend / r_divisor
            b.emit_op(Opcode::Divide, r_divisor, r_dividend, r_result, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_result, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(3)]);
    }

    #[test]
    fn test_remainder_integers() {
        // 7 % 3 = 1
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_divisor = b.alloc_reg();
            let r_dividend = b.alloc_reg();
            let r_result = b.alloc_reg();
            b.emit_op(Opcode::Integer, 3, r_divisor, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 7, r_dividend, 0, P4::None, 0);
            b.emit_op(
                Opcode::Remainder,
                r_divisor,
                r_dividend,
                r_result,
                P4::None,
                0,
            );
            b.emit_op(Opcode::ResultRow, r_result, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_remainder_by_zero() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_zero = b.alloc_reg();
            let r_val = b.alloc_reg();
            let r_result = b.alloc_reg();
            b.emit_op(Opcode::Integer, 0, r_zero, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 10, r_val, 0, P4::None, 0);
            b.emit_op(Opcode::Remainder, r_zero, r_val, r_result, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_result, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Null]);
    }

    #[test]
    fn test_divide_text_prefix_uses_integer_path() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_divisor = b.alloc_reg();
            let r_dividend = b.alloc_reg();
            let r_result = b.alloc_reg();
            b.emit_op(Opcode::Integer, 2, r_divisor, 0, P4::None, 0);
            b.emit_op(
                Opcode::String8,
                0,
                r_dividend,
                0,
                P4::Str("123abc".to_owned()),
                0,
            );
            b.emit_op(Opcode::Divide, r_divisor, r_dividend, r_result, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_result, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(61)]);
    }

    #[test]
    fn test_remainder_blob_prefix_uses_integer_path() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_divisor = b.alloc_reg();
            let r_dividend = b.alloc_reg();
            let r_result = b.alloc_reg();
            b.emit_op(Opcode::Integer, 2, r_divisor, 0, P4::None, 0);
            b.emit_op(
                Opcode::Blob,
                4,
                r_dividend,
                0,
                P4::Blob(b"123a".to_vec()),
                0,
            );
            b.emit_op(
                Opcode::Remainder,
                r_divisor,
                r_dividend,
                r_result,
                P4::None,
                0,
            );
            b.emit_op(Opcode::ResultRow, r_result, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_null_arithmetic_propagation() {
        // NULL + 1, NULL * 5, NULL - 3 should all be NULL.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_null = b.alloc_reg();
            let r_one = b.alloc_reg();
            let r_add = b.alloc_reg();
            let r_mul = b.alloc_reg();
            let r_sub = b.alloc_reg();
            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 5, r_one, 0, P4::None, 0);
            b.emit_op(Opcode::Add, r_null, r_one, r_add, P4::None, 0);
            b.emit_op(Opcode::Multiply, r_null, r_one, r_mul, P4::None, 0);
            b.emit_op(Opcode::Subtract, r_null, r_one, r_sub, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_add, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_mul, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_sub, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Null]);
        assert_eq!(rows[1], vec![SqliteValue::Null]);
        assert_eq!(rows[2], vec![SqliteValue::Null]);
    }

    #[test]
    fn test_add_imm() {
        // AddImm: register p1 += p2
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::Integer, 100, r, 0, P4::None, 0);
            b.emit_op(Opcode::AddImm, r, 50, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(150)]);
    }

    #[test]
    fn test_add_imm_negative() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::Integer, 100, r, 0, P4::None, 0);
            b.emit_op(Opcode::AddImm, r, -30, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(70)]);
    }

    // ── Bitwise Operations ─────────────────────────────────────────────

    #[test]
    fn test_bit_and() {
        // 0xFF & 0x0F = 0x0F (15)
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r3 = b.alloc_reg();
            b.emit_op(Opcode::Integer, 0xFF, r1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0x0F, r2, 0, P4::None, 0);
            b.emit_op(Opcode::BitAnd, r1, r2, r3, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(0x0F)]);
    }

    #[test]
    fn test_bit_or() {
        // 0xF0 | 0x0F = 0xFF (255)
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r3 = b.alloc_reg();
            b.emit_op(Opcode::Integer, 0xF0, r1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0x0F, r2, 0, P4::None, 0);
            b.emit_op(Opcode::BitOr, r1, r2, r3, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(0xFF)]);
    }

    #[test]
    fn test_shift_left() {
        // 1 << 8 = 256
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_amount = b.alloc_reg();
            let r_val = b.alloc_reg();
            let r_result = b.alloc_reg();
            b.emit_op(Opcode::Integer, 8, r_amount, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 1, r_val, 0, P4::None, 0);
            b.emit_op(Opcode::ShiftLeft, r_amount, r_val, r_result, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_result, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(256)]);
    }

    #[test]
    fn test_shift_right() {
        // 256 >> 4 = 16
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_amount = b.alloc_reg();
            let r_val = b.alloc_reg();
            let r_result = b.alloc_reg();
            b.emit_op(Opcode::Integer, 4, r_amount, 0, P4::None, 0);
            b.emit_op(Opcode::Int64, 0, r_val, 0, P4::Int64(256), 0);
            b.emit_op(Opcode::ShiftRight, r_amount, r_val, r_result, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_result, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(16)]);
    }

    #[test]
    fn test_shift_left_overflow_clamp() {
        // Shift >= 64 returns 0
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_amount = b.alloc_reg();
            let r_val = b.alloc_reg();
            let r_result = b.alloc_reg();
            b.emit_op(Opcode::Int64, 0, r_amount, 0, P4::Int64(64), 0);
            b.emit_op(Opcode::Integer, 1, r_val, 0, P4::None, 0);
            b.emit_op(Opcode::ShiftLeft, r_amount, r_val, r_result, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_result, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(0)]);
    }

    #[test]
    fn test_shift_negative_reverses() {
        // Negative shift amount reverses direction: <<(-2) == >>(2)
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_amount = b.alloc_reg();
            let r_val = b.alloc_reg();
            let r_result = b.alloc_reg();
            b.emit_op(Opcode::Int64, 0, r_amount, 0, P4::Int64(-2), 0);
            b.emit_op(Opcode::Integer, 8, r_val, 0, P4::None, 0);
            b.emit_op(Opcode::ShiftLeft, r_amount, r_val, r_result, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_result, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        // 8 >> 2 = 2
        assert_eq!(rows[0], vec![SqliteValue::Integer(2)]);
    }

    #[test]
    fn test_bit_not() {
        // ~0 = -1 in two's complement
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            b.emit_op(Opcode::Integer, 0, r1, 0, P4::None, 0);
            b.emit_op(Opcode::BitNot, r1, r2, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r2, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(-1)]);
    }

    #[test]
    fn test_bitwise_null_propagation() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_null = b.alloc_reg();
            let r_val = b.alloc_reg();
            let r_and = b.alloc_reg();
            let r_or = b.alloc_reg();
            let r_not = b.alloc_reg();
            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0xFF, r_val, 0, P4::None, 0);
            b.emit_op(Opcode::BitAnd, r_null, r_val, r_and, P4::None, 0);
            b.emit_op(Opcode::BitOr, r_null, r_val, r_or, P4::None, 0);
            b.emit_op(Opcode::BitNot, r_null, r_not, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_and, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_or, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_not, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Null]);
        assert_eq!(rows[1], vec![SqliteValue::Null]);
        assert_eq!(rows[2], vec![SqliteValue::Null]);
    }

    // ── String Operations ──────────────────────────────────────────────

    #[test]
    fn test_concat_two_strings() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r3 = b.alloc_reg();
            b.emit_op(Opcode::String8, 0, r1, 0, P4::Str("hello ".to_owned()), 0);
            b.emit_op(Opcode::String8, 0, r2, 0, P4::Str("world".to_owned()), 0);
            // Concat: p3 = p2 || p1 (note operand order)
            b.emit_op(Opcode::Concat, r2, r1, r3, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Text("hello world".into())]);
    }

    #[test]
    fn test_concat_empty_string() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r3 = b.alloc_reg();
            b.emit_op(Opcode::String8, 0, r1, 0, P4::Str("test".to_owned()), 0);
            b.emit_op(Opcode::String8, 0, r2, 0, P4::Str(String::new()), 0);
            b.emit_op(Opcode::Concat, r2, r1, r3, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Text("test".into())]);
    }

    // ── Comparison Ops (all 6 + NULL) ──────────────────────────────────

    #[test]
    fn test_eq_jump_taken() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Integer, 42, r1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 42, r2, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            // Eq: if p3 == p1, jump to p2 → if r2 == r1, jump
            b.emit_jump_to_label(Opcode::Eq, r1, r2, taken, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_ne_jump_taken() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Integer, 10, r1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 20, r2, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            b.emit_jump_to_label(Opcode::Ne, r1, r2, taken, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_lt_jump_taken() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_big = b.alloc_reg();
            let r_small = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Integer, 100, r_big, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 5, r_small, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            // Lt: if p3 < p1, jump → if r_small < r_big
            let taken = b.emit_label();
            b.emit_jump_to_label(Opcode::Lt, r_big, r_small, taken, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_le_with_equal_values() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Integer, 7, r1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 7, r2, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            b.emit_jump_to_label(Opcode::Le, r1, r2, taken, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_ge_with_greater_value() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_big = b.alloc_reg();
            let r_small = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Integer, 5, r_small, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 100, r_big, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            // Ge: if p3 >= p1 → if r_big >= r_small
            b.emit_jump_to_label(Opcode::Ge, r_small, r_big, taken, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_comparison_null_no_jump() {
        // Standard SQL: NULL = 5 → no jump (NULL result)
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_null = b.alloc_reg();
            let r_five = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 5, r_five, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            b.emit_jump_to_label(Opcode::Eq, r_five, r_null, taken, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        // Should NOT jump: NULL = 5 is NULL (not true)
        assert_eq!(rows[0], vec![SqliteValue::Integer(0)]);
    }

    #[test]
    fn test_ne_nulleq_one_null() {
        // IS NOT semantics: NULL IS NOT 5 → true (jump)
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_null = b.alloc_reg();
            let r_five = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 5, r_five, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            b.emit_jump_to_label(Opcode::Ne, r_five, r_null, taken, P4::None, 0x80);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_comparison_jumpifnull_flag() {
        // JUMPIFNULL (0x10): jump to P2 when either operand is NULL.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_null = b.alloc_reg();
            let r_five = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 5, r_five, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            // Eq with JUMPIFNULL (0x10): NULL = 5 should jump
            b.emit_jump_to_label(Opcode::Eq, r_five, r_null, taken, P4::None, 0x10);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        // With JUMPIFNULL, NULL = 5 should jump to P2
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_comparison_storep2_non_null() {
        // STOREP2 (0x20): store boolean result in P2 instead of jumping.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_a = b.alloc_reg();
            let r_b = b.alloc_reg();
            let o_eq = b.alloc_reg();
            let o_ne = b.alloc_reg();
            let o_lt = b.alloc_reg();
            b.emit_op(Opcode::Integer, 5, r_a, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 10, r_b, 0, P4::None, 0);
            // Eq with STOREP2: 5 == 10 → store 0
            b.emit_op(Opcode::Eq, r_b, o_eq, r_a, P4::None, 0x20);
            // Ne with STOREP2: 5 != 10 → store 1
            b.emit_op(Opcode::Ne, r_b, o_ne, r_a, P4::None, 0x20);
            // Lt with STOREP2: 5 < 10 → store 1
            b.emit_op(Opcode::Lt, r_b, o_lt, r_a, P4::None, 0x20);
            b.emit_op(Opcode::ResultRow, o_eq, 3, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(
            rows[0],
            vec![
                SqliteValue::Integer(0), // 5 == 10 → false
                SqliteValue::Integer(1), // 5 != 10 → true
                SqliteValue::Integer(1), // 5 < 10 → true
            ]
        );
    }

    #[test]
    fn test_comparison_storep2_null_gives_null() {
        // STOREP2 (0x20) with NULL operand: store NULL in P2.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_null = b.alloc_reg();
            let r_five = b.alloc_reg();
            let o1 = b.alloc_reg();
            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 5, r_five, 0, P4::None, 0);
            // Eq with STOREP2: NULL == 5 → store NULL
            b.emit_op(Opcode::Eq, r_five, o1, r_null, P4::None, 0x20);
            b.emit_op(Opcode::ResultRow, o1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Null]);
    }

    // ── Logic Edge Cases ───────────────────────────────────────────────

    #[test]
    fn test_not_null_is_null() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_null = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            b.emit_op(Opcode::Not, r_null, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Null]);
    }

    #[test]
    fn test_not_zero_is_one() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_zero = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Integer, 0, r_zero, 0, P4::None, 0);
            b.emit_op(Opcode::Not, r_zero, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_not_nonzero_is_zero() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_val = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Integer, 42, r_val, 0, P4::None, 0);
            b.emit_op(Opcode::Not, r_val, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(0)]);
    }

    // ── Conditional Jumps ──────────────────────────────────────────────

    #[test]
    fn test_if_true_jumps() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_cond = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Integer, 1, r_cond, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            b.emit_jump_to_label(Opcode::If, r_cond, 0, taken, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_if_false_no_jump() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_cond = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Integer, 0, r_cond, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 99, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            b.emit_jump_to_label(Opcode::If, r_cond, 0, taken, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        // If with false → no jump → r_out stays 99
        assert_eq!(rows[0], vec![SqliteValue::Integer(99)]);
    }

    #[test]
    fn test_if_null_no_jump() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_cond = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Null, 0, r_cond, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 99, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            b.emit_jump_to_label(Opcode::If, r_cond, 0, taken, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        // If with NULL → no jump → r_out stays 99
        assert_eq!(rows[0], vec![SqliteValue::Integer(99)]);
    }

    #[test]
    fn test_ifnot_false_jumps() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_cond = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Integer, 0, r_cond, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            b.emit_jump_to_label(Opcode::IfNot, r_cond, 0, taken, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_ifnot_null_p3_zero_no_jump() {
        // IfNot with NULL and p3=0 → no jump (SQLite: p3 controls NULL behavior)
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_cond = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Null, 0, r_cond, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            b.emit_jump_to_label(Opcode::IfNot, r_cond, 0, taken, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        // p3=0: NULL → don't jump → r_out stays 0
        assert_eq!(rows[0], vec![SqliteValue::Integer(0)]);
    }

    #[test]
    fn test_ifnot_null_p3_one_jumps() {
        // IfNot with NULL and p3=1 → jump (SQLite: p3!=0 means jump on NULL)
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_cond = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Null, 0, r_cond, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            b.emit_jump_to_label(Opcode::IfNot, r_cond, 1, taken, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        // p3=1: NULL → jump → r_out = 1
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_if_null_p3_one_jumps() {
        // If with NULL and p3=1 → jump (SQLite: p3!=0 means jump on NULL)
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_cond = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Null, 0, r_cond, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            b.emit_jump_to_label(Opcode::If, r_cond, 1, taken, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        // p3=1: NULL → jump → r_out = 1
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_once_fires_only_once() {
        // Once falls through on first pass (runs the body), jumps to p2 on
        // subsequent passes (skips the body).  This matches C SQLite semantics.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_counter = b.alloc_reg();
            b.emit_op(Opcode::Integer, 0, r_counter, 0, P4::None, 0);

            let loop_start = b.emit_label();
            b.resolve_label(loop_start);
            // Once: first pass → fall through (run init code below),
            //        second+ pass → jump to `skip_init`.
            let skip_init = b.emit_label();
            b.emit_jump_to_label(Opcode::Once, 0, 0, skip_init, P4::None, 0);
            // Init code (runs only on first pass): increment counter.
            b.emit_op(Opcode::AddImm, r_counter, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, loop_start, P4::None, 0);
            // Second pass lands here — skip the init, output result.
            b.resolve_label(skip_init);
            b.emit_op(Opcode::ResultRow, r_counter, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        // First pass: Once falls through → counter becomes 1 → loop back.
        // Second pass: Once jumps to skip_init → output counter=1.
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_open_autoindex_without_memdb_supports_idxinsert_and_found() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            let found = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let cursor_id = 1;
            let r_insert_val = b.alloc_reg();
            let r_insert_key = b.alloc_reg();
            let r_probe_val = b.alloc_reg();
            let r_probe_key = b.alloc_reg();
            let r_out = b.alloc_reg();

            b.emit_op(Opcode::OpenAutoindex, cursor_id, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 7, r_insert_val, 0, P4::None, 0);
            b.emit_op(
                Opcode::MakeRecord,
                r_insert_val,
                1,
                r_insert_key,
                P4::None,
                0,
            );
            b.emit_op(Opcode::IdxInsert, cursor_id, r_insert_key, 0, P4::None, 0);

            b.emit_op(Opcode::Integer, 7, r_probe_val, 0, P4::None, 0);
            b.emit_op(Opcode::MakeRecord, r_probe_val, 1, r_probe_key, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::Found, cursor_id, r_probe_key, found, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(found);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);

            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
    }

    #[test]
    fn test_open_autoindex_honors_nocase_collation_for_found() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            let found = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let cursor_id = 1;
            let r_insert_val = b.alloc_reg();
            let r_insert_key = b.alloc_reg();
            let r_probe_val = b.alloc_reg();
            let r_probe_key = b.alloc_reg();
            let r_out = b.alloc_reg();

            b.emit_op(
                Opcode::OpenAutoindex,
                cursor_id,
                1,
                0,
                P4::Collation("NOCASE".to_owned()),
                0,
            );
            b.emit_op(
                Opcode::String8,
                0,
                r_insert_val,
                0,
                P4::Str("alpha".to_owned()),
                0,
            );
            b.emit_op(
                Opcode::MakeRecord,
                r_insert_val,
                1,
                r_insert_key,
                P4::None,
                0,
            );
            b.emit_op(Opcode::IdxInsert, cursor_id, r_insert_key, 0, P4::None, 0);

            b.emit_op(
                Opcode::String8,
                0,
                r_probe_val,
                0,
                P4::Str("ALPHA".to_owned()),
                0,
            );
            b.emit_op(Opcode::MakeRecord, r_probe_val, 1, r_probe_key, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::Found, cursor_id, r_probe_key, found, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(found);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);

            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
    }

    #[test]
    fn test_column_reads_from_key_only_autoindex_without_trailing_rowid() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let cursor_id = 1;
            let r_insert_val = b.alloc_reg();
            let r_insert_key = b.alloc_reg();
            let r_out = b.alloc_reg();

            b.emit_op(Opcode::OpenAutoindex, cursor_id, 1, 0, P4::None, 0);
            b.emit_op(
                Opcode::String8,
                0,
                r_insert_val,
                0,
                P4::Str("alpha".to_owned()),
                0,
            );
            b.emit_op(
                Opcode::MakeRecord,
                r_insert_val,
                1,
                r_insert_key,
                P4::None,
                0,
            );
            b.emit_op(Opcode::IdxInsert, cursor_id, r_insert_key, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::Rewind, cursor_id, 0, done, P4::None, 0);
            b.emit_op(Opcode::Column, cursor_id, 0, r_out, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Text("alpha".into())]]);
    }

    // ── Type Coercion ──────────────────────────────────────────────────

    #[test]
    fn test_cast_integer_to_text() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::Integer, 42, r, 0, P4::None, 0);
            // Cast to TEXT: p2 = 'B' (66)
            b.emit_op(Opcode::Cast, r, 66, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Text("42".into())]);
    }

    #[test]
    fn test_cast_text_to_integer() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::String8, 0, r, 0, P4::Str("123".to_owned()), 0);
            // Cast to INTEGER: p2 = 'D' (68)
            b.emit_op(Opcode::Cast, r, 68, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(123)]);
    }

    #[test]
    fn test_cast_to_real() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::Integer, 5, r, 0, P4::None, 0);
            // Cast to REAL: p2 = 'E' (69)
            b.emit_op(Opcode::Cast, r, 69, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Float(5.0)]);
    }

    #[test]
    fn test_cast_to_blob() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::String8, 0, r, 0, P4::Str("hi".to_owned()), 0);
            // Cast to BLOB: p2 = 'A' (65)
            b.emit_op(Opcode::Cast, r, 65, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Blob(b"hi".to_vec().into())]);
    }

    #[test]
    fn test_cast_text_sci_notation_to_integer() {
        // SQLite integer casts ignore exponent syntax in text and consume only
        // the signed integer prefix.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::String8, 0, r, 0, P4::Str("1.5e2abc".to_owned()), 0);
            b.emit_op(Opcode::Cast, r, 68, 0, P4::None, 0); // 'D' = INTEGER
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_cast_text_huge_exponent_to_integer_ignores_exponent() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::String8, 0, r, 0, P4::Str("1e999".to_owned()), 0);
            b.emit_op(Opcode::Cast, r, 68, 0, P4::None, 0); // 'D' = INTEGER
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_cast_text_prefix_to_real() {
        // Regression: CAST('3.14abc' AS REAL) must yield 3.14, not 0.0.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::String8, 0, r, 0, P4::Str("3.14abc".to_owned()), 0);
            b.emit_op(Opcode::Cast, r, 69, 0, P4::None, 0); // 'E' = REAL
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Float(3.14)]);
    }

    #[test]
    fn test_cast_sci_notation_to_real() {
        // CAST('2.5e3xyz' AS REAL) must yield 2500.0.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::String8, 0, r, 0, P4::Str("2.5e3xyz".to_owned()), 0);
            b.emit_op(Opcode::Cast, r, 69, 0, P4::None, 0); // 'E' = REAL
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Float(2500.0)]);
    }

    #[test]
    fn test_cast_text_prefix_to_numeric() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r3 = b.alloc_reg();
            b.emit_op(Opcode::String8, 0, r1, 0, P4::Str("123abc".to_owned()), 0);
            b.emit_op(Opcode::String8, 0, r2, 0, P4::Str("1.5e2abc".to_owned()), 0);
            b.emit_op(Opcode::String8, 0, r3, 0, P4::Str("abc".to_owned()), 0);
            b.emit_op(Opcode::Cast, r1, 67, 0, P4::None, 0); // 'C' = NUMERIC
            b.emit_op(Opcode::Cast, r2, 67, 0, P4::None, 0);
            b.emit_op(Opcode::Cast, r3, 67, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r1, 3, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(
            rows[0],
            vec![
                SqliteValue::Integer(123),
                SqliteValue::Integer(150),
                SqliteValue::Integer(0),
            ]
        );
    }

    #[test]
    fn test_must_be_int_accepts_integer() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::Integer, 42, r, 0, P4::None, 0);
            // MustBeInt: p2=0 means error on non-int, but 42 is int → passes
            b.emit_op(Opcode::MustBeInt, r, 0, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(42)]);
    }

    #[test]
    fn test_must_be_int_jumps_on_non_int() {
        // MustBeInt with p2 > 0: jump to p2 instead of error.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::String8, 0, r, 0, P4::Str("not_int".to_owned()), 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let fallback = b.emit_label();
            b.emit_jump_to_label(Opcode::MustBeInt, r, 0, fallback, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(fallback);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        // Non-int triggers jump → r_out = 1
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_real_affinity_converts_int() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::Integer, 7, r, 0, P4::None, 0);
            b.emit_op(Opcode::RealAffinity, r, 0, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Float(7.0)]);
    }

    #[test]
    fn test_real_affinity_no_op_on_float() {
        // RealAffinity on a float is a no-op.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::Real, 0, r, 0, P4::Real(std::f64::consts::PI), 0);
            b.emit_op(Opcode::RealAffinity, r, 0, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Float(std::f64::consts::PI)]);
    }

    // ── Error Handling ─────────────────────────────────────────────────

    #[test]
    fn test_halt_if_null_triggers() {
        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        let r = b.alloc_reg();
        b.emit_op(Opcode::Null, 0, r, 0, P4::None, 0);
        b.emit_op(
            Opcode::HaltIfNull,
            19,
            0,
            r,
            P4::Str("NOT NULL constraint failed".to_owned()),
            0,
        );
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        let prog = b.finish().unwrap();
        let mut engine = VdbeEngine::new(prog.register_count());
        let outcome = engine.execute(&prog).unwrap();
        assert_eq!(
            outcome,
            ExecOutcome::Error {
                code: 19,
                message: "NOT NULL constraint failed".to_owned(),
            }
        );
    }

    #[test]
    fn test_halt_if_null_passes_non_null() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::Integer, 42, r, 0, P4::None, 0);
            b.emit_op(
                Opcode::HaltIfNull,
                19,
                0,
                r,
                P4::Str("should not fire".to_owned()),
                0,
            );
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(42)]);
    }

    #[test]
    fn test_typecheck_reports_sqlite_constraint_datatype_marker() {
        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        let r = b.alloc_reg();
        b.emit_op(Opcode::String8, 0, r, 0, P4::Str("bad".to_owned()), 0);
        // Encoded TypeCheck P4: "I\ttbl\tcol_a"
        b.emit_op(
            Opcode::TypeCheck,
            r,
            1,
            0,
            P4::Str("I\ttbl\tcol_a".to_owned()),
            0,
        );
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        let err = engine
            .execute(&prog)
            .expect_err("typecheck should fail for TEXT into INTEGER STRICT slot");
        let err_text = err.to_string();
        assert!(
            err_text.contains("cannot store"),
            "error should mention 'cannot store': {err_text}"
        );
        assert!(
            err_text.contains("tbl.col_a"),
            "error should mention column: {err_text}"
        );
        assert_eq!(err.error_code(), ErrorCode::Constraint);
        assert_eq!(err.extended_error_code(), 3091); // SQLITE_CONSTRAINT_DATATYPE
    }

    // ── Miscellaneous Opcodes ──────────────────────────────────────────

    #[test]
    fn test_zero_or_null_opcode() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let non_null_lhs = b.alloc_reg();
            let non_null_rhs = b.alloc_reg();
            let null_lhs = b.alloc_reg();
            let null_rhs = b.alloc_reg();
            let o1 = b.alloc_reg();
            let o2 = b.alloc_reg();
            let o3 = b.alloc_reg();
            b.emit_op(Opcode::Integer, 7, non_null_lhs, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 11, non_null_rhs, 0, P4::None, 0);
            b.emit_op(Opcode::Null, 0, null_lhs, 0, P4::None, 0);
            b.emit_op(Opcode::Null, 0, null_rhs, 0, P4::None, 0);
            b.emit_op(
                Opcode::ZeroOrNull,
                non_null_lhs,
                o1,
                non_null_rhs,
                P4::None,
                0,
            );
            b.emit_op(Opcode::ZeroOrNull, null_lhs, o2, non_null_rhs, P4::None, 0);
            b.emit_op(Opcode::ZeroOrNull, non_null_lhs, o3, null_rhs, P4::None, 0);
            b.emit_op(Opcode::ResultRow, o1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, o2, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, o3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(0)]);
        assert_eq!(rows[1], vec![SqliteValue::Null]);
        assert_eq!(rows[2], vec![SqliteValue::Null]);
    }

    #[test]
    fn test_is_true_opcode() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_true = b.alloc_reg();
            let r_false = b.alloc_reg();
            let r_null = b.alloc_reg();
            let o1 = b.alloc_reg();
            let o2 = b.alloc_reg();
            let o3 = b.alloc_reg();
            b.emit_op(Opcode::Integer, 42, r_true, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_false, 0, P4::None, 0);
            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            b.emit_op(Opcode::IsTrue, r_true, o1, 0, P4::None, 0);
            b.emit_op(Opcode::IsTrue, r_false, o2, 0, P4::None, 0);
            b.emit_op(Opcode::IsTrue, r_null, o3, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, o1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, o2, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, o3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]); // 42 is true
        assert_eq!(rows[1], vec![SqliteValue::Integer(0)]); // 0 is false
        assert_eq!(rows[2], vec![SqliteValue::Integer(0)]); // NULL is not true
    }

    #[test]
    fn test_is_true_is_false_semantics() {
        // IS FALSE: P3=1, P4=1  →  NULL→0, truthy→0, falsy→1
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_true = b.alloc_reg();
            let r_false = b.alloc_reg();
            let r_null = b.alloc_reg();
            let o1 = b.alloc_reg();
            let o2 = b.alloc_reg();
            let o3 = b.alloc_reg();
            b.emit_op(Opcode::Integer, 42, r_true, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_false, 0, P4::None, 0);
            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            b.emit_op(Opcode::IsTrue, r_true, o1, 1, P4::Int(1), 0);
            b.emit_op(Opcode::IsTrue, r_false, o2, 1, P4::Int(1), 0);
            b.emit_op(Opcode::IsTrue, r_null, o3, 1, P4::Int(1), 0);
            b.emit_op(Opcode::ResultRow, o1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, o2, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, o3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(0)]); // 42 IS FALSE → 0
        assert_eq!(rows[1], vec![SqliteValue::Integer(1)]); // 0 IS FALSE → 1
        assert_eq!(rows[2], vec![SqliteValue::Integer(0)]); // NULL IS FALSE → 0
    }

    #[test]
    fn test_is_true_is_not_true_semantics() {
        // IS NOT TRUE: P3=0, P4=1  →  NULL→1, truthy→0, falsy→1
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_true = b.alloc_reg();
            let r_false = b.alloc_reg();
            let r_null = b.alloc_reg();
            let o1 = b.alloc_reg();
            let o2 = b.alloc_reg();
            let o3 = b.alloc_reg();
            b.emit_op(Opcode::Integer, 42, r_true, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_false, 0, P4::None, 0);
            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            b.emit_op(Opcode::IsTrue, r_true, o1, 0, P4::Int(1), 0);
            b.emit_op(Opcode::IsTrue, r_false, o2, 0, P4::Int(1), 0);
            b.emit_op(Opcode::IsTrue, r_null, o3, 0, P4::Int(1), 0);
            b.emit_op(Opcode::ResultRow, o1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, o2, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, o3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(0)]); // 42 IS NOT TRUE → 0
        assert_eq!(rows[1], vec![SqliteValue::Integer(1)]); // 0 IS NOT TRUE → 1
        assert_eq!(rows[2], vec![SqliteValue::Integer(1)]); // NULL IS NOT TRUE → 1
    }

    #[test]
    fn test_is_true_is_not_false_semantics() {
        // IS NOT FALSE: P3=1, P4=0  →  NULL→1, truthy→1, falsy→0
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_true = b.alloc_reg();
            let r_false = b.alloc_reg();
            let r_null = b.alloc_reg();
            let o1 = b.alloc_reg();
            let o2 = b.alloc_reg();
            let o3 = b.alloc_reg();
            b.emit_op(Opcode::Integer, 42, r_true, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_false, 0, P4::None, 0);
            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            b.emit_op(Opcode::IsTrue, r_true, o1, 1, P4::Int(0), 0);
            b.emit_op(Opcode::IsTrue, r_false, o2, 1, P4::Int(0), 0);
            b.emit_op(Opcode::IsTrue, r_null, o3, 1, P4::Int(0), 0);
            b.emit_op(Opcode::ResultRow, o1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, o2, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, o3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]); // 42 IS NOT FALSE → 1
        assert_eq!(rows[1], vec![SqliteValue::Integer(0)]); // 0 IS NOT FALSE → 0
        assert_eq!(rows[2], vec![SqliteValue::Integer(1)]); // NULL IS NOT FALSE → 1
    }

    #[test]
    fn test_noop_does_nothing() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::Integer, 42, r, 0, P4::None, 0);
            b.emit_op(Opcode::Noop, 0, 0, 0, P4::None, 0);
            b.emit_op(Opcode::Noop, 0, 0, 0, P4::None, 0);
            b.emit_op(Opcode::Noop, 0, 0, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(42)]);
    }

    #[test]
    fn test_result_row_three_columns() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r3 = b.alloc_reg();
            b.emit_op(Opcode::Integer, 1, r1, 0, P4::None, 0);
            b.emit_op(Opcode::String8, 0, r2, 0, P4::Str("two".to_owned()), 0);
            b.emit_op(Opcode::Real, 0, r3, 0, P4::Real(3.0), 0);
            b.emit_op(Opcode::ResultRow, r1, 3, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(
            rows[0],
            vec![
                SqliteValue::Integer(1),
                SqliteValue::Text("two".into()),
                SqliteValue::Float(3.0),
            ]
        );
    }

    #[test]
    fn test_multiple_result_rows() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r = b.alloc_reg();
            b.emit_op(Opcode::Integer, 1, r, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 2, r, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 3, r, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows.len(), 3);
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
        assert_eq!(rows[1], vec![SqliteValue::Integer(2)]);
        assert_eq!(rows[2], vec![SqliteValue::Integer(3)]);
    }

    #[test]
    fn test_gosub_nested() {
        // Test nested Gosub: main calls sub1, which calls sub2.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_ret1 = b.alloc_reg();
            let r_ret2 = b.alloc_reg();
            let r_val = b.alloc_reg();

            // Main: call sub1
            let sub1 = b.emit_label();
            b.emit_jump_to_label(Opcode::Gosub, r_ret1, 0, sub1, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_val, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);

            // sub1: set r_val=10, call sub2, add 1
            b.resolve_label(sub1);
            b.emit_op(Opcode::Integer, 10, r_val, 0, P4::None, 0);
            let sub2 = b.emit_label();
            b.emit_jump_to_label(Opcode::Gosub, r_ret2, 0, sub2, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_val, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Return, r_ret1, 0, 0, P4::None, 0);

            // sub2: multiply r_val by 5
            b.resolve_label(sub2);
            let r_five = b.alloc_reg();
            b.emit_op(Opcode::Integer, 5, r_five, 0, P4::None, 0);
            b.emit_op(Opcode::Multiply, r_five, r_val, r_val, P4::None, 0);
            b.emit_op(Opcode::Return, r_ret2, 0, 0, P4::None, 0);

            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        // 10 * 5 + 1 = 51
        assert_eq!(rows[0], vec![SqliteValue::Integer(51)]);
    }

    #[test]
    fn test_coroutine_yield_resume() {
        // Producer coroutine yields 3 values; consumer resumes and emits rows.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_co = b.alloc_reg();
            let r_val = b.alloc_reg();

            // Patch target addresses after both blocks are emitted.
            let init_addr = b.emit_op(Opcode::InitCoroutine, r_co, 0, 0, P4::None, 0);
            #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
            let consumer_start = b.current_addr() as i32;
            b.emit_op(Opcode::Yield, r_co, 0, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_val, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Yield, r_co, 0, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_val, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Yield, r_co, 0, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_val, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);

            #[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
            let producer_start = b.current_addr() as i32;
            b.emit_op(Opcode::Integer, 100, r_val, 0, P4::None, 0);
            b.emit_op(Opcode::Yield, r_co, 0, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 200, r_val, 0, P4::None, 0);
            b.emit_op(Opcode::Yield, r_co, 0, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 300, r_val, 0, P4::None, 0);
            b.emit_op(Opcode::Yield, r_co, 0, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);

            if let Some(init_op) = b.op_at_mut(init_addr) {
                init_op.p2 = consumer_start;
                init_op.p3 = producer_start;
            }

            b.resolve_label(end);
        });
        assert_eq!(rows.len(), 3);
        assert_eq!(rows[0], vec![SqliteValue::Integer(100)]);
        assert_eq!(rows[1], vec![SqliteValue::Integer(200)]);
        assert_eq!(rows[2], vec![SqliteValue::Integer(300)]);
    }

    #[test]
    fn test_make_record_encodes_values() {
        // MakeRecord packs source registers into the SQLite record format blob.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r_rec = b.alloc_reg();
            b.emit_op(Opcode::Integer, 1, r1, 0, P4::None, 0);
            b.emit_op(Opcode::String8, 0, r2, 0, P4::Str("a".to_owned()), 0);
            b.emit_op(Opcode::MakeRecord, r1, 2, r_rec, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_rec, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        let produced_blob = rows.first().and_then(|row| row.first());
        assert!(
            matches!(produced_blob, Some(SqliteValue::Blob(_))),
            "MakeRecord should produce a blob"
        );
        let decoded = decode_record(&rows[0][0]).unwrap();
        assert_eq!(
            decoded,
            vec![SqliteValue::Integer(1), SqliteValue::Text("a".into())]
        );
    }

    #[test]
    fn test_make_record_negative_source_register_stays_null() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_record = b.alloc_reg();
            b.emit_op(Opcode::Integer, 777, 0, 0, P4::None, 0);
            b.emit_op(Opcode::MakeRecord, -1, 1, r_record, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_record, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        let decoded = decode_record(&rows[0][0]).expect("record should decode");
        assert_eq!(decoded, vec![SqliteValue::Null]);
    }

    #[test]
    fn test_make_record_precomputed_header_matches_generic_path() {
        let precomputed = fsqlite_types::record::PrecomputedRecordHeader::new(&[
            fsqlite_types::record::PrecomputedSerialTypeKind::NullPlaceholder,
            fsqlite_types::record::PrecomputedSerialTypeKind::IntegerOrNull,
            fsqlite_types::record::PrecomputedSerialTypeKind::RealOrNull,
        ]);

        let generic_rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_rowid = b.alloc_reg();
            let r_int = b.alloc_reg();
            let r_real = b.alloc_reg();
            let r_record = b.alloc_reg();
            b.emit_op(Opcode::Integer, 77, r_rowid, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 5, r_int, 0, P4::None, 0);
            b.emit_op(Opcode::Real, 0, r_real, 0, P4::Real(2.5), 0);
            b.emit_op(
                Opcode::MakeRecord,
                r_rowid,
                3,
                r_record,
                P4::Affinity("XDE".to_owned()),
                0,
            );
            b.emit_op(Opcode::ResultRow, r_record, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        let precomputed_rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_rowid = b.alloc_reg();
            let r_int = b.alloc_reg();
            let r_real = b.alloc_reg();
            let r_record = b.alloc_reg();
            b.emit_op(Opcode::Integer, 77, r_rowid, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 5, r_int, 0, P4::None, 0);
            b.emit_op(Opcode::Real, 0, r_real, 0, P4::Real(2.5), 0);
            b.emit_op(
                Opcode::MakeRecord,
                r_rowid,
                3,
                r_record,
                P4::PrecomputedHeader(precomputed.clone()),
                0,
            );
            b.emit_op(Opcode::ResultRow, r_record, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(precomputed_rows, generic_rows);
        let decoded = decode_record(&precomputed_rows[0][0]).expect("record should decode");
        assert_eq!(
            decoded,
            vec![
                SqliteValue::Null,
                SqliteValue::Integer(5),
                SqliteValue::Float(2.5),
            ]
        );
    }

    #[test]
    fn test_make_record_precomputed_header_falls_back_on_column_count_mismatch() {
        let precomputed = fsqlite_types::record::PrecomputedRecordHeader::new(&[
            fsqlite_types::record::PrecomputedSerialTypeKind::IntegerOrNull,
            fsqlite_types::record::PrecomputedSerialTypeKind::IntegerOrNull,
            fsqlite_types::record::PrecomputedSerialTypeKind::IntegerOrNull,
        ]);

        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_a = b.alloc_reg();
            let r_b = b.alloc_reg();
            let r_c = b.alloc_reg();
            let r_record = b.alloc_reg();
            b.emit_op(Opcode::Integer, 11, r_a, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 22, r_b, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 33, r_c, 0, P4::None, 0);
            b.emit_op(
                Opcode::MakeRecord,
                r_a,
                2,
                r_record,
                P4::PrecomputedHeader(precomputed),
                0,
            );
            b.emit_op(Opcode::ResultRow, r_record, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        let decoded = decode_record(&rows[0][0]).expect("record should decode");
        assert_eq!(
            decoded,
            vec![SqliteValue::Integer(11), SqliteValue::Integer(22)],
            "stale precomputed headers must not widen the record shape",
        );
    }

    #[test]
    fn test_make_record_affinity_shape_follows_p2_column_count() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());

        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_a = b.alloc_reg();
            let r_b = b.alloc_reg();
            let r_c = b.alloc_reg();
            let r_record_short_affinity = b.alloc_reg();
            let r_record_long_affinity = b.alloc_reg();
            b.emit_op(Opcode::Integer, 11, r_a, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 22, r_b, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 33, r_c, 0, P4::None, 0);
            b.emit_op(
                Opcode::MakeRecord,
                r_a,
                2,
                r_record_short_affinity,
                P4::Affinity("X".to_owned()),
                0,
            );
            b.emit_op(
                Opcode::MakeRecord,
                r_a,
                1,
                r_record_long_affinity,
                P4::Affinity("XDE".to_owned()),
                0,
            );
            b.emit_op(
                Opcode::ResultRow,
                r_record_short_affinity,
                2,
                0,
                P4::None,
                0,
            );
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        let short_affinity =
            decode_record(&rows[0][0]).expect("short-affinity record should decode");
        assert_eq!(
            short_affinity,
            vec![SqliteValue::Null, SqliteValue::Integer(22)],
            "a short affinity string must not shrink the P2 register range",
        );
        let long_affinity = decode_record(&rows[0][1]).expect("long-affinity record should decode");
        assert_eq!(
            long_affinity,
            vec![SqliteValue::Null],
            "a long affinity string must not widen the P2 register range",
        );
    }

    #[test]
    fn test_compiled_simple_insert_applies_make_record_null_placeholders() {
        let mut affinity_values = vec![
            SqliteValue::Integer(77),
            SqliteValue::Text("kept".into()),
            SqliteValue::Integer(9),
        ];
        VdbeEngine::apply_make_record_null_placeholders(
            &mut affinity_values,
            &P4::Affinity("XBD".to_owned()),
        );
        assert_eq!(
            affinity_values,
            vec![
                SqliteValue::Null,
                SqliteValue::Text("kept".into()),
                SqliteValue::Integer(9),
            ],
            "compiled INSERT must preserve MakeRecord X-affinity IPK placeholders",
        );

        let precomputed = fsqlite_types::record::PrecomputedRecordHeader::new(&[
            fsqlite_types::record::PrecomputedSerialTypeKind::NullPlaceholder,
            fsqlite_types::record::PrecomputedSerialTypeKind::IntegerOrNull,
        ]);
        let mut precomputed_values = vec![SqliteValue::Integer(88), SqliteValue::Integer(5)];
        VdbeEngine::apply_make_record_null_placeholders(
            &mut precomputed_values,
            &P4::PrecomputedHeader(precomputed),
        );
        assert_eq!(
            precomputed_values,
            vec![SqliteValue::Null, SqliteValue::Integer(5)],
            "compiled INSERT must preserve MakeRecord precomputed-header IPK placeholders",
        );
    }

    #[test]
    fn test_compiled_record_builder_precomputed_header_matches_generic_and_logs_microbench() {
        let header = fsqlite_types::record::PrecomputedRecordHeader::new(&[
            fsqlite_types::record::PrecomputedSerialTypeKind::NullPlaceholder,
            fsqlite_types::record::PrecomputedSerialTypeKind::IntegerOrNull,
            fsqlite_types::record::PrecomputedSerialTypeKind::RealOrNull,
        ]);
        let builder = CompiledRecordBuilder::PrecomputedHeader(header);
        let mut values = vec![
            SqliteValue::Null,
            SqliteValue::Integer(200),
            SqliteValue::Float(2.5),
        ];
        let expected = serialize_record(&values);

        let plan = build_compiled_record_write_plan(values.as_slice(), &builder);
        assert_eq!(plan.exact_size(), expected.len());
        let mut fused = vec![0; plan.exact_size()];
        plan.write_into_slice(&mut fused)
            .expect("precomputed record plan should write");
        assert_eq!(fused, expected);

        let mut fallback_buf = Vec::new();
        serialize_compiled_record_into_vec(values.as_slice(), &builder, &mut fallback_buf);
        assert_eq!(fallback_buf, expected);

        let iterations = 20_000usize;
        let mut generic_dst = Vec::new();
        let generic_start = Instant::now();
        for i in 0..iterations {
            values[1] = SqliteValue::Integer(200 + i64::try_from(i % 100).unwrap_or(0));
            let plan = fsqlite_types::record::plan_record_iter_serialization(values.iter());
            generic_dst.resize(plan.exact_size(), 0);
            plan.write_into_slice(generic_dst.as_mut_slice())
                .expect("generic plan should write");
            std::hint::black_box(&generic_dst);
        }
        let generic_elapsed = generic_start.elapsed();

        let mut fused_dst = Vec::new();
        let fused_start = Instant::now();
        for i in 0..iterations {
            values[1] = SqliteValue::Integer(200 + i64::try_from(i % 100).unwrap_or(0));
            let plan = build_compiled_record_write_plan(values.as_slice(), &builder);
            fused_dst.resize(plan.exact_size(), 0);
            plan.write_into_slice(fused_dst.as_mut_slice())
                .expect("precomputed plan should write");
            std::hint::black_box(&fused_dst);
        }
        let fused_elapsed = fused_start.elapsed();

        assert_eq!(fused_dst, generic_dst);
        eprintln!(
            "bd-q7qj6 record assembly microbench: iterations={iterations}, generic_plan_ns={}, fused_precomputed_ns={}, record_bytes={}",
            generic_elapsed.as_nanos(),
            fused_elapsed.as_nanos(),
            fused_dst.len()
        );
    }

    #[test]
    fn test_make_record_sideband_is_invalidated_by_register_overwrite() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_value = b.alloc_reg();
            let r_record = b.alloc_reg();
            b.emit_op(Opcode::Integer, 1, r_value, 0, P4::None, 0);
            b.emit_op(Opcode::MakeRecord, r_value, 1, r_record, P4::None, 0);
            b.emit_op(Opcode::Integer, 42, r_record, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_record, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Integer(42)]]);
    }

    #[test]
    fn test_write_text_to_reg_reuses_unique_heap_buffer() {
        let mut engine = VdbeEngine::new(2);

        engine.write_text_to_reg(
            1,
            "this string is definitely longer than twenty three bytes",
        );
        let first_reg = engine.get_reg(1);
        assert!(
            matches!(first_reg, SqliteValue::Text(_)),
            "register should contain text"
        );
        let SqliteValue::Text(first) = first_reg else {
            return;
        };
        let original_ptr = first.as_str().as_ptr();

        engine.write_text_to_reg(1, "another long string that still fits the same allocation");

        let updated_reg = engine.get_reg(1);
        assert!(
            matches!(updated_reg, SqliteValue::Text(_)),
            "register should still contain text"
        );
        let SqliteValue::Text(updated) = updated_reg else {
            return;
        };
        assert_eq!(updated.as_str().as_ptr(), original_ptr);
        assert_eq!(
            updated.as_str(),
            "another long string that still fits the same allocation"
        );
    }

    #[test]
    fn test_write_blob_to_reg_reuses_unique_buffer() {
        let mut engine = VdbeEngine::new(2);

        engine.write_blob_to_reg(1, &[1_u8, 2, 3, 4]);
        let first_reg = engine.get_reg(1);
        assert!(
            matches!(first_reg, SqliteValue::Blob(_)),
            "register should contain blob"
        );
        let SqliteValue::Blob(first) = first_reg else {
            return;
        };
        let original_ptr = Arc::as_ptr(first);

        engine.write_blob_to_reg(1, &[9_u8, 8, 7, 6]);

        let updated_reg = engine.get_reg(1);
        assert!(
            matches!(updated_reg, SqliteValue::Blob(_)),
            "register should still contain blob"
        );
        let SqliteValue::Blob(updated) = updated_reg else {
            return;
        };
        assert_eq!(Arc::as_ptr(updated), original_ptr);
        assert_eq!(updated.as_ref(), &[9_u8, 8, 7, 6]);
    }

    #[test]
    fn test_set_reg_returns_reusable_values_to_pool() {
        use fsqlite_types::value::{pool_clear, pool_len};

        pool_clear();
        let mut engine = VdbeEngine::new(2);

        engine.set_reg(
            1,
            SqliteValue::Text("this string is definitely longer than twenty three bytes".into()),
        );
        assert_eq!(pool_len(), 0);

        engine.set_reg(1, SqliteValue::Integer(7));
        assert_eq!(pool_len(), 1);

        pool_clear();
    }

    #[test]
    fn test_set_reg_skips_shared_values_without_reusable_backing_storage() {
        use fsqlite_types::value::{pool_clear, pool_len};

        pool_clear();
        let mut engine = VdbeEngine::new(2);

        let shared_text =
            Arc::<str>::from("this string is definitely longer than twenty three bytes");
        engine.set_reg(
            1,
            SqliteValue::Text(SmallText::from_arc(Arc::clone(&shared_text))),
        );
        engine.set_reg(1, SqliteValue::Integer(7));
        assert_eq!(
            pool_len(),
            0,
            "shared text values should not fill the reusable slab on register overwrite",
        );

        let shared_blob = Arc::<[u8]>::from([0xCA_u8, 0xFE, 0xBA, 0xBE].as_slice());
        engine.set_reg(1, SqliteValue::Blob(Arc::clone(&shared_blob)));
        engine.set_reg(1, SqliteValue::Integer(8));
        assert_eq!(
            pool_len(),
            0,
            "shared blob values should not fill the reusable slab on register overwrite",
        );

        pool_clear();
    }

    #[test]
    fn test_make_record_retains_statement_scratch_capacity() {
        let mut builder = ProgramBuilder::new();
        let end = builder.emit_label();
        builder.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        let r_value = builder.alloc_reg();
        let r_record = builder.alloc_reg();
        builder.emit_op(Opcode::String8, 0, r_value, 0, P4::Str("x".repeat(256)), 0);
        builder.emit_op(
            Opcode::MakeRecord,
            r_value,
            1,
            r_record,
            P4::Affinity("B".to_owned()),
            0,
        );
        builder.emit_op(
            Opcode::String8,
            0,
            r_value,
            0,
            P4::Str("tiny".to_owned()),
            0,
        );
        builder.emit_op(
            Opcode::MakeRecord,
            r_value,
            1,
            r_record,
            P4::Affinity("B".to_owned()),
            0,
        );
        builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        builder.resolve_label(end);

        let program = builder.finish().expect("program should build");
        let expected_capacity = estimate_make_record_buffer_capacity(&program, PageSize::DEFAULT);
        let mut engine = VdbeEngine::new(program.register_count());

        let outcome = engine.execute(&program).expect("program should execute");
        assert!(matches!(outcome, ExecOutcome::Done));
        assert!(
            engine.make_record_lookaside.capacity() >= expected_capacity,
            "MakeRecord scratch should retain its pre-sized capacity across the statement"
        );
        assert!(
            !engine.make_record_lookaside.is_empty(),
            "final MakeRecord should keep record bytes in the sideband until consumed"
        );
        let materialized = engine.clone_reg_materialized(r_record);
        assert!(
            engine.make_record_lookaside.is_empty(),
            "statement scratch should be length-reset after sideband materialization"
        );
        let decoded = decode_record(&materialized).expect("record should decode");
        assert_eq!(decoded, vec![SqliteValue::Text("tiny".into())]);
    }

    #[test]
    fn test_function_negative_argument_register_stays_null() {
        let rows = run_program_with_functions(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Integer, 777, 0, 0, P4::None, 0);
            b.emit_op(
                Opcode::Function,
                0,
                -1,
                r_out,
                P4::FuncName("typeof".to_owned()),
                1,
            );
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Text("null".into())]]);
    }

    #[test]
    fn test_result_row_negative_start_register_stays_null() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            b.emit_op(Opcode::Integer, 777, 0, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, -1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Null]]);
    }

    #[test]
    fn test_move_negative_source_register_stays_null() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_dest = b.alloc_reg();
            b.emit_op(Opcode::Integer, 777, 0, 0, P4::None, 0);
            b.emit_op(Opcode::Move, -1, r_dest, 1, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_dest, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Null]]);
    }

    #[test]
    fn test_complex_expression_chain() {
        // Test: ((10 + 20) * 3 - 5) / 2 = (90 - 5) / 2 = 85 / 2 = 42
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r10 = b.alloc_reg();
            let r20 = b.alloc_reg();
            let r3 = b.alloc_reg();
            let r5 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let t1 = b.alloc_reg();
            let t2 = b.alloc_reg();
            let t3 = b.alloc_reg();
            b.emit_op(Opcode::Integer, 10, r10, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 20, r20, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 3, r3, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 5, r5, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 2, r2, 0, P4::None, 0);
            b.emit_op(Opcode::Add, r10, r20, t1, P4::None, 0); // 30
            b.emit_op(Opcode::Multiply, r3, t1, t2, P4::None, 0); // 90
            b.emit_op(Opcode::Subtract, r5, t2, t2, P4::None, 0); // 85
            b.emit_op(Opcode::Divide, r2, t2, t3, P4::None, 0); // 42
            b.emit_op(Opcode::ResultRow, t3, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(42)]);
    }

    #[test]
    fn test_string_comparison() {
        // String comparison: 'abc' < 'abd' → true
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::String8, 0, r1, 0, P4::Str("abd".to_owned()), 0);
            b.emit_op(Opcode::String8, 0, r2, 0, P4::Str("abc".to_owned()), 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            // Lt: if p3 (r2="abc") < p1 (r1="abd"), jump
            b.emit_jump_to_label(Opcode::Lt, r1, r2, taken, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    #[test]
    fn test_mixed_type_comparison() {
        // Integer vs Float comparison: 5 == 5.0
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_int = b.alloc_reg();
            let r_float = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Integer, 5, r_int, 0, P4::None, 0);
            b.emit_op(Opcode::Real, 0, r_float, 0, P4::Real(5.0), 0);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            let taken = b.emit_label();
            b.emit_jump_to_label(Opcode::Eq, r_int, r_float, taken, P4::None, 0);
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(taken);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0], vec![SqliteValue::Integer(1)]);
    }

    // ── bd-1s7a: Storage cursor acceptance tests ───────────────────────

    /// Build and execute a program with a MemDatabase + storage cursors enabled.
    fn run_with_storage_cursors(
        db: MemDatabase,
        build: impl FnOnce(&mut ProgramBuilder),
    ) -> Vec<Vec<SqliteValue>> {
        let mut b = ProgramBuilder::new();
        build(&mut b);
        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.enable_storage_read_cursors(true);
        engine.set_database(db);
        // These tests exercise the MemPageStore path without a real pager txn.
        engine.set_reject_mem_fallback(false);
        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        engine
            .take_results()
            .into_iter()
            .map(|v| v.into_vec())
            .collect()
    }

    #[test]
    fn test_vdbe_openread_uses_btree_cursor_backend() {
        // Insert rows into a MemDatabase, then verify OpenRead routes through
        // the storage cursor path (not MemCursor) when enabled.
        let mut db = MemDatabase::new();
        let root = db.create_table(2);
        let table = db.get_table_mut(root).unwrap();
        table.insert(
            1,
            vec![SqliteValue::Integer(10), SqliteValue::Text("a".into())],
        );
        table.insert(
            2,
            vec![SqliteValue::Integer(20), SqliteValue::Text("b".into())],
        );

        let rows = run_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(2), 0);
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);

            let body = b.current_addr();
            b.emit_op(Opcode::Column, 0, 0, 1, P4::None, 0);
            b.emit_op(Opcode::Column, 0, 1, 2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 1, 2, 0, P4::None, 0);

            let next_target =
                i32::try_from(body).expect("program counter should fit into i32 for tests");
            b.emit_op(Opcode::Next, 0, next_target, 0, P4::None, 0);

            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows.len(), 2, "should return 2 rows via storage cursor");
        assert_eq!(rows[0][0], SqliteValue::Integer(10));
        assert_eq!(rows[0][1], SqliteValue::Text("a".into()));
        assert_eq!(rows[1][0], SqliteValue::Integer(20));
        assert_eq!(rows[1][1], SqliteValue::Text("b".into()));
    }

    #[test]
    fn test_select_uses_storage_cursor_not_memdb_for_persisted_table() {
        // With storage cursors enabled, verify the engine uses StorageCursor
        // (the read path) rather than MemCursor for OpenRead.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(1, vec![SqliteValue::Integer(42)]);

        let rows = run_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            // OpenRead with storage cursors enabled should use StorageCursor.
            b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);

            let body = b.current_addr();
            b.emit_op(Opcode::Column, 0, 0, 1, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
            let next_target =
                i32::try_from(body).expect("program counter should fit into i32 for tests");
            b.emit_op(Opcode::Next, 0, next_target, 0, P4::None, 0);

            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0], vec![SqliteValue::Integer(42)]);
    }

    #[test]
    fn test_count_uses_exact_cardinality_for_storage_cursor() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(1, vec![SqliteValue::Integer(10)]);
        table.insert(2, vec![SqliteValue::Integer(20)]);
        table.insert(3, vec![SqliteValue::Integer(30)]);

        let rows = run_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);
            b.emit_op(Opcode::Count, 0, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Integer(3)]]);
    }

    #[test]
    fn test_count_on_writable_storage_cursor_ignores_hydrated_memdb_fast_path() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(1, vec![SqliteValue::Integer(10)]);
        table.insert(2, vec![SqliteValue::Integer(20)]);
        table.insert(3, vec![SqliteValue::Integer(30)]);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
        b.emit_op(Opcode::Integer, 2, 1, 0, P4::None, 0);
        b.emit_jump_to_label(Opcode::SeekRowid, 0, 1, done, P4::None, 0);
        b.emit_op(Opcode::Delete, 0, 0, 0, P4::None, 0);
        b.emit_op(Opcode::Count, 0, 2, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, 2, 1, 0, P4::None, 0);
        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_memdb_rows_loaded(true);
        engine.set_reject_mem_fallback(false);

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        let rows: Vec<_> = engine
            .take_results()
            .into_iter()
            .map(|row| row.into_vec())
            .collect();
        assert_eq!(rows, vec![vec![SqliteValue::Integer(2)]]);
    }

    #[test]
    fn test_count_on_mem_cursor_uses_memdb_rows() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(1, vec![SqliteValue::Integer(10)]);
        table.insert(2, vec![SqliteValue::Integer(20)]);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::Count, 0, 1, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.set_database(db);
        engine.set_memdb_rows_loaded(true);
        engine.cursors.insert(0, MemCursor::new(root, false));

        let rows = execute_program_with_engine(&mut engine, &prog);

        assert_eq!(rows, vec![vec![SqliteValue::Integer(2)]]);
    }

    #[test]
    fn test_count_on_readonly_storage_cursor_ignores_stale_memdb_shortcut() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::Count, 0, 1, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_memdb_rows_loaded(true);
        engine.set_storage_cursor_memdb_count_shortcuts_safe(true);
        engine.set_reject_mem_fallback(false);

        assert!(engine.open_storage_cursor(0, root, true));
        engine.cursor_root_pages.insert(0, root);
        {
            let sc = engine
                .storage_cursors
                .get_mut(&0)
                .expect("storage cursor should exist");
            sc.cursor
                .table_insert(&sc.cx, 1, &encode_record(&[SqliteValue::Integer(10)]))
                .unwrap();
            sc.cursor
                .table_insert(&sc.cx, 2, &encode_record(&[SqliteValue::Integer(20)]))
                .unwrap();
            sc.writable = false;
        }

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        let rows: Vec<_> = engine
            .take_results()
            .into_iter()
            .map(|row| row.into_vec())
            .collect();
        assert_eq!(rows, vec![vec![SqliteValue::Integer(2)]]);

        let db = engine.take_database().expect("database should exist");
        let table = db.get_table(root).expect("table should exist");
        assert_eq!(table.rows.len(), 0, "attached MemDatabase stays stale here");
    }

    // ── bd-3iw8 / bd-25c6: Storage cursor WRITE path tests ────────────

    /// Build and execute a write program with storage cursors enabled.
    /// Returns both the result rows and the final MemDatabase state.
    fn run_write_with_storage_cursors(
        db: MemDatabase,
        build: impl FnOnce(&mut ProgramBuilder),
    ) -> (Vec<Vec<SqliteValue>>, MemDatabase) {
        let mut b = ProgramBuilder::new();
        build(&mut b);
        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        // These tests exercise the MemPageStore path without a real pager txn.
        engine.set_reject_mem_fallback(false);
        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        let results: Vec<_> = engine
            .take_results()
            .into_iter()
            .map(|v| v.into_vec())
            .collect();
        let db = engine.take_database().expect("database should exist");
        (results, db)
    }

    #[test]
    fn test_openwrite_uses_storage_cursor_backend() {
        // Verify OpenWrite routes through StorageCursor when enabled.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(1, vec![SqliteValue::Integer(100)]);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        // Verify the cursor was opened as a storage cursor, not a MemCursor.
        assert!(
            engine.storage_cursors.contains_key(&0),
            "OpenWrite should route through StorageCursor"
        );
        assert!(!engine.cursors.contains_key(&0));
        // Verify it's marked writable.
        assert!(
            engine
                .storage_cursors
                .get(&0)
                .is_some_and(|cursor| cursor.writable)
        );
    }

    #[test]
    fn test_insert_via_storage_cursor_write_path() {
        // Phase 5B.2 (bd-1yi8): INSERT goes ONLY through StorageCursor
        // (B-tree write path), NOT synced to MemDatabase.
        // Read-back uses the SAME cursor (Rewind) since the MemPageStore
        // is per-cursor and not shared across Close/OpenRead.
        let mut db = MemDatabase::new();
        let root = db.create_table(2);

        let (rows, final_db) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            // OpenWrite cursor 0 on root page.
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(2), 0);

            // NewRowid → r1.
            b.emit_op(Opcode::NewRowid, 0, 1, 0, P4::None, 0);

            // Build record: r2=42, r3="hello" → MakeRecord → r4.
            b.emit_op(Opcode::Integer, 42, 2, 0, P4::None, 0);
            b.emit_op(Opcode::String8, 0, 3, 0, P4::Str("hello".to_owned()), 0);
            b.emit_op(Opcode::MakeRecord, 2, 2, 4, P4::None, 0);

            // Insert(cursor=0, record=r4, rowid=r1).
            b.emit_op(Opcode::Insert, 0, 4, 1, P4::None, 0);

            // Read back via same cursor: Rewind then Column/ResultRow.
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);

            let body = b.current_addr();
            b.emit_op(Opcode::Column, 0, 0, 5, P4::None, 0);
            b.emit_op(Opcode::Column, 0, 1, 6, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 5, 2, 0, P4::None, 0);
            let next_target =
                i32::try_from(body).expect("program counter should fit into i32 for tests");
            b.emit_op(Opcode::Next, 0, next_target, 0, P4::None, 0);

            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        // Write-through: MemDatabase should NOT have the row.
        let table = final_db.get_table(root).expect("table should exist");
        assert_eq!(
            table.rows.len(),
            0,
            "MemDatabase must not be synced in write-through mode"
        );

        // Data readable from B-tree via same cursor.
        assert_eq!(
            rows.len(),
            1,
            "should read back exactly one row from B-tree"
        );
        assert_eq!(rows[0][0], SqliteValue::Integer(42));
        assert_eq!(rows[0][1], SqliteValue::Text("hello".into()));
    }

    #[test]
    fn test_fused_append_insert_honors_make_record_p4_metadata() {
        let mut db = MemDatabase::new();
        let root = db.create_table(2);

        let (rows, _) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(2), 0);
            b.emit_op(Opcode::Integer, 42, 2, 0, P4::None, 0);
            b.emit_op(Opcode::String8, 0, 3, 0, P4::Str("fused".to_owned()), 0);
            b.emit_op(
                Opcode::FusedAppendInsert,
                0,
                2,
                2,
                P4::Affinity("XD".to_owned()),
                2,
            );

            b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);
            let body = b.current_addr();
            b.emit_op(Opcode::Column, 0, 0, 5, P4::None, 0);
            b.emit_op(Opcode::Column, 0, 1, 6, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 5, 2, 0, P4::None, 0);
            let next_target =
                i32::try_from(body).expect("program counter should fit into i32 for tests");
            b.emit_op(Opcode::Next, 0, next_target, 0, P4::None, 0);

            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows,
            vec![vec![SqliteValue::Null, SqliteValue::Text("fused".into())]],
            "FusedAppendInsert must preserve MakeRecord X-affinity placeholder semantics",
        );
    }

    #[test]
    fn test_fused_append_insert_honors_precomputed_record_header() {
        let precomputed = fsqlite_types::record::PrecomputedRecordHeader::new(&[
            fsqlite_types::record::PrecomputedSerialTypeKind::NullPlaceholder,
            fsqlite_types::record::PrecomputedSerialTypeKind::IntegerOrNull,
        ]);
        let mut db = MemDatabase::new();
        let root = db.create_table(2);

        let (rows, _) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(2), 0);
            b.emit_op(Opcode::Integer, 99, 2, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 7, 3, 0, P4::None, 0);
            b.emit_op(
                Opcode::FusedAppendInsert,
                0,
                2,
                2,
                P4::PrecomputedHeader(precomputed),
                2,
            );

            b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);
            let body = b.current_addr();
            b.emit_op(Opcode::Column, 0, 0, 5, P4::None, 0);
            b.emit_op(Opcode::Column, 0, 1, 6, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 5, 2, 0, P4::None, 0);
            let next_target =
                i32::try_from(body).expect("program counter should fit into i32 for tests");
            b.emit_op(Opcode::Next, 0, next_target, 0, P4::None, 0);

            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows,
            vec![vec![SqliteValue::Null, SqliteValue::Integer(7)]],
            "FusedAppendInsert must preserve MakeRecord precomputed-header placeholders",
        );
    }

    #[test]
    fn test_delete_via_storage_cursor_write_path() {
        // Insert a row into MemDatabase, open a writable StorageCursor,
        // position on it, delete it, and verify data is removed from the
        // B-tree while MemDatabase remains unchanged (write-through mode).
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(1, vec![SqliteValue::Integer(10)]);
        table.insert(2, vec![SqliteValue::Integer(20)]);
        table.insert(3, vec![SqliteValue::Integer(30)]);

        let (rows, final_db) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            // Open writable cursor.
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);

            // Seek to rowid=2 (register 1). Jump to end if not found.
            b.emit_op(Opcode::Integer, 2, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SeekRowid, 0, 1, done, P4::None, 0);

            // Delete the current row.
            b.emit_op(Opcode::Delete, 0, 0, 0, P4::None, 0);

            // Read back rowids from B-tree to verify rowid=2 was deleted.
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);
            let body = b.current_addr();
            b.emit_op(Opcode::Rowid, 0, 2, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 2, 1, 0, P4::None, 0);
            let next_target =
                i32::try_from(body).expect("program counter should fit into i32 for tests");
            b.emit_op(Opcode::Next, 0, next_target, 0, P4::None, 0);

            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows,
            vec![vec![SqliteValue::Integer(1)], vec![SqliteValue::Integer(3)],],
            "B-tree cursor should observe rowid=2 deleted"
        );

        // MemDatabase should remain unchanged in write-through mode.
        let table = final_db.get_table(root).expect("table should exist");
        assert_eq!(table.rows.len(), 3);
        let rowids: Vec<i64> = table.rows.iter().map(|r| r.rowid).collect();
        assert!(rowids.contains(&1));
        assert!(rowids.contains(&2));
        assert!(rowids.contains(&3));
    }

    #[test]
    fn test_delete_invalidates_cached_storage_row_before_successor_column() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(1, vec![SqliteValue::Integer(10)]);
        table.insert(2, vec![SqliteValue::Integer(20)]);
        table.insert(3, vec![SqliteValue::Integer(30)]);

        let (rows, _) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);

            // Prime the per-row column cache on rowid=1/value=10.
            b.emit_op(Opcode::Column, 0, 0, 1, P4::None, 0);

            // Delete the current row. The cursor now lands on the successor.
            b.emit_op(Opcode::Delete, 0, 0, 0, P4::None, 0);

            // Without cache invalidation this would incorrectly return 10 again.
            b.emit_op(Opcode::Column, 0, 0, 2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 2, 1, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Integer(20)]]);
    }

    #[test]
    fn test_storage_cursor_delete_write_boundary_invalidates_decode_cache_once() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_metrics_enabled = vdbe_metrics_enabled();
        reset_vdbe_metrics();
        set_vdbe_metrics_enabled(true);

        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(1, vec![SqliteValue::Integer(10)]);
        table.insert(2, vec![SqliteValue::Integer(20)]);

        let before = vdbe_metrics_snapshot();
        let (rows, _) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);

            // Prime the cache on the current row, then mutate through the same
            // cursor and read the successor. The stale row image must not survive
            // across the write boundary.
            b.emit_op(Opcode::Column, 0, 0, 1, P4::None, 0);
            b.emit_op(Opcode::Delete, 0, 0, 0, P4::None, 0);
            b.emit_op(Opcode::Column, 0, 0, 2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 2, 1, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        let after = vdbe_metrics_snapshot();

        assert_eq!(rows, vec![vec![SqliteValue::Integer(20)]]);
        assert_eq!(
            after.decode_cache_invalidations_write_total
                - before.decode_cache_invalidations_write_total,
            1
        );
        assert_eq!(
            after.decode_cache_invalidations_position_total
                - before.decode_cache_invalidations_position_total,
            0
        );
        assert_eq!(
            after.decode_cache_hits_total - before.decode_cache_hits_total,
            0
        );
        assert_eq!(
            after.decode_cache_misses_total - before.decode_cache_misses_total,
            2
        );

        set_vdbe_metrics_enabled(prev_metrics_enabled);
    }

    #[test]
    fn test_delete_then_prev_then_next_advances_correctly() {
        // Regression: after Delete marks pending_next_after_delete, a
        // subsequent Prev must clear that pending state. Otherwise the next
        // Next call can incorrectly "stay put" and repeat the same row.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(1, vec![SqliteValue::Integer(10)]);
        table.insert(2, vec![SqliteValue::Integer(20)]);
        table.insert(3, vec![SqliteValue::Integer(30)]);

        let (rows, _) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);

            // Seek rowid=2 and delete it. Cursor should land on successor.
            b.emit_op(Opcode::Integer, 2, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SeekRowid, 0, 1, done, P4::None, 0);
            b.emit_op(Opcode::Delete, 0, 0, 0, P4::None, 0);

            // Step backward once (to rowid=1) and emit it.
            let prev_ok = b.emit_label();
            b.emit_jump_to_label(Opcode::Prev, 0, 0, prev_ok, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(prev_ok);
            b.emit_op(Opcode::Rowid, 0, 2, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 2, 1, 0, P4::None, 0);

            // Now step forward once. Correct behavior is rowid=3 (not 1).
            let next_ok = b.emit_label();
            b.emit_jump_to_label(Opcode::Next, 0, 0, next_ok, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(next_ok);
            b.emit_op(Opcode::Rowid, 0, 3, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 3, 1, 0, P4::None, 0);

            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows,
            vec![vec![SqliteValue::Integer(1)], vec![SqliteValue::Integer(3)]],
            "Delete->Prev->Next should land on rowids 1 then 3 without repeating row 1"
        );
    }

    #[test]
    fn test_delete_then_notexists_then_next_advances_from_probe_position() {
        // Regression: NotExists on storage cursor repositions via table_move_to.
        // If pending_next_after_delete is left stale, a following Next can
        // incorrectly repeat the probe row instead of advancing.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(1, vec![SqliteValue::Integer(10)]);
        table.insert(2, vec![SqliteValue::Integer(20)]);
        table.insert(3, vec![SqliteValue::Integer(30)]);

        let (rows, _) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);

            // Delete rowid=2.
            b.emit_op(Opcode::Integer, 2, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SeekRowid, 0, 1, done, P4::None, 0);
            b.emit_op(Opcode::Delete, 0, 0, 0, P4::None, 0);

            // Probe rowid=1 via NotExists (falls through when row exists).
            let probe_missing = b.emit_label();
            b.emit_op(Opcode::Integer, 1, 2, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::NotExists, 0, 2, probe_missing, P4::None, 0);

            // Emit current probe position (rowid=1).
            b.emit_op(Opcode::Rowid, 0, 3, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 3, 1, 0, P4::None, 0);

            // Next should advance to rowid=3 (not repeat rowid=1).
            let next_ok = b.emit_label();
            b.emit_jump_to_label(Opcode::Next, 0, 0, next_ok, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(next_ok);
            b.emit_op(Opcode::Rowid, 0, 4, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 4, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);

            // Missing-probe path (not expected in this fixture).
            b.resolve_label(probe_missing);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);

            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows,
            vec![vec![SqliteValue::Integer(1)], vec![SqliteValue::Integer(3)]],
            "Delete->NotExists->Next should advance from probe row 1 to row 3"
        );
    }

    #[test]
    fn test_delete_then_newrowid_then_next_reports_no_successor() {
        // Regression: NewRowid on storage cursor repositions with `last()`.
        // If pending_next_after_delete is stale, Next can incorrectly report
        // a successor row when already at the end.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(1, vec![SqliteValue::Integer(10)]);
        table.insert(2, vec![SqliteValue::Integer(20)]);
        table.insert(3, vec![SqliteValue::Integer(30)]);

        let (rows, _) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);

            // Delete rowid=2 (cursor lands on successor rowid=3).
            b.emit_op(Opcode::Integer, 2, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SeekRowid, 0, 1, done, P4::None, 0);
            b.emit_op(Opcode::Delete, 0, 0, 0, P4::None, 0);

            // NewRowid probes max rowid via last(); this should clear stale
            // pending delete state for cursor 0.
            b.emit_op(Opcode::NewRowid, 0, 2, 0, P4::None, 0);

            // At end of table, Next must report no successor.
            let has_next = b.emit_label();
            b.emit_jump_to_label(Opcode::Next, 0, 0, has_next, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, 3, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 3, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);

            // Unexpected path if stale pending state causes false positive.
            b.resolve_label(has_next);
            b.emit_op(Opcode::Integer, 1, 3, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 3, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);

            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows,
            vec![vec![SqliteValue::Integer(0)]],
            "Delete->NewRowid->Next should report no successor at end-of-table"
        );
    }

    #[test]
    fn test_newrowid_with_storage_cursor_allocates_correctly() {
        // Verify NewRowid allocates sequential rowids when using storage cursors.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(5, vec![SqliteValue::Integer(50)]);

        let (rows, _) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);

            // Allocate two new rowids and output them.
            b.emit_op(Opcode::NewRowid, 0, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::NewRowid, 0, 2, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 2, 1, 0, P4::None, 0);

            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        // The table had rowid 5 → next_rowid should be 6, then 7.
        assert_eq!(rows.len(), 2);
        assert_eq!(rows[0][0], SqliteValue::Integer(6));
        assert_eq!(rows[1][0], SqliteValue::Integer(7));
    }

    #[test]
    fn test_newrowid_concurrent_flag_uses_snapshot_independent_path() {
        // Phase 5B.2 (bd-1yi8): with storage cursors, NewRowid reads max
        // rowid from B-tree regardless of p3 (concurrent flag). The p3
        // flag only affects the MemDatabase fallback (Phase 4 cursors).
        fn setup_db_with_stale_counter() -> (MemDatabase, i32) {
            let mut db = MemDatabase::new();
            let root = db.create_table(1);
            let table = db.get_table_mut(root).expect("table should exist");
            table.insert(10, vec![SqliteValue::Integer(10)]);
            table.insert(11, vec![SqliteValue::Integer(11)]);
            // Simulate stale local counter state from an old snapshot.
            table.next_rowid = 1;
            (db, root)
        }

        let (db_serialized, root) = setup_db_with_stale_counter();
        let (rows_serialized, _) = run_write_with_storage_cursors(db_serialized, |b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
            // Serialized path (`p3 = 0`) — with storage cursors, reads
            // max rowid from B-tree (11), returns 12.
            b.emit_op(Opcode::NewRowid, 0, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        let (db_concurrent, root) = setup_db_with_stale_counter();
        let (rows_concurrent, _) = run_write_with_storage_cursors(db_concurrent, |b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
            // Concurrent path (`p3 != 0`) — same B-tree path, same result.
            b.emit_op(Opcode::NewRowid, 0, 1, 1, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        // Both paths read max rowid (11) from B-tree → return 12.
        assert_eq!(rows_serialized, vec![vec![SqliteValue::Integer(12)]]);
        assert_eq!(rows_concurrent, vec![vec![SqliteValue::Integer(12)]]);
    }

    #[test]
    fn test_memdb_concurrent_rowid_uses_visible_max_not_stale_counter() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).expect("table should exist");
        table.insert(10, vec![SqliteValue::Integer(10)]);
        table.insert(11, vec![SqliteValue::Integer(11)]);
        assert!(table.delete_by_rowid(11), "max rowid should be removed");
        table.next_rowid = 1;

        let rowid = db.alloc_rowid_concurrent(root);
        assert_eq!(rowid, 11, "concurrent rowid should reuse max_visible+1");
        assert_eq!(
            db.get_table(root)
                .expect("table should exist")
                .next_rowid_hint(),
            12,
            "next_rowid should advance from the visible max"
        );
    }

    #[test]
    fn test_memdb_delete_rowid_records_undo() {
        let mut db = MemDatabase::new();
        let root = db.create_table(2);
        db.get_table_mut(root)
            .expect("table should exist")
            .add_unique_column_group(vec![0]);
        db.upsert_row(
            root,
            1,
            vec![SqliteValue::Integer(10), SqliteValue::Integer(100)],
        );

        db.begin_undo();
        let token = db.undo_version();
        assert!(db.delete_rowid(root, 1), "delete should find the row");
        let table = db.get_table(root).expect("table should exist");
        assert_eq!(table.row_values_by_rowid(1), None);
        assert!(
            table
                .find_unique_conflicts(&[SqliteValue::Integer(10), SqliteValue::Integer(999)])
                .is_empty(),
            "delete should remove the unique-index entry"
        );

        db.rollback_to(token);
        let table = db.get_table(root).expect("table should exist");
        let restored = vec![SqliteValue::Integer(10), SqliteValue::Integer(100)];
        assert_eq!(table.row_values_by_rowid(1), Some(restored.as_slice()));
        assert_eq!(
            table.find_unique_conflicts(&[SqliteValue::Integer(10), SqliteValue::Integer(999)]),
            vec![1],
            "rollback should restore the unique-index entry"
        );
    }

    #[test]
    fn test_memdb_insert_auto_row_records_undo_and_patches_rowid_alias() {
        let mut db = MemDatabase::new();
        let root = db.create_table(2);
        db.upsert_row(
            root,
            5,
            vec![SqliteValue::Integer(5), SqliteValue::Text("old".into())],
        );

        db.begin_undo();
        let token = db.undo_version();
        let rowid = db
            .insert_auto_row(
                root,
                vec![SqliteValue::Null, SqliteValue::Text("new".into())],
                Some(0),
            )
            .expect("table should exist");
        assert_eq!(rowid, 6);
        let table = db.get_table(root).expect("table should exist");
        assert_eq!(table.next_rowid_hint(), 7);
        assert_eq!(
            table.row_values_by_rowid(6),
            Some([SqliteValue::Integer(6), SqliteValue::Text("new".into())].as_slice())
        );

        db.rollback_to(token);
        let table = db.get_table(root).expect("table should exist");
        assert_eq!(table.next_rowid_hint(), 6);
        assert_eq!(table.row_values_by_rowid(6), None);
        assert_eq!(
            table.row_values_by_rowid(5),
            Some([SqliteValue::Integer(5), SqliteValue::Text("old".into())].as_slice())
        );
    }

    #[test]
    fn test_memdb_replace_secondary_unique_rollback_restores_deleted_and_replaced_rows() {
        let mut db = MemDatabase::new();
        let root = db.create_table(2);
        db.get_table_mut(root)
            .expect("table should exist")
            .add_unique_column_group(vec![1]);
        db.upsert_row(
            root,
            1,
            vec![SqliteValue::Integer(1), SqliteValue::Text("alpha".into())],
        );
        db.upsert_row(
            root,
            2,
            vec![SqliteValue::Integer(2), SqliteValue::Text("beta".into())],
        );

        db.begin_undo();
        let token = db.undo_version();
        assert!(
            db.delete_rowid(root, 1),
            "REPLACE should delete the secondary-unique conflict"
        );
        db.upsert_row(
            root,
            2,
            vec![SqliteValue::Integer(2), SqliteValue::Text("alpha".into())],
        );
        let table = db.get_table(root).expect("table should exist");
        assert_eq!(table.row_values_by_rowid(1), None);
        assert_eq!(
            table.row_values_by_rowid(2),
            Some([SqliteValue::Integer(2), SqliteValue::Text("alpha".into())].as_slice())
        );
        assert_eq!(
            table.find_unique_conflicts(&[
                SqliteValue::Integer(99),
                SqliteValue::Text("alpha".into())
            ]),
            vec![2]
        );

        db.rollback_to(token);
        let table = db.get_table(root).expect("table should exist");
        assert_eq!(table.next_rowid_hint(), 3);
        assert_eq!(
            table.row_values_by_rowid(1),
            Some([SqliteValue::Integer(1), SqliteValue::Text("alpha".into())].as_slice())
        );
        assert_eq!(
            table.row_values_by_rowid(2),
            Some([SqliteValue::Integer(2), SqliteValue::Text("beta".into())].as_slice())
        );
        assert_eq!(
            table.find_unique_conflicts(&[
                SqliteValue::Integer(99),
                SqliteValue::Text("alpha".into())
            ]),
            vec![1]
        );
        assert_eq!(
            table.find_unique_conflicts(&[
                SqliteValue::Integer(99),
                SqliteValue::Text("beta".into())
            ]),
            vec![2]
        );
    }

    #[test]
    fn test_memtable_rowid_lookup_uses_dense_offset_without_breaking_sparse_rows() {
        let mut table = MemTable::new(1);
        table.insert(10, vec![SqliteValue::Integer(10)]);
        table.insert(11, vec![SqliteValue::Integer(11)]);
        table.insert(12, vec![SqliteValue::Integer(12)]);

        assert_eq!(table.find_by_rowid(11), Some(1));
        assert_eq!(
            table.row_values_by_rowid(11),
            Some(&[SqliteValue::Integer(11)][..])
        );

        assert!(table.delete_by_rowid(11), "middle row should be removed");
        assert_eq!(table.find_by_rowid(11), None);
        assert_eq!(
            table.row_values_by_rowid(12),
            Some(&[SqliteValue::Integer(12)][..]),
            "sparse tables must still fall back to binary search"
        );
    }

    #[test]
    fn test_memtable_rowid_range_bounds_use_dense_offset_with_sparse_fallback() {
        let mut table = MemTable::new(1);
        for rowid in 10..15 {
            table.insert(rowid, vec![SqliteValue::Integer(rowid)]);
        }

        assert_eq!(table.count_rowid_range(11, 14), 3);
        assert_eq!(table.count_rowid_range(i64::MIN, 12), 2);
        assert_eq!(table.count_rowid_range(13, i64::MAX), 2);
        let dense_values = table
            .iter_rows_in_rowid_range(11, 14)
            .map(|(rowid, values)| (rowid, values.first().cloned()))
            .collect::<Vec<_>>();
        assert_eq!(
            dense_values,
            vec![
                (11, Some(SqliteValue::Integer(11))),
                (12, Some(SqliteValue::Integer(12))),
                (13, Some(SqliteValue::Integer(13))),
            ]
        );

        assert!(table.delete_by_rowid(12), "middle row should be removed");
        assert_eq!(table.count_rowid_range(11, 14), 2);
        assert_eq!(table.count_rowid_range(i64::MIN, 12), 2);
        assert_eq!(table.count_rowid_range(13, i64::MAX), 2);
        let sparse_values = table
            .iter_rows_in_rowid_range(11, 14)
            .map(|(rowid, values)| (rowid, values.first().cloned()))
            .collect::<Vec<_>>();
        assert_eq!(
            sparse_values,
            vec![
                (11, Some(SqliteValue::Integer(11))),
                (13, Some(SqliteValue::Integer(13))),
            ]
        );
    }

    #[test]
    fn test_memtable_unique_conflict_index_tracks_insert_delete_and_update() {
        let mut table = MemTable::new(2);
        table.add_unique_column_group(vec![0]);
        table.insert(1, vec![SqliteValue::Integer(10), SqliteValue::Integer(100)]);
        table.insert(2, vec![SqliteValue::Integer(20), SqliteValue::Integer(200)]);

        assert_eq!(
            table.find_unique_conflicts(&[SqliteValue::Integer(10), SqliteValue::Integer(999)]),
            vec![1],
            "lookup should find the conflicting rowid via the unique index"
        );

        assert!(table.delete_by_rowid(1), "delete should succeed");
        assert!(
            table
                .find_unique_conflicts(&[SqliteValue::Integer(10), SqliteValue::Integer(999)])
                .is_empty(),
            "deleted rows must be removed from the unique index"
        );

        table.insert(2, vec![SqliteValue::Integer(10), SqliteValue::Integer(200)]);
        assert_eq!(
            table.find_unique_conflicts(&[SqliteValue::Integer(10), SqliteValue::Integer(999)]),
            vec![2],
            "updates must refresh the unique index entry"
        );

        assert!(
            table
                .find_unique_conflicts(&[SqliteValue::Null, SqliteValue::Integer(999)])
                .is_empty(),
            "NULL keys must remain non-conflicting under SQLite UNIQUE semantics"
        );
    }

    #[test]
    fn test_memtable_unique_conflict_index_honors_nocase_collation() {
        let mut table = MemTable::new(1);
        table.add_unique_column_group_with_collations(vec![0], vec![Some("NOCASE".to_owned())]);
        table.insert(1, vec![SqliteValue::Text("Alice".into())]);

        assert_eq!(
            table.find_unique_conflicts(&[SqliteValue::Text("alice".into())]),
            vec![1],
            "builtin NOCASE unique indexes must normalize case-equivalent text"
        );
    }

    #[test]
    fn test_engine_database_attachment_propagates_custom_collation_registry_for_memtable_uniques() {
        struct DashlessNoCaseCollation;

        impl fsqlite_func::collation::CollationFunction for DashlessNoCaseCollation {
            fn name(&self) -> &str {
                "DASHLESS_NOCASE"
            }

            fn compare(&self, left: &[u8], right: &[u8]) -> Ordering {
                let normalize = |bytes: &[u8]| {
                    bytes
                        .iter()
                        .filter(|&&byte| byte != b'-' && byte != b'_' && byte != b' ')
                        .map(u8::to_ascii_lowercase)
                        .collect::<Vec<_>>()
                };
                normalize(left).cmp(&normalize(right))
            }
        }

        let registry = Arc::new(Mutex::new(CollationRegistry::new()));
        registry
            .lock()
            .unwrap_or_else(|err| err.into_inner())
            .register(DashlessNoCaseCollation);

        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        {
            let table = db.get_table_mut(root).expect("table should exist");
            table.add_unique_column_group_with_collations(
                vec![0],
                vec![Some("DASHLESS_NOCASE".to_owned())],
            );
            table.insert(1, vec![SqliteValue::Text("Alpha-Beta".into())]);
        }

        let mut engine = VdbeEngine::new(1);
        engine.set_database(db);
        engine.set_collation_registry(Arc::clone(&registry));

        let db = engine
            .take_database()
            .expect("database should still be attached");
        let table = db.get_table(root).expect("table should exist");
        assert_eq!(
            table.find_unique_conflicts(&[SqliteValue::Text("alpha beta".into())]),
            vec![1],
            "custom-collation UNIQUE fallback must use the engine-provided registry"
        );
    }

    // ── bd-1yi8: INSERT write-through tests ────────────────────────────

    #[test]
    fn test_insert_write_through_no_memdb_sync() {
        // Verify INSERT with storage cursor does NOT write to MemDatabase.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let (_, final_db) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
            b.emit_op(Opcode::NewRowid, 0, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 99, 2, 0, P4::None, 0);
            b.emit_op(Opcode::MakeRecord, 2, 1, 3, P4::None, 0);
            b.emit_op(Opcode::Insert, 0, 3, 1, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        let table = final_db.get_table(root).expect("table should exist");
        assert_eq!(table.rows.len(), 0, "write-through must skip MemDatabase");
    }

    #[test]
    fn test_insert_write_through_marks_root_page_dirty_for_lazy_memdb_sync() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
        b.emit_op(Opcode::NewRowid, 0, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 99, 2, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, 2, 1, 3, P4::None, 0);
        b.emit_op(Opcode::Insert, 0, 3, 1, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_storage_cursor_memdb_count_shortcuts_safe(true);
        engine.set_reject_mem_fallback(false);

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        assert!(engine.has_dirty_root_pages());
        assert!(engine.dirty_root_pages().contains(&root));
        assert_eq!(engine.dirty_root_pages().len(), 1);
        assert!(
            !engine.storage_cursor_memdb_count_shortcuts_safe(),
            "storage-backed INSERT should leave MemDB fast paths disabled until reload"
        );

        let db = engine.take_database().expect("database should exist");
        let table = db.get_table(root).expect("table should exist");
        assert_eq!(
            table.rows.len(),
            0,
            "write-through must keep MemDatabase stale"
        );
    }

    #[test]
    fn test_lazy_dirty_flag_set_on_insert() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
        b.emit_op(Opcode::NewRowid, 0, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 77, 2, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, 2, 1, 3, P4::None, 0);
        b.emit_op(Opcode::Insert, 0, 3, 1, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_storage_cursor_memdb_count_shortcuts_safe(true);
        engine.set_reject_mem_fallback(false);

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        assert!(engine.has_dirty_root_pages());
        assert!(engine.dirty_root_pages().contains(&root));
        assert_eq!(engine.dirty_root_pages().len(), 1);
        assert!(
            !engine.storage_cursor_memdb_count_shortcuts_safe(),
            "storage-backed INSERT must mark the MemDatabase mirror stale"
        );

        let db = engine.take_database().expect("database should exist");
        let table = db.get_table(root).expect("table should exist");
        assert_eq!(
            table.rows.len(),
            0,
            "lazy dirty tracking must leave MemDatabase row mirroring deferred"
        );
    }

    #[test]
    fn test_lazy_dirty_read_from_btree() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
        b.emit_op(Opcode::NewRowid, 0, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 41, 2, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, 2, 1, 3, P4::None, 0);
        b.emit_op(Opcode::Insert, 0, 3, 1, P4::None, 0);
        b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);
        let body = b.current_addr();
        b.emit_op(Opcode::Column, 0, 0, 4, P4::None, 0);
        b.emit_op(Opcode::ResultRow, 4, 1, 0, P4::None, 0);
        let next_target =
            i32::try_from(body).expect("program counter should fit into i32 for tests");
        b.emit_op(Opcode::Next, 0, next_target, 0, P4::None, 0);
        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_storage_cursor_memdb_count_shortcuts_safe(true);
        engine.set_reject_mem_fallback(false);

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        let rows: Vec<_> = engine
            .take_results()
            .into_iter()
            .map(|row| row.into_vec())
            .collect();
        assert_eq!(
            rows,
            vec![vec![SqliteValue::Integer(41)]],
            "same-statement reads must come from the storage-backed B-tree when the MemDatabase mirror is stale"
        );
        assert!(engine.has_dirty_root_pages());
        assert!(engine.dirty_root_pages().contains(&root));

        let db = engine.take_database().expect("database should exist");
        let table = db.get_table(root).expect("table should exist");
        assert_eq!(
            table.rows.len(),
            0,
            "B-tree fallback reads must not eagerly repopulate the stale MemDatabase mirror"
        );
    }

    #[test]
    fn test_lazy_dirty_clean_table_scan_then_dirty_table_fallback() {
        let mut db = MemDatabase::new();
        let dirty_root = db.create_table(1);
        let clean_root = db.create_table(1);
        db.get_table_mut(clean_root)
            .expect("clean table should exist")
            .insert(1, vec![SqliteValue::Integer(7)]);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let clean_done = b.emit_label();
        let dirty_done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenWrite, 0, dirty_root, 0, P4::Int(1), 0);
        b.emit_op(Opcode::OpenRead, 1, clean_root, 0, P4::Int(1), 0);

        b.emit_op(Opcode::NewRowid, 0, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 41, 2, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, 2, 1, 3, P4::None, 0);
        b.emit_op(Opcode::Insert, 0, 3, 1, P4::None, 0);

        b.emit_jump_to_label(Opcode::Rewind, 1, 0, clean_done, P4::None, 0);
        let clean_body = b.current_addr();
        b.emit_op(Opcode::Column, 1, 0, 4, P4::None, 0);
        b.emit_op(Opcode::ResultRow, 4, 1, 0, P4::None, 0);
        let clean_next =
            i32::try_from(clean_body).expect("program counter should fit into i32 for tests");
        b.emit_op(Opcode::Next, 1, clean_next, 0, P4::None, 0);
        b.resolve_label(clean_done);

        b.emit_jump_to_label(Opcode::Rewind, 0, 0, dirty_done, P4::None, 0);
        let dirty_body = b.current_addr();
        b.emit_op(Opcode::Column, 0, 0, 5, P4::None, 0);
        b.emit_op(Opcode::ResultRow, 5, 1, 0, P4::None, 0);
        let dirty_next =
            i32::try_from(dirty_body).expect("program counter should fit into i32 for tests");
        b.emit_op(Opcode::Next, 0, dirty_next, 0, P4::None, 0);
        b.resolve_label(dirty_done);

        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_storage_cursor_memdb_count_shortcuts_safe(true);
        engine.set_reject_mem_fallback(false);

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        let rows: Vec<_> = engine
            .take_results()
            .into_iter()
            .map(|row| row.into_vec())
            .collect();
        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Integer(7)],
                vec![SqliteValue::Integer(41)],
            ],
            "same-statement reads should preserve clean-table visibility while still sourcing dirty-table rows from the storage-backed B-tree"
        );
        assert!(engine.has_dirty_root_pages());
        assert!(engine.dirty_root_pages().contains(&dirty_root));
        assert!(
            !engine.dirty_root_pages().contains(&clean_root),
            "reading a clean table must not mark it dirty"
        );
        assert_eq!(
            engine.dirty_root_pages().len(),
            1,
            "only the written table should remain dirty"
        );

        let db = engine.take_database().expect("database should exist");
        assert_eq!(
            db.get_table(dirty_root)
                .expect("dirty table should exist")
                .rows
                .len(),
            0,
            "lazy dirty tracking must keep the written table stale in the MemDatabase mirror"
        );
        assert_eq!(
            db.get_table(clean_root)
                .expect("clean table should exist")
                .rows
                .len(),
            1,
            "clean-table reads must leave the untouched MemDatabase mirror intact"
        );
    }

    #[test]
    fn test_lazy_dirty_multiple_tables() {
        let mut db = MemDatabase::new();
        let root_a = db.create_table(1);
        let root_b = db.create_table(1);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenWrite, 0, root_a, 0, P4::Int(1), 0);
        b.emit_op(Opcode::OpenWrite, 1, root_b, 0, P4::Int(1), 0);

        b.emit_op(Opcode::NewRowid, 0, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 10, 2, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, 2, 1, 3, P4::None, 0);
        b.emit_op(Opcode::Insert, 0, 3, 1, P4::None, 0);

        b.emit_op(Opcode::NewRowid, 1, 4, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 20, 5, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, 5, 1, 6, P4::None, 0);
        b.emit_op(Opcode::Insert, 1, 6, 4, P4::None, 0);

        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_storage_cursor_memdb_count_shortcuts_safe(true);
        engine.set_reject_mem_fallback(false);

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        assert!(engine.has_dirty_root_pages());
        assert!(engine.dirty_root_pages().contains(&root_a));
        assert!(engine.dirty_root_pages().contains(&root_b));
        assert_eq!(
            engine.dirty_root_pages().len(),
            2,
            "lazy dirty tracking must record every dirty table root separately"
        );

        let db = engine.take_database().expect("database should exist");
        assert_eq!(
            db.get_table(root_a)
                .expect("table A should exist")
                .rows
                .len(),
            0,
            "table A should remain stale in MemDatabase until a later reload"
        );
        assert_eq!(
            db.get_table(root_b)
                .expect("table B should exist")
                .rows
                .len(),
            0,
            "table B should remain stale in MemDatabase until a later reload"
        );
    }

    #[test]
    fn test_lazy_dirty_update_after_insert() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);

        b.emit_op(Opcode::Integer, 1, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 10, 2, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, 2, 1, 3, P4::None, 0);
        b.emit_op(Opcode::Insert, 0, 3, 1, P4::None, 0);

        b.emit_op(Opcode::Integer, 99, 4, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, 4, 1, 5, P4::None, 0);
        b.emit_op(Opcode::Insert, 0, 5, 1, P4::None, 5);

        b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);
        let body = b.current_addr();
        b.emit_op(Opcode::Column, 0, 0, 6, P4::None, 0);
        b.emit_op(Opcode::ResultRow, 6, 1, 0, P4::None, 0);
        let next_target =
            i32::try_from(body).expect("program counter should fit into i32 for tests");
        b.emit_op(Opcode::Next, 0, next_target, 0, P4::None, 0);
        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_storage_cursor_memdb_count_shortcuts_safe(true);
        engine.set_reject_mem_fallback(false);

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        let rows: Vec<_> = engine
            .take_results()
            .into_iter()
            .map(|row| row.into_vec())
            .collect();
        assert_eq!(
            rows,
            vec![vec![SqliteValue::Integer(99)]],
            "a later storage-backed REPLACE must be visible through the B-tree fallback even while the MemDatabase mirror remains stale"
        );
        assert!(engine.has_dirty_root_pages());
        assert!(engine.dirty_root_pages().contains(&root));
        assert_eq!(engine.dirty_root_pages().len(), 1);

        let db = engine.take_database().expect("database should exist");
        let table = db.get_table(root).expect("table should exist");
        assert_eq!(
            table.rows.len(),
            0,
            "lazy dirty tracking must not eagerly maintain the MemDatabase row mirror across update-like writes"
        );
    }

    #[test]
    fn test_lazy_dirty_delete_after_insert() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let after_delete = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);

        b.emit_op(Opcode::Integer, 1, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 10, 2, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, 2, 1, 3, P4::None, 0);
        b.emit_op(Opcode::Insert, 0, 3, 1, P4::None, 0);

        b.emit_op(Opcode::Integer, 1, 4, 0, P4::None, 0);
        b.emit_jump_to_label(Opcode::SeekRowid, 0, 4, after_delete, P4::None, 0);
        b.emit_op(Opcode::Delete, 0, 0, 0, P4::None, 0);
        b.resolve_label(after_delete);

        b.emit_op(Opcode::Count, 0, 5, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, 5, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_storage_cursor_memdb_count_shortcuts_safe(true);
        engine.set_reject_mem_fallback(false);

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        let rows: Vec<_> = engine
            .take_results()
            .into_iter()
            .map(|row| row.into_vec())
            .collect();
        assert_eq!(
            rows,
            vec![vec![SqliteValue::Integer(0)]],
            "storage-backed COUNT must observe the delete even while MemDatabase mirroring is deferred"
        );
        assert!(engine.has_dirty_root_pages());
        assert!(engine.dirty_root_pages().contains(&root));
        assert_eq!(engine.dirty_root_pages().len(), 1);

        let db = engine.take_database().expect("database should exist");
        let table = db.get_table(root).expect("table should exist");
        assert_eq!(
            table.rows.len(),
            0,
            "lazy dirty tracking must leave the MemDatabase empty until a later bulk reload"
        );
    }

    #[test]
    fn test_insert_new_rowid_from_btree() {
        // Verify NewRowid reads max from B-tree, not MemDatabase counter.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        // Insert rows 1..=3 into MemTable (these get copied to B-tree at
        // cursor open time via MemPageStore fallback).
        table.insert(1, vec![SqliteValue::Integer(10)]);
        table.insert(2, vec![SqliteValue::Integer(20)]);
        table.insert(3, vec![SqliteValue::Integer(30)]);
        // Reset counter to simulate stale state.
        table.next_rowid = 1;

        let (rows, _) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
            b.emit_op(Opcode::NewRowid, 0, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        // B-tree max rowid is 3 → should return 4, NOT 1.
        assert_eq!(rows, vec![vec![SqliteValue::Integer(4)]]);
    }

    #[test]
    fn test_insert_multiple_rows_write_through() {
        // Insert multiple rows via B-tree and read them all back.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let (rows, final_db) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);

            // Insert row 1: value=100
            b.emit_op(Opcode::NewRowid, 0, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 100, 2, 0, P4::None, 0);
            b.emit_op(Opcode::MakeRecord, 2, 1, 3, P4::None, 0);
            b.emit_op(Opcode::Insert, 0, 3, 1, P4::None, 0);

            // Insert row 2: value=200
            b.emit_op(Opcode::NewRowid, 0, 4, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 200, 5, 0, P4::None, 0);
            b.emit_op(Opcode::MakeRecord, 5, 1, 6, P4::None, 0);
            b.emit_op(Opcode::Insert, 0, 6, 4, P4::None, 0);

            // Insert row 3: value=300
            b.emit_op(Opcode::NewRowid, 0, 7, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 300, 8, 0, P4::None, 0);
            b.emit_op(Opcode::MakeRecord, 8, 1, 9, P4::None, 0);
            b.emit_op(Opcode::Insert, 0, 9, 7, P4::None, 0);

            // Read back via Rewind/Column/Next loop.
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);
            let body = b.current_addr();
            b.emit_op(Opcode::Column, 0, 0, 10, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 10, 1, 0, P4::None, 0);
            let next_target =
                i32::try_from(body).expect("program counter should fit into i32 for tests");
            b.emit_op(Opcode::Next, 0, next_target, 0, P4::None, 0);

            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        // MemDatabase should be empty (write-through).
        let table = final_db.get_table(root).expect("table should exist");
        assert_eq!(table.rows.len(), 0);

        // All 3 rows readable from B-tree.
        assert_eq!(rows.len(), 3);
        assert_eq!(rows[0][0], SqliteValue::Integer(100));
        assert_eq!(rows[1][0], SqliteValue::Integer(200));
        assert_eq!(rows[2][0], SqliteValue::Integer(300));
    }

    #[test]
    fn test_insert_replace_upsert_via_btree() {
        // Insert same rowid twice with OE_REPLACE — second insert should overwrite.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let (rows, _) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);

            // Insert rowid=1 with value=10.
            b.emit_op(Opcode::Integer, 1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 10, 2, 0, P4::None, 0);
            b.emit_op(Opcode::MakeRecord, 2, 1, 3, P4::None, 0);
            b.emit_op(Opcode::Insert, 0, 3, 1, P4::None, 0);

            // Insert rowid=1 again with value=99 (OE_REPLACE upsert).
            b.emit_op(Opcode::Integer, 99, 4, 0, P4::None, 0);
            b.emit_op(Opcode::MakeRecord, 4, 1, 5, P4::None, 0);
            b.emit_op(Opcode::Insert, 0, 5, 1, P4::None, 5);

            // Read back.
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);
            let body = b.current_addr();
            b.emit_op(Opcode::Column, 0, 0, 6, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 6, 1, 0, P4::None, 0);
            let next_target =
                i32::try_from(body).expect("program counter should fit into i32 for tests");
            b.emit_op(Opcode::Next, 0, next_target, 0, P4::None, 0);

            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        // Only one row with the updated value.
        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0][0], SqliteValue::Integer(99));
    }

    #[test]
    fn test_native_replace_row_deletes_conflict_victim_from_memdb_row_mirror() {
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let mut db = MemDatabase::new();
        let table_root = db.create_table(1);
        let index_root = 256;
        let table = db
            .get_table_mut(table_root)
            .expect("table mirror should exist");
        table.insert(1, vec![SqliteValue::Text("alice@example.com".into())]);
        table.insert(2, vec![SqliteValue::Text("bob@example.com".into())]);
        table.insert(3, vec![SqliteValue::Text("alice@example.com".into())]);

        let mut engine = VdbeEngine::new(8);
        engine.set_database(db);
        engine.set_memdb_rows_loaded(true);
        engine.set_storage_cursor_memdb_count_shortcuts_safe(true);
        engine.set_transaction(txn);
        engine.table_index_meta = Arc::new(HashMap::from([(
            0,
            vec![IndexCursorMeta {
                cursor_id: 1,
                column_indices: vec![0],
            }]
            .into_boxed_slice(),
        )]));

        assert!(engine.open_storage_cursor(0, table_root, true));
        assert!(engine.open_storage_cursor(1, index_root, true));
        engine.cursor_root_pages.insert(0, table_root);
        engine.cursor_root_pages.insert(1, index_root);

        let row1 = encode_record(&[SqliteValue::Text("alice@example.com".into())]);
        let row2 = encode_record(&[SqliteValue::Text("bob@example.com".into())]);
        let row3 = encode_record(&[SqliteValue::Text("alice@example.com".into())]);
        let idx1 = encode_record(&[
            SqliteValue::Text("alice@example.com".into()),
            SqliteValue::Integer(1),
        ]);
        let idx2 = encode_record(&[
            SqliteValue::Text("bob@example.com".into()),
            SqliteValue::Integer(2),
        ]);

        {
            let table_cursor = engine
                .storage_cursors
                .get_mut(&0)
                .expect("table storage cursor should exist");
            table_cursor
                .cursor
                .table_insert(&table_cursor.cx, 1, &row1)
                .unwrap();
            table_cursor
                .cursor
                .table_insert(&table_cursor.cx, 2, &row2)
                .unwrap();
            table_cursor
                .cursor
                .table_insert(&table_cursor.cx, 3, &row3)
                .unwrap();
        }
        {
            let index_cursor = engine
                .storage_cursors
                .get_mut(&1)
                .expect("index storage cursor should exist");
            index_cursor
                .cursor
                .index_insert(&index_cursor.cx, &idx1)
                .unwrap();
            index_cursor
                .cursor
                .index_insert(&index_cursor.cx, &idx2)
                .unwrap();
        }

        engine.native_replace_row(0, 1).unwrap();

        let mirrored_table = engine
            .db
            .as_ref()
            .and_then(|db| db.get_table(table_root))
            .expect("mirrored table should still exist");
        let mirrored_rowids: Vec<i64> = mirrored_table.rows.iter().map(|row| row.rowid).collect();
        assert_eq!(
            mirrored_rowids,
            vec![1, 2, 3],
            "lazy dirty tracking should leave the MemDB mirror stale until the connection bulk-syncs it"
        );
        assert!(engine.has_dirty_root_pages());
        assert!(engine.dirty_root_pages().contains(&table_root));
        assert!(
            !engine.storage_cursor_memdb_count_shortcuts_safe(),
            "lazy delete mirroring must disable MemDB fast paths until reload"
        );

        let index_cursor = engine
            .storage_cursors
            .get_mut(&1)
            .expect("index storage cursor should still exist");
        assert!(
            !index_cursor
                .cursor
                .index_move_to(&index_cursor.cx, &idx1)
                .unwrap()
                .is_found(),
            "conflicting unique index entry should be removed from storage"
        );
        assert!(
            index_cursor
                .cursor
                .index_move_to(&index_cursor.cx, &idx2)
                .unwrap()
                .is_found(),
            "non-conflicting index entries must remain intact"
        );
    }

    #[test]
    fn test_native_replace_row_deletes_index_entry_on_rowid_alias_column() {
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let mut db = MemDatabase::new();
        let table_root = db.create_table(2);
        let index_root = 256;

        let mut engine = VdbeEngine::new(8);
        engine.set_database(db);
        engine.set_transaction(txn);
        engine.set_rowid_alias_column_by_root_page(HashMap::from([(table_root, 0)]));
        engine.table_index_meta = Arc::new(HashMap::from([(
            0,
            vec![IndexCursorMeta {
                cursor_id: 1,
                column_indices: vec![0],
            }]
            .into_boxed_slice(),
        )]));

        assert!(engine.open_storage_cursor(0, table_root, true));
        assert!(engine.open_storage_cursor(1, index_root, true));
        engine.cursor_root_pages.insert(0, table_root);
        engine.cursor_root_pages.insert(1, index_root);

        let row1 = encode_record(&[SqliteValue::Null, SqliteValue::Text("old".into())]);
        let row2 = encode_record(&[SqliteValue::Null, SqliteValue::Text("keep".into())]);
        let idx1 = encode_record(&[SqliteValue::Integer(1), SqliteValue::Integer(1)]);
        let idx2 = encode_record(&[SqliteValue::Integer(2), SqliteValue::Integer(2)]);

        {
            let table_cursor = engine
                .storage_cursors
                .get_mut(&0)
                .expect("table storage cursor should exist");
            table_cursor
                .cursor
                .table_insert(&table_cursor.cx, 1, &row1)
                .unwrap();
            table_cursor
                .cursor
                .table_insert(&table_cursor.cx, 2, &row2)
                .unwrap();
        }
        {
            let index_cursor = engine
                .storage_cursors
                .get_mut(&1)
                .expect("index storage cursor should exist");
            index_cursor
                .cursor
                .index_insert(&index_cursor.cx, &idx1)
                .unwrap();
            index_cursor
                .cursor
                .index_insert(&index_cursor.cx, &idx2)
                .unwrap();
        }

        engine.native_replace_row(0, 1).unwrap();

        let index_cursor = engine
            .storage_cursors
            .get_mut(&1)
            .expect("index storage cursor should still exist");
        assert!(
            !index_cursor
                .cursor
                .index_move_to(&index_cursor.cx, &idx1)
                .unwrap()
                .is_found(),
            "REPLACE cleanup must use the logical rowid alias, not the raw NULL payload slot"
        );
        assert!(
            index_cursor
                .cursor
                .index_move_to(&index_cursor.cx, &idx2)
                .unwrap()
                .is_found(),
            "non-conflicting rowid-alias index entries must remain intact"
        );
    }

    #[test]
    fn test_native_replace_row_deletes_index_entry_after_multi_alter_short_rowid_alias_payload() {
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let mut db = MemDatabase::new();
        let table_root = db.create_table(4);
        let index_root = 256;

        let mut engine = VdbeEngine::new(8);
        engine.set_database(db);
        engine.set_transaction(txn);
        engine.set_rowid_alias_column_by_root_page(HashMap::from([(table_root, 0)]));
        engine.set_table_column_count_by_root_page(HashMap::from([(table_root, 4)]));
        engine.table_index_meta = Arc::new(HashMap::from([(
            0,
            vec![IndexCursorMeta {
                cursor_id: 1,
                column_indices: vec![1],
            }]
            .into_boxed_slice(),
        )]));

        assert!(engine.open_storage_cursor(0, table_root, true));
        assert!(engine.open_storage_cursor(1, index_root, true));
        engine.cursor_root_pages.insert(0, table_root);
        engine.cursor_root_pages.insert(1, index_root);

        let row1 = encode_record(&[SqliteValue::Null, SqliteValue::Text("old".into())]);
        let row2 = encode_record(&[SqliteValue::Null, SqliteValue::Text("keep".into())]);
        let idx1 = encode_record(&[SqliteValue::Text("old".into()), SqliteValue::Integer(1)]);
        let idx2 = encode_record(&[SqliteValue::Text("keep".into()), SqliteValue::Integer(2)]);

        {
            let table_cursor = engine
                .storage_cursors
                .get_mut(&0)
                .expect("table storage cursor should exist");
            table_cursor
                .cursor
                .table_insert(&table_cursor.cx, 1, &row1)
                .unwrap();
            table_cursor
                .cursor
                .table_insert(&table_cursor.cx, 2, &row2)
                .unwrap();
        }
        {
            let index_cursor = engine
                .storage_cursors
                .get_mut(&1)
                .expect("index storage cursor should exist");
            index_cursor
                .cursor
                .index_insert(&index_cursor.cx, &idx1)
                .unwrap();
            index_cursor
                .cursor
                .index_insert(&index_cursor.cx, &idx2)
                .unwrap();
        }

        engine.native_replace_row(0, 1).unwrap();

        let index_cursor = engine
            .storage_cursors
            .get_mut(&1)
            .expect("index storage cursor should still exist");
        assert!(
            !index_cursor
                .cursor
                .index_move_to(&index_cursor.cx, &idx1)
                .unwrap()
                .is_found(),
            "REPLACE cleanup must not shift columns left when an old row keeps the NULL IPK placeholder"
        );
        assert!(
            index_cursor
                .cursor
                .index_move_to(&index_cursor.cx, &idx2)
                .unwrap()
                .is_found(),
            "non-conflicting short rowid-alias payload index entries must remain intact"
        );
    }

    #[test]
    fn test_update_conflict_restore_reinserts_index_entry_on_rowid_alias_column() {
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let mut db = MemDatabase::new();
        let table_root = db.create_table(2);
        let index_root = 256;

        let mut engine = VdbeEngine::new(8);
        engine.set_database(db);
        engine.set_transaction(txn);
        engine.set_rowid_alias_column_by_root_page(HashMap::from([(table_root, 0)]));
        engine.table_index_meta = Arc::new(HashMap::from([(
            0,
            vec![IndexCursorMeta {
                cursor_id: 1,
                column_indices: vec![0],
            }]
            .into_boxed_slice(),
        )]));

        assert!(engine.open_storage_cursor(0, table_root, true));
        assert!(engine.open_storage_cursor(1, index_root, true));
        engine.cursor_root_pages.insert(0, table_root);
        engine.cursor_root_pages.insert(1, index_root);

        let restored_row = encode_record(&[SqliteValue::Null, SqliteValue::Text("old".into())]);
        let logical_idx = encode_record(&[SqliteValue::Integer(1), SqliteValue::Integer(1)]);
        let raw_payload_idx = encode_record(&[SqliteValue::Null, SqliteValue::Integer(1)]);

        engine
            .restore_pending_update_after_conflict(PendingUpdateRestore::Storage {
                cursor_id: 0,
                rowid: 1,
                payload: restored_row,
            })
            .unwrap();

        let table_cursor = engine
            .storage_cursors
            .get_mut(&0)
            .expect("table storage cursor should still exist");
        assert!(
            table_cursor
                .cursor
                .table_move_to(&table_cursor.cx, 1)
                .unwrap()
                .is_found(),
            "UPDATE conflict rollback should restore the original table row"
        );

        let index_cursor = engine
            .storage_cursors
            .get_mut(&1)
            .expect("index storage cursor should still exist");
        assert!(
            index_cursor
                .cursor
                .index_move_to(&index_cursor.cx, &logical_idx)
                .unwrap()
                .is_found(),
            "UPDATE conflict restore must use the logical rowid alias in index keys"
        );
        assert!(
            !index_cursor
                .cursor
                .index_move_to(&index_cursor.cx, &raw_payload_idx)
                .unwrap()
                .is_found(),
            "UPDATE conflict restore must not index the raw NULL rowid-alias payload slot"
        );
    }

    #[test]
    fn test_insert_after_missing_probe_disturbed_by_reposition_stays_sorted() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(10, vec![SqliteValue::Integer(100)]);
        table.insert(30, vec![SqliteValue::Integer(300)]);

        let (rows, _) = run_write_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            let probe_missing = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);

            // Probe the missing rowid=20, which leaves the cursor at its
            // successor/EOF insertion context.
            b.emit_op(Opcode::Integer, 20, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::NotExists, 0, 1, probe_missing, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
            b.resolve_label(probe_missing);

            // Disturb the cursor by repositioning it to rowid=10 before the insert.
            b.emit_op(Opcode::Integer, 10, 2, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SeekRowid, 0, 2, done, P4::None, 0);

            // Insert the previously probed rowid=20 with payload=200.
            b.emit_op(Opcode::Integer, 200, 3, 0, P4::None, 0);
            b.emit_op(Opcode::MakeRecord, 3, 1, 4, P4::None, 0);
            b.emit_op(Opcode::Insert, 0, 4, 1, P4::None, 0);

            b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);
            let body = b.current_addr();
            b.emit_op(Opcode::Rowid, 0, 5, 0, P4::None, 0);
            b.emit_op(Opcode::Column, 0, 0, 6, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 5, 2, 0, P4::None, 0);
            let next_target =
                i32::try_from(body).expect("program counter should fit into i32 for tests");
            b.emit_op(Opcode::Next, 0, next_target, 0, P4::None, 0);

            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Integer(10), SqliteValue::Integer(100)],
                vec![SqliteValue::Integer(20), SqliteValue::Integer(200)],
                vec![SqliteValue::Integer(30), SqliteValue::Integer(300)],
            ]
        );
    }

    #[test]
    fn test_insert_default_conflict_errors_via_btree() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        reset_vdbe_test_sideband_materialization_count();

        // Default conflict mode (OE_ABORT) must raise constraint error.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).expect("table should exist");
        table.insert(1, vec![SqliteValue::Integer(10)]);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);

        // Duplicate rowid=1 with default conflict handling.
        b.emit_op(Opcode::Integer, 1, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 99, 4, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, 4, 1, 5, P4::None, 0);
        b.emit_op(Opcode::Insert, 0, 5, 1, P4::None, 0);

        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let expected_capacity = estimate_make_record_buffer_capacity(&prog, PageSize::DEFAULT);
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);

        let before = vdbe_test_sideband_materialization_count_snapshot();
        let outcome = engine.execute(&prog).expect("execution should succeed");
        let after = vdbe_test_sideband_materialization_count_snapshot();
        assert_eq!(
            outcome,
            ExecOutcome::Error {
                code: ErrorCode::Constraint as i32,
                message: "PRIMARY KEY constraint failed".to_owned(),
            }
        );
        assert_eq!(
            after - before,
            0,
            "duplicate Insert should consume MakeRecord sideband bytes without materializing"
        );
        assert!(
            engine.make_record_lookaside.is_empty(),
            "duplicate Insert should return a cleared scratch buffer"
        );
        assert!(
            engine.make_record_lookaside.capacity() >= expected_capacity,
            "duplicate Insert should keep the reusable sideband allocation"
        );

        let db = engine.take_database().expect("database should exist");
        let table = db.get_table(root).expect("table should exist");
        assert_eq!(table.rows.len(), 1);
        assert_eq!(table.rows[0].values.as_slice(), &[SqliteValue::Integer(10)]);
    }

    #[test]
    fn test_insert_default_conflict_errors_memdb_path() {
        // Same behavior must hold for the legacy MemDatabase cursor path.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).expect("table should exist");
        table.insert(1, vec![SqliteValue::Integer(10)]);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);

        // Duplicate rowid=1 with default conflict handling.
        b.emit_op(Opcode::Integer, 1, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 99, 4, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, 4, 1, 5, P4::None, 0);
        b.emit_op(Opcode::Insert, 0, 5, 1, P4::None, 0);

        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.enable_storage_cursors(false);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(
            outcome,
            ExecOutcome::Error {
                code: ErrorCode::Constraint as i32,
                message: "PRIMARY KEY constraint failed".to_owned(),
            }
        );

        let db = engine.take_database().expect("database should exist");
        let table = db.get_table(root).expect("table should exist");
        assert_eq!(table.rows.len(), 1);
        assert_eq!(table.rows[0].values.as_slice(), &[SqliteValue::Integer(10)]);
    }

    // ── bd-2a3y: TransactionPageIo / SharedTxnPageIo integration tests ──

    #[test]
    fn test_set_transaction_enables_storage_cursors() {
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let mut engine = VdbeEngine::new(8);
        assert!(engine.storage_cursors_enabled);

        // set_transaction should auto-enable storage cursors.
        engine.set_transaction(txn);
        assert!(engine.storage_cursors_enabled);
        assert!(engine.txn_page_io.is_some());
    }

    #[test]
    fn test_storage_cursors_enabled_by_default() {
        let engine = VdbeEngine::new(8);
        assert!(engine.storage_cursors_enabled);
        assert!(engine.txn_page_io.is_none());
    }

    #[test]
    fn test_take_transaction_returns_handle() {
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let mut engine = VdbeEngine::new(8);
        engine.set_transaction(txn);

        // take_transaction should return the handle and clear cursors.
        let recovered = engine
            .take_transaction()
            .expect("take_transaction should succeed");
        assert!(recovered.is_some());
        assert!(engine.txn_page_io.is_none());
        assert!(engine.storage_cursors.is_empty());
    }

    #[test]
    fn test_set_transaction_concurrent_refills_retained_shared_state() {
        use fsqlite_mvcc::{CommitIndex, ConcurrentRegistry, InProcessPageLockTable};
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};
        use fsqlite_types::{CommitSeq, PageNumber, SchemaEpoch, Snapshot, WitnessKey};

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn1 = pager.begin(&cx, TransactionMode::Concurrent).unwrap();
        let txn2 = pager.begin(&cx, TransactionMode::Concurrent).unwrap();

        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let ((session1, handle1), (session2, handle2)) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let session1 = guard
                .begin_concurrent(Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1)))
                .expect("first session should register");
            let session2 = guard
                .begin_concurrent(Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1)))
                .expect("second session should register");
            let handle1 = guard
                .handle(session1)
                .expect("first session handle should exist");
            let handle2 = guard
                .handle(session2)
                .expect("second session handle should exist");
            ((session1, handle1), (session2, handle2))
        };

        let mut engine = VdbeEngine::new(8);
        engine.set_transaction_concurrent(
            txn1,
            session1,
            Arc::clone(&handle1),
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            0,
        );

        let mut retained = engine
            .txn_page_io
            .as_ref()
            .expect("engine should install shared txn state")
            .clone();
        let _drained = engine
            .drain_transaction()
            .expect("drain_transaction should leave retained state behind");

        engine.set_transaction_concurrent(
            txn2,
            session2,
            Arc::clone(&handle2),
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            0,
        );

        retained
            .read_page_data(&cx, PageNumber::ONE)
            .expect("retained clone should see the refilled transaction instead of Drained");
        retained.record_write_witness(&cx, WitnessKey::Page(PageNumber::ONE));

        let guard = registry
            .lock()
            .unwrap_or_else(std::sync::PoisonError::into_inner);
        let first = guard
            .get(session1)
            .expect("first session should remain registered");
        let second = guard
            .get(session2)
            .expect("second session should remain registered");
        assert!(
            !first
                .read_witness_keys()
                .contains(&WitnessKey::Page(PageNumber::ONE)),
            "retained clone must stop recording reads against the drained concurrent session"
        );
        assert!(
            !first
                .write_witness_keys()
                .contains(&WitnessKey::Page(PageNumber::ONE)),
            "retained clone must stop recording writes against the drained concurrent session"
        );
        assert!(
            second
                .read_witness_keys()
                .contains(&WitnessKey::Page(PageNumber::ONE)),
            "retained clone must record reads on the refilled concurrent session"
        );
        assert!(
            second
                .write_witness_keys()
                .contains(&WitnessKey::Page(PageNumber::ONE)),
            "retained clone must record writes on the refilled concurrent session"
        );
    }

    #[test]
    fn test_shared_txn_page_io_btree_read_defers_to_precise_witnesses() {
        use fsqlite_mvcc::{CommitIndex, ConcurrentRegistry, InProcessPageLockTable};
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};
        use fsqlite_types::{CommitSeq, PageNumber, SchemaEpoch, Snapshot, WitnessKey};

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Concurrent).unwrap();

        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let snapshot = Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1));
        let (session_id, handle) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let session_id = guard
                .begin_concurrent(snapshot)
                .expect("session should register");
            let handle = guard
                .handle(session_id)
                .expect("session handle should exist");
            (session_id, handle)
        };

        let page_io = SharedTxnPageIo::with_concurrent(
            txn,
            session_id,
            Arc::clone(&handle),
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            0,
        );

        let _ = page_io
            .read_btree_page_data(&cx, PageNumber::ONE)
            .expect("B-tree page read should succeed");
        {
            let guard = handle.lock();
            assert!(
                guard.read_witness_keys().is_empty(),
                "B-tree page loads must not publish coarse page read witnesses"
            );
        }

        let cell_witness = WitnessKey::Cell {
            btree_root: PageNumber::ONE,
            leaf_page: PageNumber::ONE,
            tag: 42,
        };
        page_io.record_read_witness(&cx, cell_witness.clone());

        let guard = handle.lock();
        assert!(
            guard.read_witness_keys().contains(&cell_witness),
            "cursor-supplied cell witness must be recorded on the concurrent handle"
        );
        assert!(
            !guard
                .read_witness_keys()
                .contains(&WitnessKey::Page(PageNumber::ONE)),
            "precise B-tree witness publication must not reintroduce coarse page reads"
        );
    }

    #[test]
    fn test_set_transaction_clears_stale_concurrent_context_on_retained_reuse() {
        use fsqlite_mvcc::{CommitIndex, ConcurrentRegistry, InProcessPageLockTable};
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};
        use fsqlite_types::{CommitSeq, PageNumber, SchemaEpoch, Snapshot, WitnessKey};

        let pager = MockMvccPager;
        let cx = Cx::new();
        let concurrent_txn = pager.begin(&cx, TransactionMode::Concurrent).unwrap();
        let immediate_txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let (session_id, handle) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let session_id = guard
                .begin_concurrent(Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1)))
                .expect("session should register");
            let handle = guard
                .handle(session_id)
                .expect("session handle should exist");
            (session_id, handle)
        };

        let mut engine = VdbeEngine::new(8);
        engine.set_transaction_concurrent(
            concurrent_txn,
            session_id,
            Arc::clone(&handle),
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            0,
        );

        let mut retained = engine
            .txn_page_io
            .as_ref()
            .expect("engine should install shared txn state")
            .clone();
        let _drained = engine
            .drain_transaction()
            .expect("drain_transaction should leave retained state behind");

        engine.set_transaction(immediate_txn);

        retained.read_page_data(&cx, PageNumber::ONE).expect(
            "plain set_transaction should refill the retained clone instead of leaving Drained",
        );
        retained.record_write_witness(&cx, WitnessKey::Page(PageNumber::ONE));

        let guard = registry
            .lock()
            .unwrap_or_else(std::sync::PoisonError::into_inner);
        let session = guard
            .get(session_id)
            .expect("original concurrent session should remain registered");
        assert!(
            !session
                .read_witness_keys()
                .contains(&WitnessKey::Page(PageNumber::ONE)),
            "plain set_transaction must clear stale concurrent read tracking on retained clones"
        );
        assert!(
            !session
                .write_witness_keys()
                .contains(&WitnessKey::Page(PageNumber::ONE)),
            "plain set_transaction must clear stale concurrent write tracking on retained clones"
        );
    }

    #[test]
    fn test_open_storage_cursor_prefers_txn_backend() {
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let mut engine = VdbeEngine::new(8);
        engine.set_database(db);
        engine.set_transaction(txn);

        // open_storage_cursor should succeed using the Txn backend.
        let opened = engine.open_storage_cursor(0, root, false);
        assert!(opened);

        // Verify the cursor exists in storage_cursors.
        assert!(engine.storage_cursors.contains_key(&0));

        // Clean up: drop cursors before taking transaction.
        engine.storage_cursors.clear();
        let _txn = engine
            .take_transaction()
            .expect("take_transaction should succeed");
    }

    #[test]
    fn test_open_storage_cursor_txn_index_honors_desc_key_metadata() {
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};
        use fsqlite_types::record::{parse_record, serialize_record};

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();
        let root = 256;

        let mut engine = VdbeEngine::new(8);
        engine.set_database(MemDatabase::new());
        engine.set_transaction(txn);
        engine.set_index_desc_flags_by_root_page(HashMap::from([(root, vec![true])]));

        assert!(
            engine.open_storage_cursor(0, root, true),
            "writable txn-backed index cursor should open on a fresh root page"
        );

        let sc = engine.storage_cursors.get_mut(&0).unwrap();
        let early_key = serialize_record(&[SqliteValue::Integer(10), SqliteValue::Integer(1)]);
        let late_key = serialize_record(&[SqliteValue::Integer(20), SqliteValue::Integer(2)]);
        sc.cursor.index_insert(&sc.cx, &early_key).unwrap();
        sc.cursor.index_insert(&sc.cx, &late_key).unwrap();

        assert!(sc.cursor.first(&sc.cx).unwrap());
        let first_values = parse_record(&sc.cursor.payload(&sc.cx).unwrap()).unwrap();
        assert_eq!(
            first_values,
            vec![SqliteValue::Integer(20), SqliteValue::Integer(2)],
            "descending index cursor should order the larger key first"
        );

        assert!(sc.cursor.next(&sc.cx).unwrap());
        let second_values = parse_record(&sc.cursor.payload(&sc.cx).unwrap()).unwrap();
        assert_eq!(
            second_values,
            vec![SqliteValue::Integer(10), SqliteValue::Integer(1)],
            "descending index cursor should keep the smaller key after the larger one"
        );

        engine.storage_cursors.clear();
        let _txn = engine
            .take_transaction()
            .expect("take_transaction should succeed");
    }

    #[test]
    fn test_open_storage_cursor_txn_zero_page_honors_page1_reserved_bytes_layout() {
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let mut txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();
        let root_pgno = PageNumber::new(256).unwrap();

        let mut header = DatabaseHeader {
            page_size: PageSize::DEFAULT,
            reserved_per_page: 32,
            page_count: root_pgno.get(),
            ..DatabaseHeader::default()
        };
        header.version_valid_for = header.change_counter;
        let mut page_one = vec![0u8; PageSize::DEFAULT.as_usize()];
        page_one[..DATABASE_HEADER_SIZE].copy_from_slice(&header.to_bytes().unwrap());
        txn.write_page(&cx, PageNumber::ONE, &page_one).unwrap();

        let mut engine = VdbeEngine::new(8);
        engine.set_transaction(txn);

        assert!(
            engine.open_storage_cursor(0, root_pgno.get() as i32, true),
            "txn-backed writable cursor should open on a fresh reserved-byte root page"
        );

        let storage_cursor = engine.storage_cursors.get(&0).unwrap();
        assert_eq!(
            storage_cursor.cursor.usable_size(),
            PageSize::DEFAULT.get() - 32
        );
        assert_eq!(storage_cursor.cursor.page_size(), PageSize::DEFAULT.get());

        let root_page = engine
            .txn_page_io
            .as_ref()
            .unwrap()
            .read_page(&cx, root_pgno)
            .unwrap();
        assert_eq!(root_page[0], BtreePageType::LeafTable as u8);
        assert_eq!(
            u16::from_be_bytes([root_page[5], root_page[6]]),
            (PageSize::DEFAULT.get() - 32) as u16,
            "fresh reserved-byte root pages must start their cell content area at usable_size"
        );

        engine.storage_cursors.clear();
        let _txn = engine
            .take_transaction()
            .expect("take_transaction should succeed");
    }

    #[test]
    fn test_open_storage_cursor_records_root_page_for_manual_open() {
        let mut engine = VdbeEngine::new(8);
        let mut db = MemDatabase::new();
        let root_page = db.allocate_root_page();
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);

        assert!(
            engine.open_storage_cursor(7, root_page, true),
            "manual storage cursor open should succeed"
        );
        assert_eq!(
            engine.cursor_root_pages.get(&7),
            Some(&root_page),
            "manual open must publish root-page metadata for later index collation lookups"
        );
        assert_eq!(
            engine
                .storage_cursors
                .get(&7)
                .expect("storage cursor should exist")
                .root_page,
            root_page,
            "cursor metadata and published root-page map must stay in sync"
        );
    }

    #[test]
    fn test_storage_cursor_no_conflict_scratch_matches_legacy_probe_semantics() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let (mut engine, conflict_key, miss_key) =
            build_storage_index_engine_with_duplicate_prefixes();
        let cursor = engine
            .storage_cursors
            .get_mut(&0)
            .expect("storage cursor should exist");

        let legacy_conflict = legacy_storage_cursor_no_conflict_prefix_match(cursor, &conflict_key)
            .expect("legacy conflict probe should succeed");
        let scratch_conflict = storage_cursor_no_conflict_prefix_match(0, cursor, &conflict_key)
            .expect("scratch conflict probe should succeed");
        assert_eq!(scratch_conflict, legacy_conflict);
        assert!(scratch_conflict, "duplicate prefix should conflict");

        let legacy_miss = legacy_storage_cursor_no_conflict_prefix_match(cursor, &miss_key)
            .expect("legacy miss probe should succeed");
        let scratch_miss = storage_cursor_no_conflict_prefix_match(0, cursor, &miss_key)
            .expect("scratch miss probe should succeed");
        assert_eq!(scratch_miss, legacy_miss);
        assert!(!scratch_miss, "distinct prefix should not conflict");

        let malformed = vec![0x80];
        let legacy_malformed = legacy_storage_cursor_no_conflict_prefix_match(cursor, &malformed)
            .expect("legacy malformed probe should not raise");
        let scratch_malformed = storage_cursor_no_conflict_prefix_match(0, cursor, &malformed)
            .expect("scratch malformed probe should not raise");
        assert_eq!(scratch_malformed, legacy_malformed);
        assert!(
            !scratch_malformed,
            "malformed probes should not report conflicts"
        );
    }

    #[test]
    fn test_storage_cursor_no_conflict_collated_probe_honors_nocase() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let (mut engine, conflict_key, miss_key) =
            build_storage_index_engine_with_nocase_prefixes();
        let coll_arc = Arc::clone(&engine.collation_registry);
        let coll_guard = coll_arc.lock().unwrap_or_else(|e| e.into_inner());
        let root_page = engine
            .cursor_root_pages
            .get(&0)
            .copied()
            .expect("storage cursor should record its root page");
        let desc_flags = engine.index_desc_flags_for_root(root_page);
        let collations = engine.index_collations_for_root(root_page);
        let cursor = engine
            .storage_cursors
            .get_mut(&0)
            .expect("storage cursor should exist");

        assert!(
            storage_cursor_no_conflict_prefix_match_collated(
                0,
                cursor,
                &conflict_key,
                &desc_flags,
                &collations,
                &coll_guard,
            )
            .expect("collated conflict probe should succeed"),
            "NOCASE-equivalent text must be treated as a duplicate prefix"
        );
        assert!(
            !storage_cursor_no_conflict_prefix_match_collated(
                0,
                cursor,
                &miss_key,
                &desc_flags,
                &collations,
                &coll_guard,
            )
            .expect("collated miss probe should succeed"),
            "distinct NOCASE text must remain insertable"
        );
    }

    #[test]
    fn test_storage_cursor_no_conflict_reuses_payload_and_value_scratch() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let (mut engine, conflict_key, _) = build_storage_index_engine_with_duplicate_prefixes();
        let iterations = 256;
        let prev_record_profile_enabled = fsqlite_types::record::record_profile_enabled();

        let run_legacy = |cursor: &mut StorageCursor| {
            fsqlite_types::record::reset_record_profile();
            fsqlite_types::record::set_record_profile_enabled(true);
            fsqlite_btree::reset_btree_copy_profile();
            fsqlite_btree::set_btree_copy_profile_enabled(true);
            let _scope = enter_record_profile_scope(RecordProfileScope::VdbeEngine);
            let mut matches = 0usize;
            for _ in 0..iterations {
                if legacy_storage_cursor_no_conflict_prefix_match(cursor, &conflict_key)
                    .expect("legacy probe should succeed")
                {
                    matches += 1;
                }
            }
            (
                matches,
                fsqlite_types::record::record_profile_snapshot(),
                fsqlite_btree::btree_copy_profile_snapshot(),
            )
        };

        let run_scratch = |cursor: &mut StorageCursor| {
            fsqlite_types::record::reset_record_profile();
            fsqlite_types::record::set_record_profile_enabled(true);
            fsqlite_btree::reset_btree_copy_profile();
            fsqlite_btree::set_btree_copy_profile_enabled(true);
            let _scope = enter_record_profile_scope(RecordProfileScope::VdbeEngine);
            let mut matches = 0usize;
            let mut payload_ptr = None;
            let mut value_ptr = None;
            for _ in 0..iterations {
                if storage_cursor_no_conflict_prefix_match(0, cursor, &conflict_key)
                    .expect("scratch probe should succeed")
                {
                    matches += 1;
                }
                let payload_buf_ptr = cursor.payload_buf.as_ptr() as usize;
                let cur_vals_ptr = cursor.cur_vals_buf.as_ptr() as usize;
                if let Some(expected) = payload_ptr {
                    assert_eq!(
                        payload_buf_ptr, expected,
                        "payload scratch buffer should be reused across probes"
                    );
                } else {
                    payload_ptr = Some(payload_buf_ptr);
                }
                if let Some(expected) = value_ptr {
                    assert_eq!(
                        cur_vals_ptr, expected,
                        "value scratch buffer should be reused across probes"
                    );
                } else {
                    value_ptr = Some(cur_vals_ptr);
                }
            }
            (
                matches,
                fsqlite_types::record::record_profile_snapshot(),
                fsqlite_btree::btree_copy_profile_snapshot(),
            )
        };

        let (legacy_matches, legacy_record_profile, legacy_copy_profile) = {
            let cursor = engine
                .storage_cursors
                .get_mut(&0)
                .expect("storage cursor should exist");
            run_legacy(cursor)
        };
        let (scratch_matches, scratch_record_profile, scratch_copy_profile) = {
            let cursor = engine
                .storage_cursors
                .get_mut(&0)
                .expect("storage cursor should exist");
            run_scratch(cursor)
        };

        assert_eq!(legacy_matches, iterations);
        assert_eq!(scratch_matches, iterations);
        assert!(
            legacy_record_profile
                .callsite_breakdown
                .vdbe_engine
                .parse_record_calls
                >= u64::try_from(iterations * 2).unwrap_or(u64::MAX),
            "legacy loop should fully parse both probe and entry on every iteration"
        );
        assert_eq!(
            scratch_record_profile
                .callsite_breakdown
                .vdbe_engine
                .parse_record_calls,
            0,
            "scratch path should eliminate full parse_record allocations"
        );
        assert!(
            scratch_record_profile
                .callsite_breakdown
                .vdbe_engine
                .parse_record_into_calls
                >= u64::try_from(iterations * 2).unwrap_or(u64::MAX),
            "scratch path should decode probe and entry into reusable buffers"
        );
        assert!(
            legacy_copy_profile.owned_payload_materialization_calls
                >= u64::try_from(iterations).unwrap_or(u64::MAX),
            "legacy path should materialize an owned payload for every probe"
        );
        assert_eq!(
            scratch_copy_profile.owned_payload_materialization_calls, 0,
            "scratch path should avoid owned payload materialization"
        );
        assert!(
            scratch_copy_profile.local_payload_copy_calls
                >= u64::try_from(iterations).unwrap_or(u64::MAX),
            "scratch path should reuse the caller-owned payload buffer"
        );

        eprintln!(
            concat!(
                "BD_DB300_4_3_2_CHURN_JSON=",
                "{{",
                "\"bead_id\":\"bd-db300.4.3.2\",",
                "\"hot_path\":\"storage_cursor_no_conflict_index_probe\",",
                "\"iterations\":{},",
                "\"legacy\":{{",
                "\"parse_record_calls\":{},",
                "\"parse_record_into_calls\":{},",
                "\"owned_payload_materialization_calls\":{},",
                "\"owned_payload_materialization_bytes\":{},",
                "\"local_payload_copy_calls\":{}",
                "}},",
                "\"scratch\":{{",
                "\"parse_record_calls\":{},",
                "\"parse_record_into_calls\":{},",
                "\"owned_payload_materialization_calls\":{},",
                "\"owned_payload_materialization_bytes\":{},",
                "\"local_payload_copy_calls\":{}",
                "}}",
                "}}"
            ),
            iterations,
            legacy_record_profile
                .callsite_breakdown
                .vdbe_engine
                .parse_record_calls,
            legacy_record_profile
                .callsite_breakdown
                .vdbe_engine
                .parse_record_into_calls,
            legacy_copy_profile.owned_payload_materialization_calls,
            legacy_copy_profile.owned_payload_materialization_bytes,
            legacy_copy_profile.local_payload_copy_calls,
            scratch_record_profile
                .callsite_breakdown
                .vdbe_engine
                .parse_record_calls,
            scratch_record_profile
                .callsite_breakdown
                .vdbe_engine
                .parse_record_into_calls,
            scratch_copy_profile.owned_payload_materialization_calls,
            scratch_copy_profile.owned_payload_materialization_bytes,
            scratch_copy_profile.local_payload_copy_calls,
        );

        fsqlite_btree::set_btree_copy_profile_enabled(false);
        fsqlite_types::record::set_record_profile_enabled(prev_record_profile_enabled);
    }

    #[test]
    fn test_storage_cursor_no_conflict_mixed_boundaries_reduce_allocator_pressure_and_preserve_reset_safety()
     {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let (mut engine, conflict_key, miss_key) =
            build_storage_index_engine_with_duplicate_prefixes();
        let malformed_key = vec![0x80];
        let conflict_target = [SqliteValue::Integer(7)];
        let miss_target = [SqliteValue::Integer(9)];
        let iterations = 128usize;
        let prev_record_profile_enabled = fsqlite_types::record::record_profile_enabled();

        let run_legacy = |cursor: &mut StorageCursor| {
            fsqlite_types::record::reset_record_profile();
            fsqlite_types::record::set_record_profile_enabled(true);
            fsqlite_btree::reset_btree_copy_profile();
            fsqlite_btree::set_btree_copy_profile_enabled(true);
            let _scope = enter_record_profile_scope(RecordProfileScope::VdbeEngine);
            let mut matches = 0usize;
            for _ in 0..iterations {
                for (probe, expected) in [
                    (conflict_key.as_slice(), true),
                    (miss_key.as_slice(), false),
                    (malformed_key.as_slice(), false),
                    (conflict_key.as_slice(), true),
                ] {
                    let actual = legacy_storage_cursor_no_conflict_prefix_match(cursor, probe)
                        .expect("legacy probe should succeed");
                    assert_eq!(actual, expected);
                    if actual {
                        matches += 1;
                    }
                }
            }
            (
                matches,
                fsqlite_types::record::record_profile_snapshot(),
                fsqlite_btree::btree_copy_profile_snapshot(),
            )
        };

        let run_scratch = |cursor: &mut StorageCursor| {
            fsqlite_types::record::reset_record_profile();
            fsqlite_types::record::set_record_profile_enabled(true);
            fsqlite_btree::reset_btree_copy_profile();
            fsqlite_btree::set_btree_copy_profile_enabled(true);
            let _scope = enter_record_profile_scope(RecordProfileScope::VdbeEngine);
            let mut matches = 0usize;
            let mut payload_capacity = None;
            let mut target_capacity = None;
            let mut current_capacity = None;

            for _ in 0..iterations {
                for (boundary, probe, expected, expected_target) in [
                    (
                        "conflict",
                        conflict_key.as_slice(),
                        true,
                        Some(conflict_target.as_slice()),
                    ),
                    (
                        "miss",
                        miss_key.as_slice(),
                        false,
                        Some(miss_target.as_slice()),
                    ),
                    ("malformed", malformed_key.as_slice(), false, None),
                    (
                        "recovery",
                        conflict_key.as_slice(),
                        true,
                        Some(conflict_target.as_slice()),
                    ),
                ] {
                    let actual = storage_cursor_no_conflict_prefix_match(0, cursor, probe)
                        .expect("scratch probe should succeed");
                    assert_eq!(
                        actual, expected,
                        "{boundary} probe should preserve semantics"
                    );
                    if actual {
                        matches += 1;
                    }

                    match expected_target {
                        Some(expected_target) => {
                            assert_eq!(
                                cursor.target_vals_buf.as_slice(),
                                expected_target,
                                "{boundary} probe should refresh target scratch without stale values"
                            );
                        }
                        None => {
                            assert!(
                                cursor.target_vals_buf.is_empty(),
                                "{boundary} probe should clear malformed target scratch"
                            );
                        }
                    }

                    let observed_payload_capacity = cursor.payload_buf.capacity();
                    if let Some(expected_capacity) = payload_capacity {
                        assert_eq!(
                            observed_payload_capacity, expected_capacity,
                            "{boundary} probe should retain payload scratch capacity"
                        );
                    } else if observed_payload_capacity > 0 {
                        payload_capacity = Some(observed_payload_capacity);
                    }

                    let observed_target_capacity = cursor.target_vals_buf.capacity();
                    if let Some(expected_capacity) = target_capacity {
                        assert_eq!(
                            observed_target_capacity, expected_capacity,
                            "{boundary} probe should retain target scratch capacity"
                        );
                    } else if observed_target_capacity > 0 {
                        target_capacity = Some(observed_target_capacity);
                    }

                    let observed_current_capacity = cursor.cur_vals_buf.capacity();
                    if let Some(expected_capacity) = current_capacity {
                        assert_eq!(
                            observed_current_capacity, expected_capacity,
                            "{boundary} probe should retain current-entry scratch capacity"
                        );
                    } else if observed_current_capacity > 0 {
                        current_capacity = Some(observed_current_capacity);
                    }
                }
            }

            (
                matches,
                fsqlite_types::record::record_profile_snapshot(),
                fsqlite_btree::btree_copy_profile_snapshot(),
                payload_capacity.unwrap_or_else(|| cursor.payload_buf.capacity()),
                target_capacity.unwrap_or_else(|| cursor.target_vals_buf.capacity()),
                current_capacity.unwrap_or_else(|| cursor.cur_vals_buf.capacity()),
            )
        };

        let (legacy_matches, legacy_record_profile, legacy_copy_profile) = {
            let cursor = engine
                .storage_cursors
                .get_mut(&0)
                .expect("storage cursor should exist");
            run_legacy(cursor)
        };
        let (
            scratch_matches,
            scratch_record_profile,
            scratch_copy_profile,
            payload_capacity,
            target_capacity,
            current_capacity,
        ) = {
            let cursor = engine
                .storage_cursors
                .get_mut(&0)
                .expect("storage cursor should exist");
            run_scratch(cursor)
        };

        assert_eq!(legacy_matches, iterations * 2);
        assert_eq!(scratch_matches, iterations * 2);
        assert_eq!(
            scratch_record_profile
                .callsite_breakdown
                .vdbe_engine
                .parse_record_calls,
            0,
            "mixed-boundary scratch path should avoid parse_record allocations entirely"
        );
        assert!(
            legacy_record_profile
                .callsite_breakdown
                .vdbe_engine
                .parse_record_calls
                > scratch_record_profile
                    .callsite_breakdown
                    .vdbe_engine
                    .parse_record_calls,
            "legacy path should incur more parse_record churn under mixed boundaries"
        );
        assert_eq!(
            scratch_copy_profile.owned_payload_materialization_calls, 0,
            "scratch path should avoid owned payload materialization under mixed boundaries"
        );
        assert!(
            legacy_copy_profile.owned_payload_materialization_calls
                >= u64::try_from(iterations * 2).unwrap_or(u64::MAX),
            "legacy path should materialize owned payloads for both found probes per iteration"
        );
        assert!(
            scratch_copy_profile.local_payload_copy_calls
                >= u64::try_from(iterations * 2).unwrap_or(u64::MAX),
            "scratch path should reuse caller-owned payload storage on found probes"
        );
        assert!(payload_capacity > 0);
        assert!(target_capacity > 0);
        assert!(current_capacity > 0);

        eprintln!(
            concat!(
                "BD_DB300_4_3_3_RESET_CHURN_JSON=",
                "{{",
                "\"bead_id\":\"bd-db300.4.3.3\",",
                "\"hot_path\":\"storage_cursor_no_conflict_mixed_boundaries\",",
                "\"iterations\":{},",
                "\"boundary_sequence\":\"conflict,miss,malformed,recovery\",",
                "\"legacy\":{{",
                "\"parse_record_calls\":{},",
                "\"owned_payload_materialization_calls\":{}",
                "}},",
                "\"scratch\":{{",
                "\"parse_record_calls\":{},",
                "\"owned_payload_materialization_calls\":{},",
                "\"local_payload_copy_calls\":{},",
                "\"payload_capacity\":{},",
                "\"target_capacity\":{},",
                "\"current_capacity\":{}",
                "}}",
                "}}"
            ),
            iterations,
            legacy_record_profile
                .callsite_breakdown
                .vdbe_engine
                .parse_record_calls,
            legacy_copy_profile.owned_payload_materialization_calls,
            scratch_record_profile
                .callsite_breakdown
                .vdbe_engine
                .parse_record_calls,
            scratch_copy_profile.owned_payload_materialization_calls,
            scratch_copy_profile.local_payload_copy_calls,
            payload_capacity,
            target_capacity,
            current_capacity,
        );

        fsqlite_btree::set_btree_copy_profile_enabled(false);
        fsqlite_types::record::set_record_profile_enabled(prev_record_profile_enabled);
    }

    #[test]
    fn test_no_conflict_opcode_uses_storage_cursor_scratch_probe_path() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let (mut engine, conflict_key, miss_key) =
            build_storage_index_engine_with_duplicate_prefixes();
        let program = build_no_conflict_opcode_probe_program(conflict_key, miss_key, vec![0x80]);
        let rows = execute_program_with_engine(&mut engine, &program);

        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Integer(0)],
                vec![SqliteValue::Integer(1)],
                vec![SqliteValue::Integer(1)],
            ],
            "NoConflict should treat duplicate prefixes as conflicts and malformed probes as insertable"
        );
    }

    #[test]
    fn test_no_conflict_opcode_honors_nocase_collation() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let (mut engine, conflict_key, miss_key) =
            build_storage_index_engine_with_nocase_prefixes();
        let program = build_no_conflict_opcode_probe_program(conflict_key, miss_key, vec![0x80]);
        let rows = execute_program_with_engine(&mut engine, &program);

        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Integer(0)],
                vec![SqliteValue::Integer(1)],
                vec![SqliteValue::Integer(1)],
            ],
            "NoConflict must honor per-index NOCASE equality when probing storage indexes"
        );
    }

    #[test]
    fn test_found_opcode_honors_nocase_collation() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let (mut engine, _, _) = build_storage_index_engine_with_nocase_prefixes();
        let conflict_key =
            encode_record(&[SqliteValue::Text("ALPHA".into()), SqliteValue::Integer(1)]);
        let miss_key =
            encode_record(&[SqliteValue::Text("gamma".into()), SqliteValue::Integer(99)]);
        let program = build_found_opcode_probe_program(conflict_key, miss_key);
        let rows = execute_program_with_engine(&mut engine, &program);

        assert_eq!(
            rows,
            vec![vec![SqliteValue::Integer(1)], vec![SqliteValue::Integer(0)]],
            "Found must treat NOCASE-equivalent index keys as exact matches"
        );
    }

    #[test]
    fn test_not_found_opcode_honors_nocase_collation() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let (mut engine, _, _) = build_storage_index_engine_with_nocase_prefixes();
        let conflict_key =
            encode_record(&[SqliteValue::Text("ALPHA".into()), SqliteValue::Integer(1)]);
        let miss_key =
            encode_record(&[SqliteValue::Text("gamma".into()), SqliteValue::Integer(99)]);
        let program = build_not_found_opcode_probe_program(conflict_key, miss_key);
        let rows = execute_program_with_engine(&mut engine, &program);

        assert_eq!(
            rows,
            vec![vec![SqliteValue::Integer(0)], vec![SqliteValue::Integer(1)]],
            "NotFound must respect NOCASE equality when probing storage indexes"
        );
    }

    #[test]
    fn test_seekge_honors_nocase_collation_for_full_index_keys() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let mut engine = build_storage_index_engine_with_nocase_mixed_case_ordering();

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_text = b.alloc_reg();
        let r_rowid = b.alloc_reg();
        let r_seek_key = b.alloc_reg();
        let r_found_text = b.alloc_reg();
        let r_found_rowid = b.alloc_reg();

        b.emit_op(
            Opcode::String8,
            0,
            r_text,
            0,
            P4::Str("ALPHA".to_owned()),
            0,
        );
        b.emit_op(Opcode::Integer, 1, r_rowid, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, r_text, 2, r_seek_key, P4::None, 0);
        b.emit_jump_to_label(Opcode::SeekGE, 0, r_seek_key, done, P4::None, 0);
        b.emit_op(Opcode::Column, 0, 0, r_found_text, P4::None, 0);
        b.emit_op(Opcode::IdxRowid, 0, r_found_rowid, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r_found_text, 2, 0, P4::None, 0);

        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let program = b.finish().expect("program should build");
        let rows = execute_program_with_engine(&mut engine, &program);

        assert_eq!(
            rows,
            vec![vec![
                SqliteValue::Text("alpha".into()),
                SqliteValue::Integer(1),
            ]],
            "SeekGE must land on the smallest collated key >= the probe, not the first raw-binary successor"
        );
    }

    #[test]
    fn test_seekgt_honors_nocase_collation_for_full_index_keys() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let mut engine = build_storage_index_engine_with_nocase_mixed_case_ordering();

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_text = b.alloc_reg();
        let r_rowid = b.alloc_reg();
        let r_seek_key = b.alloc_reg();
        let r_found_text = b.alloc_reg();
        let r_found_rowid = b.alloc_reg();

        b.emit_op(
            Opcode::String8,
            0,
            r_text,
            0,
            P4::Str("alpha".to_owned()),
            0,
        );
        b.emit_op(Opcode::Integer, 1, r_rowid, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, r_text, 2, r_seek_key, P4::None, 0);
        b.emit_jump_to_label(Opcode::SeekGT, 0, r_seek_key, done, P4::None, 0);
        b.emit_op(Opcode::Column, 0, 0, r_found_text, P4::None, 0);
        b.emit_op(Opcode::IdxRowid, 0, r_found_rowid, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r_found_text, 2, 0, P4::None, 0);

        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let program = b.finish().expect("program should build");
        let rows = execute_program_with_engine(&mut engine, &program);

        assert_eq!(
            rows,
            vec![vec![
                SqliteValue::Text("ALPHA".into()),
                SqliteValue::Integer(2),
            ]],
            "SeekGT must skip only collated-equal predecessors and land on the next greater key"
        );
    }

    #[test]
    fn test_seeklt_honors_nocase_collation_for_full_index_keys() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let mut engine = build_storage_index_engine_with_nocase_mixed_case_ordering();

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_text = b.alloc_reg();
        let r_rowid = b.alloc_reg();
        let r_seek_key = b.alloc_reg();
        let r_found_text = b.alloc_reg();
        let r_found_rowid = b.alloc_reg();

        b.emit_op(
            Opcode::String8,
            0,
            r_text,
            0,
            P4::Str("ALPHA".to_owned()),
            0,
        );
        b.emit_op(Opcode::Integer, 2, r_rowid, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, r_text, 2, r_seek_key, P4::None, 0);
        b.emit_jump_to_label(Opcode::SeekLT, 0, r_seek_key, done, P4::None, 0);
        b.emit_op(Opcode::Column, 0, 0, r_found_text, P4::None, 0);
        b.emit_op(Opcode::IdxRowid, 0, r_found_rowid, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r_found_text, 2, 0, P4::None, 0);

        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let program = b.finish().expect("program should build");
        let rows = execute_program_with_engine(&mut engine, &program);

        assert_eq!(
            rows,
            vec![vec![
                SqliteValue::Text("alpha".into()),
                SqliteValue::Integer(1),
            ]],
            "SeekLT must return the greatest collated key strictly below the probe"
        );
    }

    #[test]
    fn test_idxinsert_unique_no_conflict_inserts_distinct_prefix_after_cursor_disturbance() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let (mut engine, _, _) = build_storage_index_engine_with_duplicate_prefixes();

        let disturb_key = encode_record(&[SqliteValue::Integer(7), SqliteValue::Integer(1)]);
        let inserted_key = encode_record(&[SqliteValue::Integer(9), SqliteValue::Integer(500)]);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let disturbed = b.emit_label();
        let found = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_disturb_key = b.alloc_reg();
        let r_insert_key = b.alloc_reg();
        let r_probe_key = b.alloc_reg();
        let r_out = b.alloc_reg();

        b.emit_op(Opcode::Blob, 0, r_disturb_key, 0, P4::Blob(disturb_key), 0);
        b.emit_jump_to_label(Opcode::Found, 0, r_disturb_key, disturbed, P4::None, 0);
        b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
        b.resolve_label(disturbed);

        b.emit_op(
            Opcode::Blob,
            0,
            r_insert_key,
            0,
            P4::Blob(inserted_key.clone()),
            0,
        );
        b.emit_op(
            Opcode::IdxInsert,
            0,
            r_insert_key,
            1,
            P4::Table("idx_col".to_owned()),
            1,
        );

        b.emit_op(Opcode::Blob, 0, r_probe_key, 0, P4::Blob(inserted_key), 0);
        b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
        b.emit_jump_to_label(Opcode::Found, 0, r_probe_key, found, P4::None, 0);
        b.emit_jump_to_label(Opcode::Goto, 0, 0, done, P4::None, 0);
        b.resolve_label(found);
        b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
        b.resolve_label(done);

        b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let program = b.finish().expect("program should build");
        let rows = execute_program_with_engine(&mut engine, &program);

        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
    }

    #[test]
    fn test_count_index_eq_run_consumes_duplicate_run_and_leaves_cursor_on_next_key() {
        let mut engine = VdbeEngine::new(8);
        install_duplicate_prefix_index_fixture(&mut engine, 0);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_probe = b.alloc_reg();
        let r_min_rowid = b.alloc_reg();
        let r_seek_key = b.alloc_reg();
        let r_count = b.alloc_reg();
        let r_next_key = b.alloc_reg();

        b.emit_op(Opcode::Integer, 7, r_probe, 0, P4::None, 0);
        b.emit_op(Opcode::Int64, 0, r_min_rowid, 0, P4::Int64(i64::MIN), 0);
        b.emit_op(Opcode::MakeRecord, r_probe, 2, r_seek_key, P4::None, 0);
        b.emit_jump_to_label(Opcode::SeekGE, 0, r_seek_key, done, P4::None, 0);
        b.emit_op(Opcode::Integer, 0, r_count, 0, P4::None, 0);
        b.emit_op(Opcode::CountIndexEqRun, 0, r_count, r_probe, P4::None, 0);
        b.emit_jump_to_label(Opcode::IfNullRow, 0, 0, done, P4::None, 0);
        b.emit_op(Opcode::Column, 0, 0, r_next_key, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r_count, 2, 0, P4::None, 0);

        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let program = b.finish().expect("program should build");
        let rows = execute_program_with_engine(&mut engine, &program);

        assert_eq!(
            rows,
            vec![vec![SqliteValue::Integer(128), SqliteValue::Integer(8)]],
            "CountIndexEqRun should count the full duplicate run and leave the cursor on the next key"
        );
    }

    #[test]
    fn test_count_index_eq_run_uses_cursor_row_decode_scratch() {
        let mut engine = VdbeEngine::new(8);
        install_duplicate_prefix_index_fixture(&mut engine, 0);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_probe = b.alloc_reg();
        let r_min_rowid = b.alloc_reg();
        let r_seek_key = b.alloc_reg();
        let r_count = b.alloc_reg();
        let r_current_key = b.alloc_reg();

        b.emit_op(Opcode::Integer, 7, r_probe, 0, P4::None, 0);
        b.emit_op(Opcode::Int64, 0, r_min_rowid, 0, P4::Int64(i64::MIN), 0);
        b.emit_op(Opcode::MakeRecord, r_probe, 2, r_seek_key, P4::None, 0);
        b.emit_jump_to_label(Opcode::SeekGE, 0, r_seek_key, done, P4::None, 0);
        b.emit_op(Opcode::Integer, 0, r_count, 0, P4::None, 0);
        b.emit_op(Opcode::Column, 0, 0, r_current_key, P4::None, 0);
        b.emit_op(Opcode::CountIndexEqRun, 0, r_count, r_probe, P4::None, 0);

        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let program = b.finish().expect("program should build");
        let _ = execute_program_with_engine(&mut engine, &program);

        let cursor = engine
            .storage_cursors
            .get(&0)
            .expect("cursor should still exist");

        assert!(
            cursor.cur_vals_buf.is_empty(),
            "CountIndexEqRun should not populate the legacy full-record decode scratch"
        );
        assert!(
            cursor.row_decode.cached_value_ready(0),
            "CountIndexEqRun should leave the first index key cached in row-decode scratch"
        );
    }

    #[test]
    fn test_count_index_eq_run_honors_nocase_collation() {
        let mut engine = build_storage_index_engine_with_nocase_mixed_case_ordering();

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_probe = b.alloc_reg();
        let r_min_rowid = b.alloc_reg();
        let r_seek_key = b.alloc_reg();
        let r_count = b.alloc_reg();
        let r_next_key = b.alloc_reg();

        b.emit_op(
            Opcode::String8,
            0,
            r_probe,
            0,
            P4::Str("ALPHA".to_owned()),
            0,
        );
        b.emit_op(Opcode::Int64, 0, r_min_rowid, 0, P4::Int64(i64::MIN), 0);
        b.emit_op(Opcode::MakeRecord, r_probe, 2, r_seek_key, P4::None, 0);
        b.emit_jump_to_label(Opcode::SeekGE, 0, r_seek_key, done, P4::None, 0);
        b.emit_op(Opcode::Integer, 0, r_count, 0, P4::None, 0);
        b.emit_op(Opcode::CountIndexEqRun, 0, r_count, r_probe, P4::None, 0);
        b.emit_jump_to_label(Opcode::IfNullRow, 0, 0, done, P4::None, 0);
        b.emit_op(Opcode::Column, 0, 0, r_next_key, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r_count, 2, 0, P4::None, 0);

        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let program = b.finish().expect("program should build");
        let rows = execute_program_with_engine(&mut engine, &program);

        assert_eq!(
            rows,
            vec![vec![
                SqliteValue::Integer(2),
                SqliteValue::Text("beta".into())
            ]],
            "CountIndexEqRun must consume a full NOCASE-equivalent run and stop on the next distinct key"
        );
    }

    #[test]
    fn test_count_index_eq_run_integer_fast_lane_falls_back_for_float_keys() {
        let mut engine = VdbeEngine::new(8);
        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();

        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);

        assert!(
            engine.open_storage_cursor(0, index_root, true),
            "index storage cursor should open"
        );

        let cursor = engine
            .storage_cursors
            .get_mut(&0)
            .expect("storage cursor should exist");
        let key = encode_record(&[SqliteValue::Float(7.0), SqliteValue::Integer(1)]);
        cursor
            .cursor
            .index_insert(&cursor.cx, &key)
            .expect("float-key index entry should insert");
        assert!(
            cursor
                .cursor
                .first(&cursor.cx)
                .expect("cursor first should succeed")
        );

        let matches = storage_cursor_current_first_index_key_equals(
            cursor,
            &SqliteValue::Integer(7),
            false,
            "CountIndexEqRun: malformed index entry record",
        )
        .expect("float-key comparison should succeed");

        assert!(
            matches,
            "integer probe should still match a numerically equal float key via generic fallback"
        );
        assert!(
            cursor.row_decode.cached_value_ready(0),
            "generic fallback should still decode and cache the float key"
        );
    }

    #[test]
    fn test_count_index_eq_run_integer_probe_preserves_float_fallback_through_run_loop() {
        let mut engine = VdbeEngine::new(8);
        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();

        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);

        assert!(
            engine.open_storage_cursor(0, index_root, true),
            "index storage cursor should open"
        );

        let cursor = engine
            .storage_cursors
            .get_mut(&0)
            .expect("storage cursor should exist");
        for key in [
            encode_record(&[SqliteValue::Float(7.0), SqliteValue::Integer(1)]),
            encode_record(&[SqliteValue::Float(7.0), SqliteValue::Integer(2)]),
            encode_record(&[SqliteValue::Integer(8), SqliteValue::Integer(3)]),
        ] {
            cursor
                .cursor
                .index_insert(&cursor.cx, &key)
                .expect("index entry should insert");
        }

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_probe = b.alloc_reg();
        let r_min_rowid = b.alloc_reg();
        let r_seek_key = b.alloc_reg();
        let r_count = b.alloc_reg();
        let r_next_key = b.alloc_reg();

        b.emit_op(Opcode::Integer, 7, r_probe, 0, P4::None, 0);
        b.emit_op(Opcode::Int64, 0, r_min_rowid, 0, P4::Int64(i64::MIN), 0);
        b.emit_op(Opcode::MakeRecord, r_probe, 2, r_seek_key, P4::None, 0);
        b.emit_jump_to_label(Opcode::SeekGE, 0, r_seek_key, done, P4::None, 0);
        b.emit_op(Opcode::Integer, 0, r_count, 0, P4::None, 0);
        b.emit_op(Opcode::CountIndexEqRun, 0, r_count, r_probe, P4::None, 0);
        b.emit_jump_to_label(Opcode::IfNullRow, 0, 0, done, P4::None, 0);
        b.emit_op(Opcode::Column, 0, 0, r_next_key, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r_count, 2, 0, P4::None, 0);

        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let program = b.finish().expect("program should build");
        let rows = execute_program_with_engine(&mut engine, &program);

        assert_eq!(
            rows,
            vec![vec![SqliteValue::Integer(2), SqliteValue::Integer(8)]],
            "integer-probe CountIndexEqRun should still count numerically equal float keys before stopping on the next distinct key"
        );
    }

    #[test]
    fn test_count_index_eq_run_local_integer_segment_preserves_float_fallback() {
        let mut engine = VdbeEngine::new(8);
        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();

        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);

        assert!(
            engine.open_storage_cursor(0, index_root, true),
            "index storage cursor should open"
        );

        let cursor = engine
            .storage_cursors
            .get_mut(&0)
            .expect("storage cursor should exist");
        for key in [
            encode_record(&[SqliteValue::Integer(7), SqliteValue::Integer(1)]),
            encode_record(&[SqliteValue::Integer(7), SqliteValue::Integer(2)]),
            encode_record(&[SqliteValue::Float(7.0), SqliteValue::Integer(3)]),
            encode_record(&[SqliteValue::Integer(8), SqliteValue::Integer(4)]),
        ] {
            cursor
                .cursor
                .index_insert(&cursor.cx, &key)
                .expect("index entry should insert");
        }

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_probe = b.alloc_reg();
        let r_min_rowid = b.alloc_reg();
        let r_seek_key = b.alloc_reg();
        let r_count = b.alloc_reg();
        let r_next_key = b.alloc_reg();

        b.emit_op(Opcode::Integer, 7, r_probe, 0, P4::None, 0);
        b.emit_op(Opcode::Int64, 0, r_min_rowid, 0, P4::Int64(i64::MIN), 0);
        b.emit_op(Opcode::MakeRecord, r_probe, 2, r_seek_key, P4::None, 0);
        b.emit_jump_to_label(Opcode::SeekGE, 0, r_seek_key, done, P4::None, 0);
        b.emit_op(Opcode::Integer, 0, r_count, 0, P4::None, 0);
        b.emit_op(Opcode::CountIndexEqRun, 0, r_count, r_probe, P4::None, 0);
        b.emit_jump_to_label(Opcode::IfNullRow, 0, 0, done, P4::None, 0);
        b.emit_op(Opcode::Column, 0, 0, r_next_key, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r_count, 2, 0, P4::None, 0);

        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let program = b.finish().expect("program should build");
        let rows = execute_program_with_engine(&mut engine, &program);

        assert_eq!(
            rows,
            vec![vec![SqliteValue::Integer(3), SqliteValue::Integer(8)]],
            "CountIndexEqRun should preserve the float generic fallback after consuming a local integer segment"
        );
    }

    #[test]
    fn test_like_const_fast_handles_match_not_and_null() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_input = b.alloc_reg();
            let r_result = b.alloc_reg();

            b.emit_op(
                Opcode::String8,
                0,
                r_input,
                0,
                P4::Str("Document 123".to_owned()),
                0,
            );
            b.emit_op(
                Opcode::LikeConstFast,
                r_input,
                r_result,
                SqlLikeFastPathKind::Prefix.opcode_tag(),
                P4::Str("Document 1".to_owned()),
                0,
            );
            b.emit_op(Opcode::ResultRow, r_result, 1, 0, P4::None, 0);

            b.emit_op(Opcode::String8, 0, r_input, 0, P4::Str("zzz".to_owned()), 0);
            b.emit_op(
                Opcode::LikeConstFast,
                r_input,
                r_result,
                SqlLikeFastPathKind::Prefix.opcode_tag(),
                P4::Str("Document 1".to_owned()),
                1,
            );
            b.emit_op(Opcode::ResultRow, r_result, 1, 0, P4::None, 0);

            b.emit_op(Opcode::Null, 0, r_input, 0, P4::None, 0);
            b.emit_op(
                Opcode::LikeConstFast,
                r_input,
                r_result,
                SqlLikeFastPathKind::Prefix.opcode_tag(),
                P4::Str("Document 1".to_owned()),
                0,
            );
            b.emit_op(Opcode::ResultRow, r_result, 1, 0, P4::None, 0);

            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Integer(1)],
                vec![SqliteValue::Integer(1)],
                vec![SqliteValue::Null],
            ],
            "LikeConstFast should preserve LIKE match, NOT LIKE inversion, and NULL propagation"
        );
    }

    #[test]
    fn test_no_conflict_opcode_reset_for_reuse_reseeds_clean_scratch_state() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let conflict_key = encode_record(&[SqliteValue::Integer(7)]);
        let miss_key = encode_record(&[SqliteValue::Integer(9)]);
        let program = build_no_conflict_opcode_probe_program(
            conflict_key.clone(),
            miss_key.clone(),
            vec![0x80],
        );
        let expected_rows = vec![
            vec![SqliteValue::Integer(0)],
            vec![SqliteValue::Integer(1)],
            vec![SqliteValue::Integer(1)],
        ];

        let mut engine = VdbeEngine::new(program.register_count());
        let (installed_conflict_key, installed_miss_key) =
            install_duplicate_prefix_index_fixture(&mut engine, 0);
        assert_eq!(installed_conflict_key, conflict_key);
        assert_eq!(installed_miss_key, miss_key);

        let first_rows = execute_program_with_engine(&mut engine, &program);
        assert_eq!(first_rows, expected_rows);

        let reset_cx = Cx::new();
        engine.reset_for_reuse(program.register_count(), &reset_cx, PageSize::DEFAULT);
        assert!(engine.storage_cursors.is_empty());
        assert!(engine.results().is_empty());

        let (reinstalled_conflict_key, reinstalled_miss_key) =
            install_duplicate_prefix_index_fixture(&mut engine, 0);
        assert_eq!(reinstalled_conflict_key, conflict_key);
        assert_eq!(reinstalled_miss_key, miss_key);
        let cursor = engine
            .storage_cursors
            .get(&0)
            .expect("storage cursor should be reinstalled");
        assert!(cursor.payload_buf.is_empty());
        assert!(cursor.target_vals_buf.is_empty());
        assert!(cursor.cur_vals_buf.is_empty());

        let second_rows = execute_program_with_engine(&mut engine, &program);
        assert_eq!(second_rows, expected_rows);
    }

    #[test]
    fn test_compare_index_prefix_keys_honors_desc_flags() {
        let registry = Mutex::new(CollationRegistry::new());
        let lhs = vec![SqliteValue::Integer(20), SqliteValue::Integer(2)];
        let rhs = vec![SqliteValue::Integer(10), SqliteValue::Integer(1)];
        let coll_guard = registry.lock().unwrap();

        assert_eq!(
            compare_index_prefix_keys(&lhs, &rhs, 1, &[true], &[], &coll_guard),
            Ordering::Less,
            "descending index comparison should treat the larger key as earlier"
        );
        assert_eq!(
            compare_index_prefix_keys(&lhs, &rhs, 1, &[false], &[], &coll_guard),
            Ordering::Greater,
            "ascending index comparison should keep natural integer ordering"
        );
        assert_eq!(
            compare_index_prefix_keys(&lhs, &rhs, 0, &[true], &[], &coll_guard),
            Ordering::Less,
            "zero key_columns should still compare the leading term"
        );
    }

    #[test]
    fn test_storage_cursor_first_index_key_compare_honors_desc_flag() {
        let mut engine = VdbeEngine::new(8);
        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();

        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        engine.set_index_desc_flags_by_root_page(HashMap::from([(index_root, vec![true])]));

        assert!(
            engine.open_storage_cursor(0, index_root, true),
            "descending index storage cursor should open"
        );

        let cursor = engine
            .storage_cursors
            .get_mut(&0)
            .expect("storage cursor should exist");
        for key in [
            encode_record(&[SqliteValue::Integer(10), SqliteValue::Integer(1)]),
            encode_record(&[SqliteValue::Integer(20), SqliteValue::Integer(2)]),
        ] {
            cursor
                .cursor
                .index_insert(&cursor.cx, &key)
                .expect("descending index key should insert");
        }
        assert!(
            cursor.cursor.first(&cursor.cx).expect("first should work"),
            "descending index should contain entries"
        );

        let registry = Arc::clone(&engine.collation_registry);
        let coll_guard = registry.lock().unwrap_or_else(|err| err.into_inner());
        let cmp = storage_cursor_current_first_index_key_compare(
            cursor,
            &SqliteValue::Integer(10),
            false,
            "IdxCmp: malformed index record at cursor position",
            true,
            None,
            &coll_guard,
        )
        .expect("first-key compare should succeed");

        assert_eq!(
            cmp,
            Ordering::Less,
            "larger integer keys sort before smaller keys in a DESC index"
        );
    }

    #[test]
    fn test_idxgt_single_column_prefix_ignores_trailing_rowid() {
        let mut engine = VdbeEngine::new(8);
        let mut db = MemDatabase::new();
        let index_root = db.allocate_root_page();

        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);

        assert!(
            engine.open_storage_cursor(0, index_root, true),
            "index storage cursor should open"
        );

        let cursor = engine
            .storage_cursors
            .get_mut(&0)
            .expect("storage cursor should exist");
        for key in [
            encode_record(&[SqliteValue::Text("alpha".into()), SqliteValue::Integer(1)]),
            encode_record(&[SqliteValue::Text("alpha".into()), SqliteValue::Integer(2)]),
            encode_record(&[SqliteValue::Text("beta".into()), SqliteValue::Integer(3)]),
        ] {
            cursor
                .cursor
                .index_insert(&cursor.cx, &key)
                .expect("index key should insert");
        }

        let mut b = ProgramBuilder::new();
        let done = b.emit_label();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

        let r_probe = b.alloc_reg();
        let r_low_rowid = b.alloc_reg();
        let r_seek_key = b.alloc_reg();
        let r_out = b.alloc_reg();

        b.emit_op(
            Opcode::String8,
            0,
            r_probe,
            0,
            P4::Str("alpha".to_owned()),
            0,
        );
        b.emit_op(Opcode::Int64, 0, r_low_rowid, 0, P4::Int64(i64::MIN), 0);
        b.emit_op(Opcode::MakeRecord, r_probe, 2, r_seek_key, P4::None, 0);
        b.emit_jump_to_label(Opcode::SeekGE, 0, r_seek_key, done, P4::None, 0);
        b.emit_jump_to_label(Opcode::IdxGT, 0, r_seek_key, done, P4::None, 1);
        b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);

        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let program = b.finish().expect("program should build");
        let rows = execute_program_with_engine(&mut engine, &program);

        assert_eq!(
            rows,
            vec![vec![SqliteValue::Integer(1)]],
            "IdxGT with p5=1 must compare only the equality prefix, not the rowid suffix"
        );
    }

    #[test]
    fn test_distinct_key_collated_honors_rtrim() {
        let base = distinct_key_collated(&[SqliteValue::Text("abc".into())], Some("RTRIM"));
        let padded = distinct_key_collated(&[SqliteValue::Text("abc  ".into())], Some("rtrim"));
        let tabbed = distinct_key_collated(&[SqliteValue::Text("abc\t".into())], Some("RTRIM"));

        assert_eq!(
            base, padded,
            "RTRIM DISTINCT keys must ignore trailing ASCII spaces"
        );
        assert_ne!(
            base, tabbed,
            "RTRIM DISTINCT keys must not trim non-space suffixes like tabs"
        );
    }

    #[test]
    fn test_open_storage_cursor_write_init_failure_does_not_fallback_to_mem() {
        use fsqlite_mvcc::{ConcurrentRegistry, InProcessPageLockTable};
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let mut engine = VdbeEngine::new(8);
        // Use a page number whose low byte is 0 so MockTransaction::get_page
        // returns a zero type byte, forcing the writable root-page init path.
        let root = 256;

        // Deliberately install concurrent context with an inactive handle.
        // SharedTxnPageIo::write_page will fail before touching pager state.
        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let snapshot = Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1));
        let (session_id, handle) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let session_id = guard
                .begin_concurrent(snapshot)
                .expect("session should register");
            let handle = guard.handle(session_id).expect("handle should exist");
            (session_id, handle)
        };
        handle.lock().mark_aborted();
        engine.set_transaction_concurrent(txn, session_id, handle, lock_table, commit_index, 5000);

        let opened = engine.open_storage_cursor(0, root, true);
        assert!(
            !opened,
            "write-init errors must fail cursor open instead of silently falling back to Mem"
        );
        assert!(
            !engine.storage_cursors.contains_key(&0),
            "failed open must not leave a cursor installed"
        );
    }

    #[test]
    fn test_open_storage_cursor_write_read_failure_does_not_fallback_to_mem() {
        use std::path::PathBuf;

        use fsqlite_pager::{MvccPager as _, SimplePager, TransactionMode};
        use fsqlite_vfs::MemoryVfs;

        let vfs = MemoryVfs::new();
        let path = PathBuf::from("/vdbe_write_read_failure_no_mem_fallback.db");
        let cx = Cx::new();
        let pager = SimplePager::open_with_cx(&cx, vfs, &path, PageSize::MIN).unwrap();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let mut db = MemDatabase::new();
        let root = 2;
        db.create_table_at(root, 1);

        let mut engine = VdbeEngine::new(8);
        engine.set_database(db);
        engine.set_transaction(txn);
        engine.set_reject_mem_fallback(false);

        let opened = engine.open_storage_cursor(0, root, true);
        assert!(
            !opened,
            "writable cursor opens must fail when pager reads error instead of falling back to Mem"
        );
        assert!(
            !engine.storage_cursors.contains_key(&0),
            "failed open must not leave a cursor installed"
        );
    }

    #[test]
    fn test_open_storage_cursor_falls_back_to_mem_without_txn() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        db.get_table_mut(root)
            .unwrap()
            .insert(1, vec![SqliteValue::Integer(100)]);

        let mut engine = VdbeEngine::new(8);
        engine.enable_storage_cursors(true);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);

        // Without a transaction, should fall back to Mem backend.
        let opened = engine.open_storage_cursor(0, root, false);
        assert!(opened);
        assert!(engine.storage_cursors.contains_key(&0));
    }

    #[test]
    fn test_open_storage_cursor_zero_page_with_txn_does_not_fallback_to_mem() {
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let mut engine = VdbeEngine::new(8);
        engine.set_transaction(txn);

        // MockTransaction synthesizes page bytes from the page number; page 256
        // yields first byte 0x00, simulating an uninitialized root page.
        let opened = engine.open_storage_cursor(0, 256, false);
        assert!(
            !opened,
            "transaction-backed opens must not silently fall back to MemPageStore"
        );
        assert!(
            !engine.storage_cursors.contains_key(&0),
            "failed open must not leave a cursor installed"
        );
    }

    #[test]
    fn test_open_storage_cursor_write_invalid_page_does_not_fallback_to_mem() {
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let mut db = MemDatabase::new();
        let root = 256;
        db.create_table_at(root, 1);

        let mut engine = VdbeEngine::new(8);
        engine.set_database(db);
        engine.set_transaction(txn);
        engine.set_reject_mem_fallback(false);

        let opened = engine.open_storage_cursor(0, root, true);
        assert!(
            !opened,
            "writable cursor opens must fail on invalid pager pages instead of falling back to Mem"
        );
        assert!(
            !engine.storage_cursors.contains_key(&0),
            "failed open must not leave a cursor installed"
        );
    }

    #[test]
    fn test_txn_cursor_open_close_lifecycle() {
        // Verify the TransactionPageIo cursor lifecycle:
        // set_transaction → open cursor → close cursor → take_transaction.
        // MockTransaction doesn't produce valid B-tree pages, so we don't
        // attempt navigation — that's tested via MemPageStore-backed tests.
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        // Open a read cursor — this creates a CursorBackend::Txn.
        b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);
        // Close the cursor immediately without navigation.
        b.emit_op(Opcode::Close, 0, 0, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        let prog = b.finish().expect("program should build");

        let mut engine = VdbeEngine::new(prog.register_count());
        engine.set_database(db);
        engine.set_transaction(txn);

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);

        // Verify transaction recovery after cursor lifecycle.
        engine.storage_cursors.clear();
        assert!(
            engine
                .take_transaction()
                .expect("take_transaction should succeed")
                .is_some()
        );
    }

    // ── bd-3pti: Seek opcode tests ───────────────────────────────────────

    #[test]
    fn test_seek_ge_with_storage_cursor() {
        // SeekGE(key=5): should position at first row with rowid >= 5.
        // Table has rows: 3, 5, 7, 9
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(3, vec![SqliteValue::Integer(30)]);
        table.insert(5, vec![SqliteValue::Integer(50)]);
        table.insert(7, vec![SqliteValue::Integer(70)]);
        table.insert(9, vec![SqliteValue::Integer(90)]);

        let rows = run_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let not_found = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);

            // Seek to rowid >= 5 (should land on rowid 5)
            b.emit_op(Opcode::Integer, 5, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SeekGE, 0, 1, not_found, P4::None, 0);

            // Read the column value at current position.
            b.emit_op(Opcode::Column, 0, 0, 2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 2, 1, 0, P4::None, 0);

            b.resolve_label(not_found);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0], vec![SqliteValue::Integer(50)]); // rowid 5, value 50
    }

    #[test]
    fn test_seek_ge_not_exact_match() {
        // SeekGE(key=4): should position at first row with rowid >= 4.
        // Table has rows: 3, 5, 7, 9 → should land on rowid 5
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(3, vec![SqliteValue::Integer(30)]);
        table.insert(5, vec![SqliteValue::Integer(50)]);
        table.insert(7, vec![SqliteValue::Integer(70)]);
        table.insert(9, vec![SqliteValue::Integer(90)]);

        let rows = run_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let not_found = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);

            // Seek to rowid >= 4 (should land on rowid 5, the next larger)
            b.emit_op(Opcode::Integer, 4, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SeekGE, 0, 1, not_found, P4::None, 0);

            b.emit_op(Opcode::Column, 0, 0, 2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 2, 1, 0, P4::None, 0);

            b.resolve_label(not_found);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0], vec![SqliteValue::Integer(50)]); // rowid 5, value 50
    }

    #[test]
    fn test_seek_gt_with_storage_cursor() {
        // SeekGT(key=5): should position at first row with rowid > 5.
        // Table has rows: 3, 5, 7, 9 → should land on rowid 7
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(3, vec![SqliteValue::Integer(30)]);
        table.insert(5, vec![SqliteValue::Integer(50)]);
        table.insert(7, vec![SqliteValue::Integer(70)]);
        table.insert(9, vec![SqliteValue::Integer(90)]);

        let rows = run_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let not_found = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);

            // Seek to rowid > 5 (should land on rowid 7)
            b.emit_op(Opcode::Integer, 5, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SeekGT, 0, 1, not_found, P4::None, 0);

            b.emit_op(Opcode::Column, 0, 0, 2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 2, 1, 0, P4::None, 0);

            b.resolve_label(not_found);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0], vec![SqliteValue::Integer(70)]); // rowid 7, value 70
    }

    #[test]
    fn test_seek_le_with_storage_cursor() {
        // SeekLE(key=5): should position at last row with rowid <= 5.
        // Table has rows: 3, 5, 7, 9 → should land on rowid 5
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(3, vec![SqliteValue::Integer(30)]);
        table.insert(5, vec![SqliteValue::Integer(50)]);
        table.insert(7, vec![SqliteValue::Integer(70)]);
        table.insert(9, vec![SqliteValue::Integer(90)]);

        let rows = run_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let not_found = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);

            // Seek to rowid <= 5 (should land on rowid 5)
            b.emit_op(Opcode::Integer, 5, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SeekLE, 0, 1, not_found, P4::None, 0);

            b.emit_op(Opcode::Column, 0, 0, 2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 2, 1, 0, P4::None, 0);

            b.resolve_label(not_found);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0], vec![SqliteValue::Integer(50)]); // rowid 5, value 50
    }

    #[test]
    fn test_seek_le_not_exact_match() {
        // SeekLE(key=6): should position at last row with rowid <= 6.
        // Table has rows: 3, 5, 7, 9 → should land on rowid 5
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(3, vec![SqliteValue::Integer(30)]);
        table.insert(5, vec![SqliteValue::Integer(50)]);
        table.insert(7, vec![SqliteValue::Integer(70)]);
        table.insert(9, vec![SqliteValue::Integer(90)]);

        let rows = run_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let not_found = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);

            // Seek to rowid <= 6 (should land on rowid 5)
            b.emit_op(Opcode::Integer, 6, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SeekLE, 0, 1, not_found, P4::None, 0);

            b.emit_op(Opcode::Column, 0, 0, 2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 2, 1, 0, P4::None, 0);

            b.resolve_label(not_found);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0], vec![SqliteValue::Integer(50)]); // rowid 5, value 50
    }

    #[test]
    fn test_seek_lt_with_storage_cursor() {
        // SeekLT(key=5): should position at last row with rowid < 5.
        // Table has rows: 3, 5, 7, 9 → should land on rowid 3
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(3, vec![SqliteValue::Integer(30)]);
        table.insert(5, vec![SqliteValue::Integer(50)]);
        table.insert(7, vec![SqliteValue::Integer(70)]);
        table.insert(9, vec![SqliteValue::Integer(90)]);

        let rows = run_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let not_found = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);

            // Seek to rowid < 5 (should land on rowid 3)
            b.emit_op(Opcode::Integer, 5, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SeekLT, 0, 1, not_found, P4::None, 0);

            b.emit_op(Opcode::Column, 0, 0, 2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 2, 1, 0, P4::None, 0);

            b.resolve_label(not_found);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0], vec![SqliteValue::Integer(30)]); // rowid 3, value 30
    }

    #[test]
    fn test_seek_ge_empty_table_jumps() {
        // SeekGE on empty table should jump to p2.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        // Table is empty.

        let rows = run_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let not_found = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);

            b.emit_op(Opcode::Integer, 5, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SeekGE, 0, 1, not_found, P4::None, 0);

            // This should NOT be reached.
            b.emit_op(Opcode::Integer, 999, 2, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 2, 1, 0, P4::None, 0);

            b.resolve_label(not_found);
            // Jump target - we output nothing to indicate the jump was taken.
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        // Empty table → no rows returned, jump to p2.
        assert_eq!(rows.len(), 0);
    }

    #[test]
    fn test_seek_lt_no_smaller_row_jumps() {
        // SeekLT(key=3) when smallest rowid is 3 should jump to p2.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(3, vec![SqliteValue::Integer(30)]);
        table.insert(5, vec![SqliteValue::Integer(50)]);

        let rows = run_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let not_found = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);

            // Seek to rowid < 3 (no such row → should jump)
            b.emit_op(Opcode::Integer, 3, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SeekLT, 0, 1, not_found, P4::None, 0);

            // This should NOT be reached.
            b.emit_op(Opcode::Integer, 999, 2, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 2, 1, 0, P4::None, 0);

            b.resolve_label(not_found);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        // No row < 3 → jump taken, no results.
        assert_eq!(rows.len(), 0);
    }

    // ── Swiss Tables (bd-3ta.7) integration tests ─────────────────────

    #[test]
    fn test_swiss_index_metrics_emitted_on_cursor_ops() {
        // Verify that SwissIndex operations work correctly through the
        // MemDatabase interface.  Probe metrics are only recorded when
        // TRACE-level tracing is enabled (cold path), so we only assert
        // functional correctness here.
        let mut db = MemDatabase::new();
        let root = db.create_table(2);
        assert!(root > 0, "create_table should return a valid root page");

        // Insert a row via MemDatabase to exercise SwissIndex lookups.
        db.get_table_mut(root).unwrap().insert_row(
            1,
            vec![SqliteValue::Integer(42), SqliteValue::Text("a".into())],
        );
        let table = db.get_table(root).unwrap();
        assert_eq!(table.rows.len(), 1, "table should contain the inserted row");
    }

    #[test]
    fn test_swiss_index_replaces_hashmap_in_engine() {
        // Smoke test: run a simple expression program to exercise the engine's
        // SwissIndex-based internal maps (sorters, cursors, aggregates).
        use fsqlite_btree::instrumentation::reset_btree_metrics;

        reset_btree_metrics();

        // Even a simple expression program exercises VdbeEngine construction
        // which initializes SwissIndex maps. The metrics counter is global, so
        // any cursor open/close in the test suite contributes.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::Integer, 42, 1, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0][0], SqliteValue::Integer(42));

        // The engine's internal SwissIndex maps (cursors, sorters, aggregates,
        // storage_cursors) are all SwissIndex now. If we got here without
        // panics, the drop-in replacement works.
    }

    // ── External Sort Tests (bd-1rw.4) ──────────────────────────────────

    /// Mutex to serialize tests that mutate global VDBE observability settings.
    ///
    /// JIT and metrics configuration are both process-global, so tests that
    /// toggle them must not run concurrently.
    static VDBE_OBSERVABILITY_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());

    fn run_sorter_metric_program() -> Vec<Vec<SqliteValue>> {
        run_program(|b| {
            let end = b.emit_label();
            let loop_start = b.emit_label();
            let empty = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_value = b.alloc_reg();
            let r_record = b.alloc_reg();
            let r_sorted = b.alloc_reg();

            b.emit_op(Opcode::SorterOpen, 0, 1, 0, P4::None, 0);

            for value in [50, 30, 10, 40, 20] {
                b.emit_op(Opcode::Integer, value, r_value, 0, P4::None, 0);
                b.emit_op(Opcode::MakeRecord, r_value, 1, r_record, P4::None, 0);
                b.emit_op(Opcode::SorterInsert, 0, r_record, 0, P4::None, 0);
            }

            b.emit_jump_to_label(Opcode::SorterSort, 0, 0, empty, P4::None, 0);
            b.resolve_label(loop_start);
            b.emit_op(Opcode::SorterData, 0, r_sorted, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_sorted, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::SorterNext, 0, 0, loop_start, P4::None, 0);
            b.resolve_label(empty);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        })
    }

    #[test]
    fn test_sort_metrics_emitted_on_sorter_sort() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_metrics_enabled = vdbe_metrics_enabled();
        set_vdbe_metrics_enabled(true);
        // Verify that sort row metrics are updated when a sorter sorts rows.
        // Delta-based: snapshot before/after to avoid racing with parallel tests.
        let before = vdbe_metrics_snapshot();
        let rows = run_sorter_metric_program();

        assert_eq!(rows.len(), 5);
        let after = vdbe_metrics_snapshot();
        let delta_sort_rows = after.sort_rows_total - before.sort_rows_total;
        let delta_spill = after.sort_spill_pages_total - before.sort_spill_pages_total;
        assert!(
            delta_sort_rows >= 5,
            "sort_rows_total delta should be >= 5, got {delta_sort_rows}",
        );
        // No spill expected for 5 tiny rows.
        assert_eq!(delta_spill, 0, "no spill expected for small dataset");
        set_vdbe_metrics_enabled(prev_metrics_enabled);
    }

    #[test]
    fn test_vdbe_metrics_can_be_disabled_off_hot_path() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_metrics_enabled = vdbe_metrics_enabled();
        reset_vdbe_metrics();
        set_vdbe_metrics_enabled(false);

        let before = vdbe_metrics_snapshot();
        let rows = run_sorter_metric_program();
        let after = vdbe_metrics_snapshot();

        assert_eq!(rows.len(), 5);
        assert_eq!(after.opcodes_executed_total, before.opcodes_executed_total);
        assert_eq!(after.statements_total, before.statements_total);
        assert_eq!(
            after.statement_duration_us_total,
            before.statement_duration_us_total
        );
        assert_eq!(after.sort_rows_total, before.sort_rows_total);
        assert_eq!(after.sort_spill_pages_total, before.sort_spill_pages_total);

        set_vdbe_metrics_enabled(prev_metrics_enabled);
    }

    #[test]
    fn test_decode_record_with_metrics_flag_bypasses_global_reload() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_metrics_enabled = vdbe_metrics_enabled();
        reset_vdbe_metrics();
        set_vdbe_metrics_enabled(false);

        let record = SqliteValue::Blob(
            encode_record(&[SqliteValue::Integer(7), SqliteValue::Text("hello".into())]).into(),
        );

        let before = vdbe_metrics_snapshot();
        let decoded_without_metrics =
            decode_record_with_metrics(&record, false).expect("record should decode");
        let after_without_metrics = vdbe_metrics_snapshot();
        assert_eq!(
            decoded_without_metrics,
            vec![SqliteValue::Integer(7), SqliteValue::Text("hello".into())]
        );
        assert_eq!(
            after_without_metrics.record_decode_calls_total,
            before.record_decode_calls_total
        );
        assert_eq!(
            after_without_metrics.decoded_values_total,
            before.decoded_values_total
        );

        let decoded_with_metrics =
            decode_record_with_metrics(&record, true).expect("record should decode");
        let after_with_metrics = vdbe_metrics_snapshot();
        assert_eq!(decoded_with_metrics, decoded_without_metrics);
        assert_eq!(
            after_with_metrics.record_decode_calls_total,
            after_without_metrics.record_decode_calls_total + 1
        );
        assert_eq!(
            after_with_metrics.decoded_values_total,
            after_without_metrics.decoded_values_total + 2
        );

        set_vdbe_metrics_enabled(prev_metrics_enabled);
    }

    #[test]
    fn test_storage_cursor_decode_cache_metrics_track_hits_misses_and_position_changes() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_metrics_enabled = vdbe_metrics_enabled();
        reset_vdbe_metrics();
        set_vdbe_metrics_enabled(true);

        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).expect("table should exist");
        table.insert(1, vec![SqliteValue::Integer(10)]);
        table.insert(2, vec![SqliteValue::Integer(20)]);

        let before = vdbe_metrics_snapshot();
        let rows = run_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let eof = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, eof, P4::None, 0);

            let body = b.current_addr();
            b.emit_op(Opcode::Column, 0, 0, 1, P4::None, 0);
            b.emit_op(Opcode::Column, 0, 0, 2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 1, 2, 0, P4::None, 0);

            let next_target =
                i32::try_from(body).expect("program counter should fit into i32 for tests");
            b.emit_op(Opcode::Next, 0, next_target, 0, P4::None, 0);
            b.resolve_label(eof);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        let after = vdbe_metrics_snapshot();

        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Integer(10), SqliteValue::Integer(10)],
                vec![SqliteValue::Integer(20), SqliteValue::Integer(20)],
            ]
        );
        assert_eq!(
            after.decode_cache_hits_total - before.decode_cache_hits_total,
            2
        );
        assert_eq!(
            after.decode_cache_misses_total - before.decode_cache_misses_total,
            2
        );
        assert_eq!(
            after.decode_cache_invalidations_position_total
                - before.decode_cache_invalidations_position_total,
            1
        );
        assert_eq!(
            after.decode_cache_invalidations_write_total
                - before.decode_cache_invalidations_write_total,
            0
        );
        assert_eq!(
            after.decode_cache_invalidations_pseudo_total
                - before.decode_cache_invalidations_pseudo_total,
            0
        );

        set_vdbe_metrics_enabled(prev_metrics_enabled);
    }

    #[test]
    fn test_pseudo_cursor_decode_cache_metrics_track_hits_and_blob_changes() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_metrics_enabled = vdbe_metrics_enabled();
        reset_vdbe_metrics();
        set_vdbe_metrics_enabled(true);

        let mut engine = VdbeEngine::new(2);
        engine.collect_vdbe_metrics = true;
        engine.cursors.insert(0, MemCursor::new_pseudo(1));

        engine.set_reg_fast(
            1,
            SqliteValue::Blob(
                encode_record(&[SqliteValue::Integer(7), SqliteValue::Text("alpha".into())]).into(),
            ),
        );

        let before = vdbe_metrics_snapshot();
        assert_eq!(
            engine
                .cursor_column(0, 0)
                .expect("pseudo row should decode"),
            SqliteValue::Integer(7)
        );
        assert_eq!(
            engine
                .cursor_column(0, 0)
                .expect("pseudo row cache should hit"),
            SqliteValue::Integer(7)
        );

        engine.set_reg_fast(
            1,
            SqliteValue::Blob(
                encode_record(&[SqliteValue::Integer(9), SqliteValue::Text("beta".into())]).into(),
            ),
        );
        assert_eq!(
            engine
                .cursor_column(0, 0)
                .expect("changed pseudo row should decode"),
            SqliteValue::Integer(9)
        );
        let after = vdbe_metrics_snapshot();

        assert_eq!(
            after.decode_cache_hits_total - before.decode_cache_hits_total,
            1
        );
        assert_eq!(
            after.decode_cache_misses_total - before.decode_cache_misses_total,
            2
        );
        assert_eq!(
            after.decode_cache_invalidations_pseudo_total
                - before.decode_cache_invalidations_pseudo_total,
            1
        );
        assert_eq!(
            after.decode_cache_invalidations_position_total
                - before.decode_cache_invalidations_position_total,
            0
        );
        assert_eq!(
            after.decode_cache_invalidations_write_total
                - before.decode_cache_invalidations_write_total,
            0
        );

        set_vdbe_metrics_enabled(prev_metrics_enabled);
    }

    #[test]
    fn test_sorter_decode_cache_metrics_track_hits_misses_and_position_changes() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_metrics_enabled = vdbe_metrics_enabled();
        reset_vdbe_metrics();
        set_vdbe_metrics_enabled(true);

        let mut sorter = SorterCursor::new(1, vec![SortKeyOrder::Asc], Vec::new());
        sorter
            .insert_row(
                vec![SqliteValue::Integer(1)],
                encode_record(&[SqliteValue::Integer(1), SqliteValue::Text("alpha".into())]),
            )
            .expect("sorter insert should succeed");
        sorter
            .insert_row(
                vec![SqliteValue::Integer(2)],
                encode_record(&[SqliteValue::Integer(2), SqliteValue::Text("beta".into())]),
            )
            .expect("sorter insert should succeed");
        sorter.sort().expect("sorter sort should succeed");
        sorter.position = Some(0);

        let mut engine = VdbeEngine::new(1);
        engine.collect_vdbe_metrics = true;
        engine.sorters.insert(0, sorter);

        let before = vdbe_metrics_snapshot();
        assert_eq!(
            engine
                .cursor_column(0, 1)
                .expect("first sorter decode should succeed"),
            SqliteValue::Text("alpha".into())
        );
        assert_eq!(
            engine
                .cursor_column(0, 1)
                .expect("second sorter read should hit cache"),
            SqliteValue::Text("alpha".into())
        );
        engine
            .sorters
            .get_mut(&0)
            .expect("sorter cursor should exist")
            .position = Some(1);
        assert_eq!(
            engine
                .cursor_column(0, 1)
                .expect("next sorter row should decode"),
            SqliteValue::Text("beta".into())
        );
        let after = vdbe_metrics_snapshot();

        assert_eq!(
            after.decode_cache_hits_total - before.decode_cache_hits_total,
            1
        );
        assert_eq!(
            after.decode_cache_misses_total - before.decode_cache_misses_total,
            2
        );
        assert_eq!(
            after.decode_cache_invalidations_position_total
                - before.decode_cache_invalidations_position_total,
            1
        );
        assert_eq!(
            after.decode_cache_invalidations_write_total
                - before.decode_cache_invalidations_write_total,
            0
        );
        assert_eq!(
            after.decode_cache_invalidations_pseudo_total
                - before.decode_cache_invalidations_pseudo_total,
            0
        );

        set_vdbe_metrics_enabled(prev_metrics_enabled);
    }

    #[test]
    fn test_storage_cursor_decode_cache_hits_wide_row_tail_column_after_eager_decode() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_metrics_enabled = vdbe_metrics_enabled();
        reset_vdbe_metrics();
        set_vdbe_metrics_enabled(true);

        let mut db = MemDatabase::new();
        let root = db.create_table(65);
        let row = (0_i64..65).map(SqliteValue::Integer).collect::<Vec<_>>();
        db.get_table_mut(root)
            .expect("table should exist")
            .insert(1, row);

        let before = vdbe_metrics_snapshot();
        let rows = run_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let eof = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(65), 0);
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, eof, P4::None, 0);
            b.emit_op(Opcode::Column, 0, 64, 1, P4::None, 0);
            b.emit_op(Opcode::Column, 0, 64, 2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 1, 2, 0, P4::None, 0);
            b.resolve_label(eof);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        let after = vdbe_metrics_snapshot();

        assert_eq!(
            rows,
            vec![vec![SqliteValue::Integer(64), SqliteValue::Integer(64)]]
        );
        assert_eq!(
            after.decode_cache_hits_total - before.decode_cache_hits_total,
            1
        );
        assert_eq!(
            after.decode_cache_misses_total - before.decode_cache_misses_total,
            1
        );

        set_vdbe_metrics_enabled(prev_metrics_enabled);
    }

    #[test]
    fn test_sorter_decode_cache_hits_wide_row_tail_column_after_eager_decode() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_metrics_enabled = vdbe_metrics_enabled();
        reset_vdbe_metrics();
        set_vdbe_metrics_enabled(true);

        let record = encode_record(&(0_i64..65).map(SqliteValue::Integer).collect::<Vec<_>>());
        let mut sorter = SorterCursor::new(1, vec![SortKeyOrder::Asc], Vec::new());
        sorter
            .insert_row(vec![SqliteValue::Integer(0)], record)
            .expect("sorter insert should succeed");
        sorter.sort().expect("sorter sort should succeed");
        sorter.position = Some(0);

        let mut engine = VdbeEngine::new(1);
        engine.collect_vdbe_metrics = true;
        engine.sorters.insert(0, sorter);

        let before = vdbe_metrics_snapshot();
        assert_eq!(
            engine
                .cursor_column(0, 64)
                .expect("first wide sorter decode should succeed"),
            SqliteValue::Integer(64)
        );
        assert_eq!(
            engine
                .cursor_column(0, 64)
                .expect("second wide sorter read should hit cache"),
            SqliteValue::Integer(64)
        );
        let after = vdbe_metrics_snapshot();

        assert_eq!(
            after.decode_cache_hits_total - before.decode_cache_hits_total,
            1
        );
        assert_eq!(
            after.decode_cache_misses_total - before.decode_cache_misses_total,
            1
        );

        set_vdbe_metrics_enabled(prev_metrics_enabled);
    }

    #[test]
    fn test_storage_cursor_wide_row_reuse_hits_cache_without_second_parse_into() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_metrics_enabled = vdbe_metrics_enabled();
        let prev_record_profile_enabled = fsqlite_types::record::record_profile_enabled();
        reset_vdbe_metrics();
        set_vdbe_metrics_enabled(true);
        fsqlite_types::record::reset_record_profile();
        fsqlite_types::record::set_record_profile_enabled(true);

        let mut db = MemDatabase::new();
        let root = db.create_table(65);
        let row = (0_i64..65).map(SqliteValue::Integer).collect::<Vec<_>>();
        db.get_table_mut(root)
            .expect("table should exist")
            .insert(1, row);

        let before_metrics = vdbe_metrics_snapshot();
        let before_record_profile = fsqlite_types::record::record_profile_snapshot();
        let rows = run_with_storage_cursors(db, |b| {
            let end = b.emit_label();
            let eof = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(65), 0);
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, eof, P4::None, 0);
            b.emit_op(Opcode::Column, 0, 64, 1, P4::None, 0);
            b.emit_op(Opcode::Column, 0, 64, 2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 1, 2, 0, P4::None, 0);
            b.resolve_label(eof);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        let after_metrics = vdbe_metrics_snapshot();
        let after_record_profile = fsqlite_types::record::record_profile_snapshot();

        assert_eq!(
            rows,
            vec![vec![SqliteValue::Integer(64), SqliteValue::Integer(64)]]
        );
        assert_eq!(
            after_metrics.decode_cache_hits_total - before_metrics.decode_cache_hits_total,
            1
        );
        assert_eq!(
            after_metrics.decode_cache_misses_total - before_metrics.decode_cache_misses_total,
            1
        );
        assert_eq!(
            after_record_profile
                .callsite_breakdown
                .vdbe_engine
                .parse_record_into_calls
                - before_record_profile
                    .callsite_breakdown
                    .vdbe_engine
                    .parse_record_into_calls,
            1
        );

        fsqlite_types::record::set_record_profile_enabled(prev_record_profile_enabled);
        set_vdbe_metrics_enabled(prev_metrics_enabled);
    }

    #[test]
    #[ignore = "manual perf evidence for bd-db300.2.2"]
    fn bench_vdbe_metrics_toggle_execute_hot_path() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_metrics_enabled = vdbe_metrics_enabled();
        let iterations = 20_000;

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::Integer, 42, 1, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        let prog = b.finish().expect("program should build");

        let run = |metrics_enabled| {
            set_vdbe_metrics_enabled(metrics_enabled);
            reset_vdbe_metrics();
            let mut engine = VdbeEngine::new(prog.register_count());
            let start = Instant::now();
            for _ in 0..iterations {
                let outcome = engine.execute(&prog).expect("execution should succeed");
                assert_eq!(outcome, ExecOutcome::Done);
                engine.results.clear();
            }
            start.elapsed()
        };

        let metrics_disabled = run(false);
        let metrics_enabled = run(true);
        eprintln!(
            "bd-db300.2.2 execute hot-path benchmark: iterations={iterations}, metrics_disabled_us={}, metrics_enabled_us={}",
            metrics_disabled.as_micros(),
            metrics_enabled.as_micros()
        );

        set_vdbe_metrics_enabled(prev_metrics_enabled);
    }

    #[test]
    fn test_jit_scaffold_metrics_compile_and_cache_hit() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_enabled = vdbe_jit_enabled();
        let prev_threshold = vdbe_jit_hot_threshold();
        let prev_capacity = vdbe_jit_cache_capacity();

        // Delta-based: snapshot before/after to avoid racing with parallel tests.
        set_vdbe_jit_enabled(true);
        let _ = set_vdbe_jit_hot_threshold(2);
        let _ = set_vdbe_jit_cache_capacity(8);
        let before = vdbe_jit_metrics_snapshot();

        for _ in 0..3 {
            let rows = run_program(|b| {
                let end = b.emit_label();
                b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
                b.emit_op(Opcode::Integer, 42, 1, 0, P4::None, 0);
                b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
                b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
                b.resolve_label(end);
            });
            assert_eq!(rows, vec![vec![SqliteValue::Integer(42)]]);
        }

        let after = vdbe_jit_metrics_snapshot();
        let delta_compilations = after.jit_compilations_total - before.jit_compilations_total;
        let delta_cache_hits = after.jit_cache_hits_total - before.jit_cache_hits_total;
        assert!(
            delta_compilations >= 1,
            "expected at least one JIT compile delta, got {delta_compilations}",
        );
        assert!(
            delta_cache_hits >= 1,
            "expected at least one JIT cache hit delta, got {delta_cache_hits}",
        );
        assert!(
            after.cache_entries >= 1,
            "expected non-empty JIT cache after hot runs"
        );

        set_vdbe_jit_enabled(prev_enabled);
        let _ = set_vdbe_jit_hot_threshold(prev_threshold);
        let _ = set_vdbe_jit_cache_capacity(prev_capacity);
    }

    #[test]
    fn test_jit_disabled_leaves_runtime_state_cold() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_enabled = vdbe_jit_enabled();
        let prev_threshold = vdbe_jit_hot_threshold();
        let prev_capacity = vdbe_jit_cache_capacity();

        set_vdbe_jit_enabled(false);
        let _ = set_vdbe_jit_hot_threshold(1);
        let _ = set_vdbe_jit_cache_capacity(8);
        reset_vdbe_jit_metrics();
        let before = vdbe_jit_metrics_snapshot();

        for _ in 0..3 {
            let rows = run_program(|b| {
                let end = b.emit_label();
                b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
                b.emit_op(Opcode::Integer, 42, 1, 0, P4::None, 0);
                b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
                b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
                b.resolve_label(end);
            });
            assert_eq!(rows, vec![vec![SqliteValue::Integer(42)]]);
        }

        let after = vdbe_jit_metrics_snapshot();
        assert_eq!(after.jit_compilations_total, before.jit_compilations_total);
        assert_eq!(
            after.jit_compile_failures_total,
            before.jit_compile_failures_total
        );
        assert_eq!(after.jit_triggers_total, before.jit_triggers_total);
        assert_eq!(after.jit_cache_hits_total, before.jit_cache_hits_total);
        assert_eq!(after.jit_cache_misses_total, before.jit_cache_misses_total);
        assert_eq!(after.cache_entries, before.cache_entries);

        set_vdbe_jit_enabled(prev_enabled);
        let _ = set_vdbe_jit_hot_threshold(prev_threshold);
        let _ = set_vdbe_jit_cache_capacity(prev_capacity);
    }

    #[test]
    fn test_jit_scaffold_compile_failure_falls_back_to_interpreter() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_enabled = vdbe_jit_enabled();
        let prev_threshold = vdbe_jit_hot_threshold();
        let prev_capacity = vdbe_jit_cache_capacity();

        // Delta-based: snapshot before/after to avoid racing with parallel tests.
        set_vdbe_jit_enabled(true);
        let _ = set_vdbe_jit_hot_threshold(1);
        let _ = set_vdbe_jit_cache_capacity(8);
        let before = vdbe_jit_metrics_snapshot();

        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        db.get_table_mut(root)
            .expect("table should exist")
            .insert_row(7, vec![SqliteValue::Integer(700)]);

        let rows = run_with_storage_cursors(db, |b| {
            let start = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, start, P4::None, 0);
            b.resolve_label(start);
            b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);
            let empty = b.emit_label();
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, empty, P4::None, 0);
            b.emit_op(Opcode::Column, 0, 0, 1, P4::None, 0);
            b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
            b.resolve_label(empty);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Integer(700)]]);
        let after = vdbe_jit_metrics_snapshot();
        let delta_failures = after.jit_compile_failures_total - before.jit_compile_failures_total;
        assert!(
            delta_failures >= 1,
            "expected at least one JIT compile failure delta, got {delta_failures}",
        );

        set_vdbe_jit_enabled(prev_enabled);
        let _ = set_vdbe_jit_hot_threshold(prev_threshold);
        let _ = set_vdbe_jit_cache_capacity(prev_capacity);
    }

    #[test]
    fn test_jit_scaffold_distinguishes_programs_that_only_differ_in_p4() {
        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_enabled = vdbe_jit_enabled();
        let prev_threshold = vdbe_jit_hot_threshold();
        let prev_capacity = vdbe_jit_cache_capacity();

        set_vdbe_jit_enabled(true);
        let _ = set_vdbe_jit_hot_threshold(1);
        let _ = set_vdbe_jit_cache_capacity(8);
        reset_vdbe_jit_metrics();
        let before = vdbe_jit_metrics_snapshot();

        let alpha_rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::String8, 0, 1, 0, P4::Str("alpha".to_owned()), 0);
            b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(alpha_rows, vec![vec![SqliteValue::Text("alpha".into())]]);

        let beta_rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            b.emit_op(Opcode::String8, 0, 1, 0, P4::Str("beta".to_owned()), 0);
            b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(beta_rows, vec![vec![SqliteValue::Text("beta".into())]]);

        let after = vdbe_jit_metrics_snapshot();
        let delta_compilations = after.jit_compilations_total - before.jit_compilations_total;
        let delta_cache_hits = after.jit_cache_hits_total - before.jit_cache_hits_total;
        assert!(
            delta_compilations >= 2,
            "expected both distinct P4 programs to compile separately, got {delta_compilations}",
        );
        assert_eq!(
            delta_cache_hits, 0,
            "programs that only differ in P4 must not alias in the JIT cache",
        );

        reset_vdbe_jit_metrics();
        set_vdbe_jit_enabled(prev_enabled);
        let _ = set_vdbe_jit_hot_threshold(prev_threshold);
        let _ = set_vdbe_jit_cache_capacity(prev_capacity);
    }

    #[test]
    fn test_jit_cache_hit_arc_clone_microbench() {
        let value_sources = (0..24).map(InsertValueSource::Binding).collect::<Vec<_>>();
        let compiled = CompiledProgram::SimpleInsert(SimpleInsertTemplate {
            cursor_id: 0,
            root_page: 2,
            num_cols: i32::try_from(value_sources.len()).expect("test value count fits i32"),
            value_sources,
            affinity: Some("BBBBBBBBBBBBBBBBBBBBBBBB".to_owned()),
            record_p4: P4::None,
            record_builder: CompiledRecordBuilder::Generic,
            insert_flags: 2,
        });
        let shared = Arc::new(compiled.clone());
        let iterations = 50_000_u64;

        let direct_started = Instant::now();
        let mut direct_acc = 0_u64;
        for _ in 0..iterations {
            let cloned = std::hint::black_box(compiled.clone());
            direct_acc = direct_acc.wrapping_add(estimate_compiled_program_size(&cloned));
        }
        let direct_clone_ns = direct_started.elapsed().as_nanos();

        let arc_started = Instant::now();
        let mut arc_acc = 0_usize;
        for _ in 0..iterations {
            let cloned = std::hint::black_box(Arc::clone(&shared));
            arc_acc = arc_acc.wrapping_add(Arc::strong_count(&cloned));
        }
        let arc_clone_ns = arc_started.elapsed().as_nanos();

        std::hint::black_box((direct_acc, arc_acc));
        eprintln!(
            "bd-db300.2 jit cache-hit clone microbench: iterations={iterations}, deep_clone_ns={direct_clone_ns}, arc_clone_ns={arc_clone_ns}, template_size_bytes={}",
            estimate_compiled_program_size(shared.as_ref())
        );
        assert!(direct_acc > 0);
        assert!(arc_acc > 0);
        assert_eq!(Arc::strong_count(&shared), 1);
    }

    #[test]
    fn test_sorter_spill_to_disk_under_low_threshold() {
        // Set an artificially low spill threshold to trigger disk spill
        // with a small dataset, then verify the external merge produces
        // correct sorted output.

        // Build the sorter cursor directly to test spill logic.
        let mut sorter = SorterCursor::new(1, vec![SortKeyOrder::Asc], Vec::new());
        // Set threshold to 1 byte to force immediate spill on first insert.
        sorter.spill_threshold = 1;

        // Insert several rows — each should trigger a spill.
        // The blob must be a valid serialized record so that the spill
        // read-back path (`parse_record_prefix`) can decode key columns.
        for value in [50i64, 30, 10, 40, 20] {
            let vals = vec![SqliteValue::Integer(value)];
            let blob = serialize_record(&vals);
            sorter
                .insert_row(vals, blob)
                .expect("insert should succeed");
        }

        // We should have spilled runs.
        assert!(
            !sorter.spill_runs.is_empty(),
            "low threshold should cause spills"
        );
        let spill_count = sorter.spill_runs.len();
        assert!(
            sorter.spill_pages_total > 0,
            "spill_pages_total should be > 0"
        );

        // Sort (triggers merge).
        sorter.sort().expect("sort should succeed");

        // After merge, all runs should be cleaned up.
        assert!(sorter.spill_runs.is_empty(), "runs should be drained");

        // Verify sorted order.
        let values: Vec<i64> = sorter
            .rows
            .iter()
            .map(|r| r.values[0].to_integer())
            .collect();
        assert_eq!(values, vec![10, 20, 30, 40, 50]);

        // All 5 rows should have been counted (spill batches + in-memory remainder).
        assert!(
            sorter.rows_sorted_total >= 5,
            "rows_sorted_total should be >= 5, got {}",
            sorter.rows_sorted_total
        );
        // At least one spill run was created.
        assert!(spill_count >= 1, "at least one spill run expected");
    }

    #[test]
    fn test_sorter_reset_cleans_spill_state() {
        // Verify that reset() clears in-memory rows, position, and spill runs.
        let mut sorter = SorterCursor::new(1, vec![SortKeyOrder::Asc], Vec::new());
        sorter.spill_threshold = 1;

        for value in [3i64, 1, 2] {
            sorter
                .insert_row(vec![SqliteValue::Integer(value)], vec![])
                .expect("insert should succeed");
        }
        assert!(!sorter.spill_runs.is_empty());

        sorter.reset();

        assert!(sorter.rows.is_empty(), "rows should be cleared");
        assert!(sorter.position.is_none(), "position should be None");
        assert!(sorter.spill_runs.is_empty(), "spill_runs should be cleared");
        assert_eq!(sorter.memory_used, 0, "memory_used should be 0");
    }

    #[test]
    fn test_sorter_desc_key_order_with_external_merge() {
        // Verify that DESC sort order works correctly through the external
        // merge path.
        let mut sorter = SorterCursor::new(1, vec![SortKeyOrder::Desc], Vec::new());
        sorter.spill_threshold = 1;

        for value in [10i64, 50, 30, 20, 40] {
            let vals = vec![SqliteValue::Integer(value)];
            let blob = serialize_record(&vals);
            sorter
                .insert_row(vals, blob)
                .expect("insert should succeed");
        }

        sorter.sort().expect("sort should succeed");

        let values: Vec<i64> = sorter
            .rows
            .iter()
            .map(|r| r.values[0].to_integer())
            .collect();
        assert_eq!(values, vec![50, 40, 30, 20, 10]);
    }

    #[test]
    fn test_sorter_multi_column_key_with_mixed_order() {
        // Test sorting with 2 key columns: first ASC, second DESC.
        let mut sorter =
            SorterCursor::new(2, vec![SortKeyOrder::Asc, SortKeyOrder::Desc], Vec::new());

        // Insert rows: (group, value)
        for (group, value) in [(1i64, 30i64), (2, 10), (1, 20), (2, 40), (1, 10)] {
            sorter
                .insert_row(
                    vec![SqliteValue::Integer(group), SqliteValue::Integer(value)],
                    vec![],
                )
                .expect("insert should succeed");
        }

        sorter.sort().expect("sort should succeed");

        let values: Vec<(i64, i64)> = sorter
            .rows
            .iter()
            .map(|r| (r.values[0].to_integer(), r.values[1].to_integer()))
            .collect();
        // Group 1 first (ASC), within group value DESC: 30, 20, 10.
        // Group 2 second, within group value DESC: 40, 10.
        assert_eq!(values, vec![(1, 30), (1, 20), (1, 10), (2, 40), (2, 10)]);
    }

    #[test]
    fn test_sorter_memory_estimation() {
        // Verify memory tracking increases with row insertions.
        let mut sorter = SorterCursor::new(1, vec![SortKeyOrder::Asc], Vec::new());
        assert_eq!(sorter.memory_used, 0);

        sorter
            .insert_row(vec![SqliteValue::Integer(42)], vec![])
            .expect("insert should succeed");
        let after_one = sorter.memory_used;
        assert!(after_one > 0, "memory should increase after insert");

        sorter
            .insert_row(vec![SqliteValue::Text("hello world".into())], vec![])
            .expect("insert should succeed");
        let after_two = sorter.memory_used;
        assert!(
            after_two > after_one,
            "memory should increase with text value"
        );
    }

    #[test]
    fn test_sorter_empty_sort() {
        // Sorting an empty sorter should succeed and leave rows empty.
        let mut sorter = SorterCursor::new(1, vec![SortKeyOrder::Asc], Vec::new());
        sorter.sort().expect("empty sort should succeed");
        assert!(sorter.rows.is_empty());
    }

    #[test]
    fn test_sorter_pure_inmemory_sort_path() {
        // Verify the fast in-memory path works when no spill occurs.
        let mut sorter = SorterCursor::new(1, vec![SortKeyOrder::Asc], Vec::new());
        // Default threshold is 100 MiB — won't spill.

        for value in [5i64, 3, 1, 4, 2] {
            sorter
                .insert_row(vec![SqliteValue::Integer(value)], vec![])
                .expect("insert should succeed");
        }

        assert!(sorter.spill_runs.is_empty(), "no spill expected");
        sorter.sort().expect("sort should succeed");

        let values: Vec<i64> = sorter
            .rows
            .iter()
            .map(|r| r.values[0].to_integer())
            .collect();
        assert_eq!(values, vec![1, 2, 3, 4, 5]);
        assert_eq!(sorter.rows_sorted_total, 5);
    }

    #[test]
    fn test_sorter_top_n_limit_keeps_only_best_rows() {
        let mut sorter = SorterCursor::with_collation_registry(
            1,
            vec![SortKeyOrder::Asc],
            Vec::new(),
            Arc::new(Mutex::new(CollationRegistry::new())),
            Some(3),
        );

        for value in [9i64, 1, 7, 3, 2] {
            sorter
                .insert_row(vec![SqliteValue::Integer(value)], vec![])
                .expect("insert should succeed");
        }

        assert_eq!(
            sorter.rows.len(),
            3,
            "top-N sorter should prune rows during insertion"
        );
        sorter.sort().expect("sort should succeed");

        let values: Vec<i64> = sorter
            .rows
            .iter()
            .map(|r| r.values[0].to_integer())
            .collect();
        assert_eq!(values, vec![1, 2, 3]);
    }

    #[test]
    fn test_sorter_top_n_limit_desc_keeps_rows_sorted_during_insert() {
        let mut sorter = SorterCursor::with_collation_registry(
            1,
            vec![SortKeyOrder::Desc],
            Vec::new(),
            Arc::new(Mutex::new(CollationRegistry::new())),
            Some(3),
        );

        for value in [1i64, 2, 3, 4, 5] {
            sorter
                .insert_row(vec![SqliteValue::Integer(value)], vec![])
                .expect("insert should succeed");
        }

        let retained: Vec<i64> = sorter
            .rows
            .iter()
            .map(|r| r.values[0].to_integer())
            .collect();
        assert_eq!(retained, vec![5, 4, 3]);

        sorter.sort().expect("sort should succeed");
        let sorted: Vec<i64> = sorter
            .rows
            .iter()
            .map(|r| r.values[0].to_integer())
            .collect();
        assert_eq!(sorted, vec![5, 4, 3]);
    }

    // ── bd-2ttd8.1: Pager routing and parity-cert tests ──────────────

    #[test]
    fn test_reject_mem_fallback_default_on() {
        // bd-zjisk.1: Parity-cert mode is enabled by default.
        let engine = VdbeEngine::new(8);
        assert!(engine.reject_mem_fallback);
    }

    #[test]
    fn test_set_reject_mem_fallback() {
        let mut engine = VdbeEngine::new(8);
        engine.set_reject_mem_fallback(true);
        assert!(engine.reject_mem_fallback);
        engine.set_reject_mem_fallback(false);
        assert!(!engine.reject_mem_fallback);
    }

    #[test]
    fn test_open_storage_cursor_mem_fallback_without_parity_cert() {
        // Without parity-cert mode, OpenRead should succeed via MemPageStore
        // fallback when no pager transaction is set.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).expect("table should exist");
        table.insert(1, vec![SqliteValue::Integer(42)]);

        let mut engine = VdbeEngine::new(8);
        engine.set_database(db);
        // Explicitly opt out of parity-cert mode to test fallback path.
        engine.set_reject_mem_fallback(false);

        // No txn_page_io set — should fall back to MemPageStore.
        assert!(engine.open_storage_cursor(0, root, false));
        assert!(engine.storage_cursors.get(&0).is_some());
    }

    #[test]
    fn test_open_storage_cursor_rejected_in_parity_cert_mode() {
        // In parity-cert mode (now the default), OpenRead should FAIL when
        // no pager transaction is available and MemPageStore fallback would
        // be used.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).expect("table should exist");
        table.insert(1, vec![SqliteValue::Integer(42)]);

        let mut engine = VdbeEngine::new(8);
        engine.set_database(db);
        // Default is already true (parity-cert mode), but be explicit.
        engine.set_reject_mem_fallback(true);

        // No txn_page_io set — parity-cert should reject the fallback.
        assert!(!engine.open_storage_cursor(0, root, false));
        assert!(engine.storage_cursors.get(&0).is_none());
    }

    #[test]
    fn test_open_storage_cursor_invalid_page_number() {
        // Root page 0 is invalid (PageNumber requires nonzero).
        let mut engine = VdbeEngine::new(8);
        assert!(!engine.open_storage_cursor(0, 0, false));
    }

    #[test]
    fn test_bd_2ttd8_set_transaction_enables_storage_cursors() {
        // set_transaction should auto-enable storage cursors and set txn_page_io.
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let mut engine = VdbeEngine::new(8);
        engine.set_transaction(txn);
        assert!(engine.storage_cursors_enabled);
        assert!(engine.txn_page_io.is_some());
    }

    #[test]
    fn test_storage_only_table_program_executes_without_attached_memdb() {
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();
        let root = 256;

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
        b.emit_op(Opcode::Integer, 1, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 42, 2, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, 2, 1, 3, P4::None, 0);
        b.emit_op(Opcode::Insert, 0, 3, 1, P4::None, 0);
        b.emit_op(Opcode::Count, 0, 4, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, 4, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        assert!(
            !prog.requires_attached_memdb(),
            "storage-only table bytecode should not require an attached MemDatabase"
        );

        let mut engine = VdbeEngine::new(prog.register_count());
        engine.set_transaction(txn);
        engine.set_reject_mem_fallback(true);

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        assert!(
            engine.all_cursors_are_txn_backed(),
            "storage-only hot path must stay on txn-backed cursors"
        );
        let rows: Vec<_> = engine
            .take_results()
            .into_iter()
            .map(|row| row.into_vec())
            .collect();
        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
        assert!(
            engine.take_database().is_none(),
            "the engine should not need an attached MemDatabase for this hot path"
        );
    }

    #[test]
    fn test_storage_only_nested_loop_join_executes_without_attached_memdb() {
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();
        let orders_root = 256;
        let customers_root = 257;

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenWrite, 0, orders_root, 0, P4::Int(2), 0);
        b.emit_op(Opcode::OpenWrite, 1, customers_root, 0, P4::Int(2), 0);

        b.emit_op(Opcode::Integer, 1, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 1, 2, 0, P4::None, 0);
        b.emit_op(Opcode::String8, 0, 3, 0, P4::Str("ann".to_owned()), 0);
        b.emit_op(Opcode::MakeRecord, 2, 2, 4, P4::None, 0);
        b.emit_op(Opcode::Insert, 1, 4, 1, P4::None, 0);

        b.emit_op(Opcode::Integer, 2, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 2, 2, 0, P4::None, 0);
        b.emit_op(Opcode::String8, 0, 3, 0, P4::Str("bob".to_owned()), 0);
        b.emit_op(Opcode::MakeRecord, 2, 2, 4, P4::None, 0);
        b.emit_op(Opcode::Insert, 1, 4, 1, P4::None, 0);

        b.emit_op(Opcode::Integer, 1, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 1, 2, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 10, 3, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, 2, 2, 4, P4::None, 0);
        b.emit_op(Opcode::Insert, 0, 4, 1, P4::None, 0);

        b.emit_op(Opcode::Integer, 2, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 2, 2, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 20, 3, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, 2, 2, 4, P4::None, 0);
        b.emit_op(Opcode::Insert, 0, 4, 1, P4::None, 0);

        b.emit_op(Opcode::Integer, 3, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 1, 2, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 30, 3, 0, P4::None, 0);
        b.emit_op(Opcode::MakeRecord, 2, 2, 4, P4::None, 0);
        b.emit_op(Opcode::Insert, 0, 4, 1, P4::None, 0);

        let done = b.emit_label();
        let next_order = b.emit_label();
        b.emit_jump_to_label(Opcode::Rewind, 0, 0, done, P4::None, 0);
        let order_body = b.current_addr();
        b.emit_op(Opcode::Column, 0, 0, 10, P4::None, 0);
        b.emit_op(Opcode::Column, 0, 1, 11, P4::None, 0);

        b.emit_jump_to_label(Opcode::Rewind, 1, 0, next_order, P4::None, 0);
        let customer_body = b.current_addr();
        b.emit_op(Opcode::Column, 1, 0, 12, P4::None, 0);
        let no_match = b.emit_label();
        b.emit_jump_to_label(Opcode::Ne, 10, 12, no_match, P4::None, 0);
        b.emit_op(Opcode::Column, 1, 1, 13, P4::None, 0);
        b.emit_op(Opcode::SCopy, 11, 14, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, 13, 2, 0, P4::None, 0);
        b.resolve_label(no_match);
        b.emit_op(
            Opcode::Next,
            1,
            i32::try_from(customer_body).expect("test program address should fit in i32"),
            0,
            P4::None,
            0,
        );

        b.resolve_label(next_order);
        b.emit_op(
            Opcode::Next,
            0,
            i32::try_from(order_body).expect("test program address should fit in i32"),
            0,
            P4::None,
            0,
        );
        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        assert!(
            !prog.requires_attached_memdb(),
            "pager-backed nested-loop join bytecode must not require an attached MemDatabase"
        );

        let mut engine = VdbeEngine::new(prog.register_count());
        engine.set_transaction(txn);
        engine.set_reject_mem_fallback(true);

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        assert!(
            engine.all_cursors_are_txn_backed(),
            "join scan cursors must stay on txn-backed storage cursors"
        );
        let rows: Vec<_> = engine
            .take_results()
            .into_iter()
            .map(|row| row.into_vec())
            .collect();
        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Text("ann".into()), SqliteValue::Integer(10)],
                vec![SqliteValue::Text("bob".into()), SqliteValue::Integer(20)],
                vec![SqliteValue::Text("ann".into()), SqliteValue::Integer(30)],
            ]
        );
        assert!(
            engine.take_database().is_none(),
            "the engine should not need an attached MemDatabase for join scans"
        );
    }

    #[test]
    fn test_open_read_opcode_with_mem_fallback() {
        // OpenRead via VDBE execution should succeed when MemDatabase has the
        // table, verifying the full cursor lifecycle.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).expect("table should exist");
        table.insert(1, vec![SqliteValue::Integer(100)]);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);
        // Rewind to first row.
        let halt_label = b.emit_label();
        b.emit_jump_to_label(Opcode::Rewind, 0, 0, halt_label, P4::None, 0);
        // Read column 0 into register 1.
        b.emit_op(Opcode::Column, 0, 0, 1, P4::None, 0);
        b.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
        b.resolve_label(halt_label);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.set_database(db);
        // Explicitly opt out of parity-cert to test the MemPageStore fallback.
        engine.set_reject_mem_fallback(false);

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);

        let results: Vec<_> = engine
            .take_results()
            .into_iter()
            .map(|v| v.into_vec())
            .collect();
        assert_eq!(results.len(), 1);
        assert_eq!(results[0], vec![SqliteValue::Integer(100)]);
    }

    #[test]
    fn test_open_write_insert_delete_cursor_lifecycle() {
        // Verify full cursor lifecycle: OpenWrite → Insert → Rewind →
        // Column → Delete → verify empty.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);

        // Insert rowid=1 with value 42.
        b.emit_op(Opcode::Integer, 1, 1, 0, P4::None, 0); // rowid in r1
        b.emit_op(Opcode::Integer, 42, 2, 0, P4::None, 0); // value in r2
        b.emit_op(Opcode::MakeRecord, 2, 1, 3, P4::None, 0); // record in r3
        b.emit_op(Opcode::Insert, 0, 3, 1, P4::None, 0);

        // Rewind and read back.
        let eof_label = b.emit_label();
        b.emit_jump_to_label(Opcode::Rewind, 0, 0, eof_label, P4::None, 0);
        b.emit_op(Opcode::Column, 0, 0, 4, P4::None, 0);
        b.emit_op(Opcode::ResultRow, 4, 1, 0, P4::None, 0);
        // Delete the row.
        b.emit_op(Opcode::Delete, 0, 0, 0, P4::None, 0);
        b.resolve_label(eof_label);

        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.set_database(db);
        // Explicitly opt out of parity-cert to test the MemPageStore fallback.
        engine.set_reject_mem_fallback(false);

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);

        let results: Vec<_> = engine
            .take_results()
            .into_iter()
            .map(|v| v.into_vec())
            .collect();
        assert_eq!(results.len(), 1);
        assert_eq!(results[0], vec![SqliteValue::Integer(42)]);
    }

    #[test]
    fn test_parity_cert_rejects_open_read_without_txn() {
        // In parity-cert mode, OpenRead should fail execution when no pager
        // transaction is available.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).expect("table should exist");
        table.insert(1, vec![SqliteValue::Integer(1)]);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.set_database(db);
        engine.set_reject_mem_fallback(true);

        let result = engine.execute(&prog);
        assert!(
            result.is_err(),
            "OpenRead should fail in parity-cert mode without txn"
        );
    }

    #[test]
    fn test_parity_cert_rejects_open_write_without_txn() {
        // In parity-cert mode, OpenWrite should also fail without a txn.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);

        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.set_database(db);
        engine.set_reject_mem_fallback(true);

        let result = engine.execute(&prog);
        assert!(
            result.is_err(),
            "OpenWrite should fail in parity-cert mode without txn"
        );
    }

    // ── bd-2ttd8.4: Backend-identity ratchet tests ──────────────────

    #[test]
    fn test_cursor_backend_kind_mem() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let mut engine = VdbeEngine::new(8);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);

        assert!(engine.open_storage_cursor(0, root, false));
        assert!(
            engine.has_mem_cursor(),
            "cursor should be mem-backed without txn"
        );
        assert!(!engine.all_cursors_are_txn_backed());
    }

    #[test]
    fn test_cursor_backend_kind_txn() {
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};
        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Deferred).unwrap();

        let mut engine = VdbeEngine::new(8);
        engine.set_database(MemDatabase::new());
        engine.set_transaction(txn);

        // Open cursor on page 1 (valid with pager txn).
        assert!(engine.open_storage_cursor(0, 1, false));
        assert!(
            engine.all_cursors_are_txn_backed(),
            "cursor should be txn-backed with pager transaction"
        );
        assert!(!engine.has_mem_cursor());
    }

    #[test]
    fn test_validate_parity_cert_invariant_no_cursors() {
        let mut engine = VdbeEngine::new(8);
        engine.set_reject_mem_fallback(true);
        assert!(
            engine.validate_parity_cert_invariant().is_ok(),
            "vacuously valid with no cursors"
        );
    }

    #[test]
    fn test_validate_parity_cert_invariant_with_txn_cursor() {
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};
        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Deferred).unwrap();

        let mut engine = VdbeEngine::new(8);
        engine.set_database(MemDatabase::new());
        engine.set_transaction(txn);
        engine.set_reject_mem_fallback(true);

        assert!(engine.open_storage_cursor(0, 1, false));
        assert!(
            engine.validate_parity_cert_invariant().is_ok(),
            "txn-backed cursor satisfies parity-cert invariant"
        );
    }

    #[test]
    fn test_validate_parity_cert_invariant_disabled_allows_mem() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let mut engine = VdbeEngine::new(8);
        engine.set_database(db);
        // Explicitly disable parity-cert — mem cursors allowed.
        engine.set_reject_mem_fallback(false);
        assert!(engine.open_storage_cursor(0, root, false));
        assert!(
            engine.validate_parity_cert_invariant().is_ok(),
            "parity-cert disabled should always pass"
        );
    }

    #[test]
    fn test_all_cursors_txn_backed_vacuous() {
        let engine = VdbeEngine::new(8);
        assert!(
            engine.all_cursors_are_txn_backed(),
            "no cursors → vacuously true"
        );
        assert!(!engine.has_mem_cursor(), "no cursors → no mem cursor");
    }

    #[test]
    fn test_cursor_kind_str_values() {
        let mut db = MemDatabase::new();
        let root = db.create_table(1);

        let mut engine = VdbeEngine::new(8);
        engine.set_database(db);
        engine.set_reject_mem_fallback(false);
        engine.open_storage_cursor(0, root, false);

        let sc = engine.storage_cursors.get(&0).unwrap();
        assert_eq!(sc.cursor.kind_str(), "mem");
    }

    #[test]
    fn test_ratchet_prevents_mem_cursor_creation_in_parity_mode() {
        // This is the core anti-regression ratchet: when parity-cert is
        // enabled and no txn is set, cursor creation MUST fail — it cannot
        // silently fall through to MemPageStore.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(1, vec![SqliteValue::Integer(99)]);

        let mut engine = VdbeEngine::new(8);
        engine.set_database(db);
        engine.set_reject_mem_fallback(true);

        // Attempt to open cursor — should fail.
        let opened = engine.open_storage_cursor(0, root, false);
        assert!(
            !opened,
            "ratchet must prevent cursor creation in parity-cert mode"
        );

        // Validate invariant still holds.
        assert!(engine.validate_parity_cert_invariant().is_ok());
        assert!(!engine.has_mem_cursor());
    }

    #[test]
    fn test_ratchet_allows_txn_cursor_in_parity_mode() {
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};
        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Deferred).unwrap();

        let mut engine = VdbeEngine::new(8);
        engine.set_database(MemDatabase::new());
        engine.set_transaction(txn);
        engine.set_reject_mem_fallback(true);

        // With txn set, cursor creation should succeed via pager path.
        let opened = engine.open_storage_cursor(0, 1, false);
        assert!(opened, "txn-backed cursor should work in parity-cert mode");
        assert!(engine.all_cursors_are_txn_backed());
        assert!(engine.validate_parity_cert_invariant().is_ok());
    }

    #[test]
    fn test_ratchet_multiple_cursors_mixed_rejection() {
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};
        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Deferred).unwrap();

        let mut engine = VdbeEngine::new(8);
        engine.set_database(MemDatabase::new());
        engine.set_transaction(txn);
        engine.set_reject_mem_fallback(true);

        // Open cursor 0 on page 1 — should succeed (txn path).
        assert!(engine.open_storage_cursor(0, 1, false));
        assert!(engine.all_cursors_are_txn_backed());

        // Attempt cursor 1 on non-existent high page — should still
        // succeed via txn path (MockMvccPager returns zero-filled pages).
        assert!(engine.open_storage_cursor(1, 1, false));
        assert!(engine.all_cursors_are_txn_backed());
        assert!(engine.validate_parity_cert_invariant().is_ok());
    }

    // ── RowSet opcode tests ──────────────────────────────────────

    #[test]
    fn test_rowset_add_and_read_returns_sorted() {
        // Add rowids 30, 10, 20 then read them back — should come out sorted.
        let rows = run_program(|b| {
            let end = b.emit_label();
            let exhausted = b.emit_label();
            let loop_start = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_val = b.alloc_reg();
            let r_out = b.alloc_reg();
            let rowset_reg = b.alloc_reg();

            // Add 30, 10, 20 to rowset
            for v in [30, 10, 20] {
                b.emit_op(Opcode::Integer, v, r_val, 0, P4::None, 0);
                b.emit_op(Opcode::RowSetAdd, rowset_reg, r_val, 0, P4::None, 0);
            }

            // Read loop: RowSetRead P1=rowset, P2=jump_when_exhausted, P3=output
            b.resolve_label(loop_start);
            b.emit_jump_to_label(
                Opcode::RowSetRead,
                rowset_reg,
                r_out,
                exhausted,
                P4::None,
                0,
            );
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, loop_start, P4::None, 0);

            b.resolve_label(exhausted);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        let vals: Vec<i64> = rows.into_iter().map(|row| row[0].to_integer()).collect();
        assert_eq!(vals, vec![10, 20, 30]);
    }

    #[test]
    fn test_rowset_deduplicates() {
        // Add the same value twice; read should return it once.
        let rows = run_program(|b| {
            let end = b.emit_label();
            let exhausted = b.emit_label();
            let loop_start = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_val = b.alloc_reg();
            let r_out = b.alloc_reg();
            let rowset_reg = b.alloc_reg();

            for v in [5, 5, 5] {
                b.emit_op(Opcode::Integer, v, r_val, 0, P4::None, 0);
                b.emit_op(Opcode::RowSetAdd, rowset_reg, r_val, 0, P4::None, 0);
            }

            b.resolve_label(loop_start);
            b.emit_jump_to_label(
                Opcode::RowSetRead,
                rowset_reg,
                r_out,
                exhausted,
                P4::None,
                0,
            );
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, loop_start, P4::None, 0);

            b.resolve_label(exhausted);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0][0].to_integer(), 5);
    }

    #[test]
    fn test_rowset_test_jumps_if_found() {
        // Add 42 to rowset, then test for 42 — should jump.
        let rows = run_program(|b| {
            let end = b.emit_label();
            let found = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_val = b.alloc_reg();
            let r_out = b.alloc_reg();
            let rowset_reg = b.alloc_reg();

            // Add 42
            b.emit_op(Opcode::Integer, 42, r_val, 0, P4::None, 0);
            b.emit_op(Opcode::RowSetAdd, rowset_reg, r_val, 0, P4::None, 0);

            // Test for 42 — should jump to `found`
            // RowSetTest: P1=rowset, P2=jump_if_found, P3=value register
            b.emit_jump_to_label(Opcode::RowSetTest, rowset_reg, r_val, found, P4::None, 0);
            // Not found path
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);

            // Found path
            b.resolve_label(found);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
    }

    #[test]
    fn test_rowset_test_falls_through_and_adds_if_not_found() {
        // RowSetTest on empty set: should fall through and add the value.
        let rows = run_program(|b| {
            let end = b.emit_label();
            let found = b.emit_label();
            let exhausted = b.emit_label();
            let loop_start = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_val = b.alloc_reg();
            let r_out = b.alloc_reg();
            let rowset_reg = b.alloc_reg();

            // Test for 99 on empty rowset — should fall through and add 99
            b.emit_op(Opcode::Integer, 99, r_val, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::RowSetTest, rowset_reg, r_val, found, P4::None, 0);

            // Fall-through: 99 was added, now read it back
            b.resolve_label(loop_start);
            b.emit_jump_to_label(
                Opcode::RowSetRead,
                rowset_reg,
                r_out,
                exhausted,
                P4::None,
                0,
            );
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, loop_start, P4::None, 0);

            b.resolve_label(found);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(exhausted);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0][0].to_integer(), 99);
    }

    // ── FK counter opcode tests ──────────────────────────────────

    #[test]
    fn test_fk_counter_and_fk_if_zero() {
        // FkCounter increments, FkIfZero tests for zero.
        let rows = run_program(|b| {
            let end = b.emit_label();
            let is_zero = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_out = b.alloc_reg();

            // Increment FK counter by 3
            b.emit_op(Opcode::FkCounter, 0, 3, 0, P4::None, 0);
            // Test if zero — should NOT jump (counter is 3)
            b.emit_jump_to_label(Opcode::FkIfZero, 0, 0, is_zero, P4::None, 0);
            // Decrement by 3
            b.emit_op(Opcode::FkCounter, 0, -3, 0, P4::None, 0);
            // Test if zero — SHOULD jump now
            b.emit_jump_to_label(Opcode::FkIfZero, 0, 0, is_zero, P4::None, 0);
            // Should not reach here
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);

            b.resolve_label(is_zero);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
    }

    // ── MemMax opcode test ───────────────────────────────────────

    #[test]
    fn test_memmax_stores_larger_value() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();

            // r1=50, r2=30 → MemMax(r1, r2) → r2=50
            b.emit_op(Opcode::Integer, 50, r1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 30, r2, 0, P4::None, 0);
            b.emit_op(Opcode::MemMax, r1, r2, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r2, 1, 0, P4::None, 0);

            // r1=10, r2=50 → MemMax(r1, r2) → r2 stays 50
            // Note: ResultRow clears registers, so r2 must be re-initialized.
            b.emit_op(Opcode::Integer, 10, r1, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 50, r2, 0, P4::None, 0);
            b.emit_op(Opcode::MemMax, r1, r2, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r2, 1, 0, P4::None, 0);

            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Integer(50)],
                vec![SqliteValue::Integer(50)],
            ]
        );
    }

    // ── OffsetLimit opcode test ──────────────────────────────────

    #[test]
    fn test_offset_limit_combines_values() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_limit = b.alloc_reg();
            let r_offset = b.alloc_reg();
            let r_combined = b.alloc_reg();

            // LIMIT=10, OFFSET=5 → combined=15
            b.emit_op(Opcode::Integer, 10, r_limit, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 5, r_offset, 0, P4::None, 0);
            b.emit_op(
                Opcode::OffsetLimit,
                r_limit,
                r_offset,
                r_combined,
                P4::None,
                0,
            );
            b.emit_op(Opcode::ResultRow, r_combined, 1, 0, P4::None, 0);

            // LIMIT=-1 (no limit), OFFSET=5 → combined=-1
            b.emit_op(Opcode::Integer, -1, r_limit, 0, P4::None, 0);
            b.emit_op(
                Opcode::OffsetLimit,
                r_limit,
                r_offset,
                r_combined,
                P4::None,
                0,
            );
            b.emit_op(Opcode::ResultRow, r_combined, 1, 0, P4::None, 0);

            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Integer(15)],
                vec![SqliteValue::Integer(-1)],
            ]
        );
    }

    // ── DecrJumpZero opcode test ─────────────────────────────────

    #[test]
    fn test_decr_jump_zero_via_hot_path() {
        // Pins the hot-path arm's behaviour to the main-match arm:
        // - counter > 1: decrement, fall through (pc += 1)
        // - counter == 1: decrement to 0, jump to p2
        // - counter == 0: fall through (no write, no jump)
        //
        // `ResultRow` drains its source registers via `take_reg_range`, so
        // the asserts below reflect that move-out.
        //
        // Trace (r_counter seeded to 2, r_hit to 0):
        //   DecrJumpZero   val=2→1, fall through          (r_hit=0)
        //   AddImm r_hit,1 r_hit=1
        //   ResultRow r_hit                               emits [1], r_hit=NULL
        //   DecrJumpZero   val=1→0, jump over AddImm      (r_hit=NULL)
        //   ResultRow r_hit                               emits [NULL]
        //   DecrJumpZero   val=0 not>0, fall through      (r_counter still 0)
        //   AddImm r_hit,1 r_hit = NULL.to_integer() + 1 = 1
        //   ResultRow r_counter                           emits [0]
        let rows = run_program(|b| {
            let end = b.emit_label();
            let skip1 = b.emit_label();
            let skip2 = b.emit_label();
            let skip3 = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_counter = b.alloc_reg();
            let r_hit = b.alloc_reg();
            b.emit_op(Opcode::Integer, 2, r_counter, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_hit, 0, P4::None, 0);

            b.emit_jump_to_label(Opcode::DecrJumpZero, r_counter, 0, skip1, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_hit, 1, 0, P4::None, 0);
            b.resolve_label(skip1);
            b.emit_op(Opcode::ResultRow, r_hit, 1, 0, P4::None, 0);

            b.emit_jump_to_label(Opcode::DecrJumpZero, r_counter, 0, skip2, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_hit, 1, 0, P4::None, 0);
            b.resolve_label(skip2);
            b.emit_op(Opcode::ResultRow, r_hit, 1, 0, P4::None, 0);

            b.emit_jump_to_label(Opcode::DecrJumpZero, r_counter, 0, skip3, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_hit, 1, 0, P4::None, 0);
            b.resolve_label(skip3);
            b.emit_op(Opcode::ResultRow, r_counter, 1, 0, P4::None, 0);

            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Integer(1)],
                vec![SqliteValue::Null],
                vec![SqliteValue::Integer(0)],
            ]
        );
    }

    // ── IfPos opcode test ────────────────────────────────────────

    #[test]
    fn test_if_pos_via_hot_path() {
        // Pins the hot-path arm's behaviour to the main-match arm:
        // - val > 0: subtract p3, jump to p2 (taken branch)
        // - val == 0: fall through (no write, no jump)
        // - val < 0: fall through (no write, no jump)
        //
        // `ResultRow` drains its source register via `take_reg_range`, so
        // the asserts below reflect that move-out.
        //
        // Trace (r_offset seeded to 2, r_hit to 0; each IfPos has p3=1):
        //   IfPos r_offset → val=2>0: val-1=1, jump skip1 (skip AddImm)  r_offset=1, r_hit=0
        //   skip1: ResultRow r_hit                                       emits [0], r_hit=NULL
        //   IfPos r_offset → val=1>0: val-1=0, jump skip2 (skip AddImm)  r_offset=0, r_hit=NULL
        //   skip2: ResultRow r_hit                                       emits [NULL]
        //   IfPos r_offset → val=0 not>0, fall through                   r_offset=0
        //   AddImm r_hit,1                                               r_hit = NULL.to_integer()+1 = 1
        //   skip3: ResultRow r_offset                                    emits [0]
        let rows = run_program(|b| {
            let end = b.emit_label();
            let skip1 = b.emit_label();
            let skip2 = b.emit_label();
            let skip3 = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_offset = b.alloc_reg();
            let r_hit = b.alloc_reg();
            b.emit_op(Opcode::Integer, 2, r_offset, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_hit, 0, P4::None, 0);

            // p3=1 (decrement-by-one OFFSET shape).
            b.emit_jump_to_label(Opcode::IfPos, r_offset, 1, skip1, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_hit, 1, 0, P4::None, 0);
            b.resolve_label(skip1);
            b.emit_op(Opcode::ResultRow, r_hit, 1, 0, P4::None, 0);

            b.emit_jump_to_label(Opcode::IfPos, r_offset, 1, skip2, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_hit, 1, 0, P4::None, 0);
            b.resolve_label(skip2);
            b.emit_op(Opcode::ResultRow, r_hit, 1, 0, P4::None, 0);

            b.emit_jump_to_label(Opcode::IfPos, r_offset, 1, skip3, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_hit, 1, 0, P4::None, 0);
            b.resolve_label(skip3);
            b.emit_op(Opcode::ResultRow, r_offset, 1, 0, P4::None, 0);

            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Integer(0)],
                vec![SqliteValue::Null],
                vec![SqliteValue::Integer(0)],
            ]
        );
    }

    // ── IsNull opcode test ───────────────────────────────────────

    #[test]
    fn test_is_null_via_hot_path() {
        // Pins the hot-path arm's behaviour to the main-match arm:
        // - val IS NULL: jump to p2 (taken branch)
        // - val IS NOT NULL: fall through (no jump, no register write)
        //
        // The hot-path arm only covers control flow — neither branch
        // mutates the source register, so the seeded value stays put
        // across each IsNull dispatch.
        //
        // `ResultRow` drains its source register via `take_reg_range`,
        // so the asserts below reflect that move-out.
        //
        // Trace (r_null seeded to NULL, r_int seeded to 7, r_hit to 0):
        //   IsNull r_null  → NULL: jump skip1 (skip AddImm)            r_hit=0
        //   skip1: ResultRow r_hit                                     emits [0], r_hit=NULL
        //   IsNull r_int   → not NULL: fall through                    r_hit=NULL
        //   AddImm r_hit,1 r_hit = NULL.to_integer()+1 = 1
        //   skip2: ResultRow r_hit                                     emits [1], r_hit=NULL
        //   IsNull r_null  → NULL: jump skip3 (skip AddImm)            r_hit=NULL
        //   skip3: ResultRow r_int                                     emits [7]
        let rows = run_program(|b| {
            let end = b.emit_label();
            let skip1 = b.emit_label();
            let skip2 = b.emit_label();
            let skip3 = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_null = b.alloc_reg();
            let r_int = b.alloc_reg();
            let r_hit = b.alloc_reg();
            // r_null left as the default Null seed; explicitly set the
            // others so the test reads as a complete state machine
            // even if `alloc_reg`'s initial value ever changes.
            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 7, r_int, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_hit, 0, P4::None, 0);

            b.emit_jump_to_label(Opcode::IsNull, r_null, 0, skip1, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_hit, 1, 0, P4::None, 0);
            b.resolve_label(skip1);
            b.emit_op(Opcode::ResultRow, r_hit, 1, 0, P4::None, 0);

            b.emit_jump_to_label(Opcode::IsNull, r_int, 0, skip2, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_hit, 1, 0, P4::None, 0);
            b.resolve_label(skip2);
            b.emit_op(Opcode::ResultRow, r_hit, 1, 0, P4::None, 0);

            b.emit_jump_to_label(Opcode::IsNull, r_null, 0, skip3, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_hit, 1, 0, P4::None, 0);
            b.resolve_label(skip3);
            b.emit_op(Opcode::ResultRow, r_int, 1, 0, P4::None, 0);

            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Integer(0)],
                vec![SqliteValue::Integer(1)],
                vec![SqliteValue::Integer(7)],
            ]
        );
    }

    #[test]
    fn test_not_null_main_dispatch() {
        // Pins the main interpreter arm's behaviour after the hot-dispatch
        // arm was removed:
        // - val IS NULL: fall through
        // - val IS NOT NULL: jump to p2
        let rows = run_program(|b| {
            let end = b.emit_label();
            let skip1 = b.emit_label();
            let skip2 = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_null = b.alloc_reg();
            let r_int = b.alloc_reg();
            let r_hit = b.alloc_reg();
            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 7, r_int, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_hit, 0, P4::None, 0);

            b.emit_jump_to_label(Opcode::NotNull, r_null, 0, skip1, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_hit, 1, 0, P4::None, 0);
            b.resolve_label(skip1);

            b.emit_jump_to_label(Opcode::NotNull, r_int, 0, skip2, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_hit, 1, 0, P4::None, 0);
            b.resolve_label(skip2);

            b.emit_op(Opcode::ResultRow, r_hit, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
    }

    #[test]
    fn test_if_not_via_hot_path() {
        // Pins the hot-path arm's behaviour to the main-match arm
        // across all three IfNot input classes:
        //   - truthy non-zero integer:    fall through (no jump)
        //   - falsy zero integer:         jump (taken)
        //   - NULL with p3=0:             fall through (don't jump)
        //   - NULL with p3=1:             jump (taken)
        //
        // The hot-path arm only covers control flow — neither branch
        // mutates the source register, so the seeded values stay put
        // across each IfNot dispatch.  Each block uses an AddImm
        // sentinel after the IfNot to prove which side ran.
        //
        // Trace (r_truthy=1, r_falsy=0, r_null=NULL, r_hit=0):
        //   IfNot r_truthy → 1 truthy: fall through; AddImm r_hit,1 → r_hit=1
        //   skip1: ResultRow r_hit                              emits [1], r_hit=NULL
        //   r_hit=0
        //   IfNot r_falsy  → 0 falsy: jump skip2 (skip AddImm); r_hit=0
        //   skip2: ResultRow r_hit                              emits [0], r_hit=NULL
        //   r_hit=0
        //   IfNot r_null,p3=0 → NULL && p3==0: fall through; AddImm r_hit,1 → r_hit=1
        //   skip3: ResultRow r_hit                              emits [1], r_hit=NULL
        //   r_hit=0
        //   IfNot r_null,p3=1 → NULL && p3!=0: jump skip4 (skip AddImm); r_hit=0
        //   skip4: ResultRow r_hit                              emits [0]
        let rows = run_program(|b| {
            let end = b.emit_label();
            let skip1 = b.emit_label();
            let skip2 = b.emit_label();
            let skip3 = b.emit_label();
            let skip4 = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_truthy = b.alloc_reg();
            let r_falsy = b.alloc_reg();
            let r_null = b.alloc_reg();
            let r_hit = b.alloc_reg();
            b.emit_op(Opcode::Integer, 1, r_truthy, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_falsy, 0, P4::None, 0);
            b.emit_op(Opcode::Null, 0, r_null, 0, P4::None, 0);

            // Block 1: truthy → fall through
            b.emit_op(Opcode::Integer, 0, r_hit, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::IfNot, r_truthy, 0, skip1, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_hit, 1, 0, P4::None, 0);
            b.resolve_label(skip1);
            b.emit_op(Opcode::ResultRow, r_hit, 1, 0, P4::None, 0);

            // Block 2: falsy → jump
            b.emit_op(Opcode::Integer, 0, r_hit, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::IfNot, r_falsy, 0, skip2, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_hit, 1, 0, P4::None, 0);
            b.resolve_label(skip2);
            b.emit_op(Opcode::ResultRow, r_hit, 1, 0, P4::None, 0);

            // Block 3: NULL with p3=0 → fall through
            b.emit_op(Opcode::Integer, 0, r_hit, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::IfNot, r_null, 0, skip3, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_hit, 1, 0, P4::None, 0);
            b.resolve_label(skip3);
            b.emit_op(Opcode::ResultRow, r_hit, 1, 0, P4::None, 0);

            // Block 4: NULL with p3=1 → jump
            b.emit_op(Opcode::Integer, 0, r_hit, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::IfNot, r_null, 1, skip4, P4::None, 0);
            b.emit_op(Opcode::AddImm, r_hit, 1, 0, P4::None, 0);
            b.resolve_label(skip4);
            b.emit_op(Opcode::ResultRow, r_hit, 1, 0, P4::None, 0);

            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Integer(1)],
                vec![SqliteValue::Integer(0)],
                vec![SqliteValue::Integer(1)],
                vec![SqliteValue::Integer(0)],
            ]
        );
    }

    // ── Rowid opcode test ────────────────────────────────────────

    #[test]
    fn test_rowid_via_hot_path() {
        // Pins the hot-path arm's behaviour to the main-match arm
        // across the two Rowid input classes that share a single
        // `cursor_rowid` call:
        //   - storage cursor positioned on a row → emit Integer(rowid)
        //   - cursor id never opened            → emit Null
        //
        // The hot-path arm is a verbatim copy of the main-match arm
        // (`val = self.cursor_rowid(cursor_id)?; set_reg_fast(p2, val)`),
        // so any divergence between the two would surface here.
        //
        // Layout note: the bytecode verifier requires Rewind p2 to be
        // strictly `< op_count`, so the Rewind EOF target must point at
        // a real instruction (we use the Halt at the program end via a
        // label resolved *at* that Halt).  Init is omitted entirely:
        // the engine starts execution at pc=0 unconditionally and no
        // verifier path requires Init's presence.
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(7, vec![SqliteValue::Integer(70)]);
        table.insert(11, vec![SqliteValue::Integer(110)]);

        let (rows, _) = run_write_with_storage_cursors(db, |b| {
            let halt = b.emit_label();

            // Probe a never-opened cursor id first → expects Null.  This
            // exercises the `cursor_rowid` fallthrough where neither
            // `storage_cursors` nor `vtab_cursors` nor `cursors` has a
            // matching id.
            let r_unopened = b.alloc_reg();
            b.emit_op(Opcode::Rowid, 99, r_unopened, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_unopened, 1, 0, P4::None, 0);

            // Open writable cursor 0 and Rewind onto rowid=7.  EOF
            // jumps to Halt directly.
            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, halt, P4::None, 0);

            // Body: Rowid → ResultRow → Next loops until EOF, then
            // falls through to Halt.
            let body = b.current_addr();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Rowid, 0, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            let next_target =
                i32::try_from(body).expect("program counter should fit into i32 for tests");
            b.emit_op(Opcode::Next, 0, next_target, 0, P4::None, 0);

            b.resolve_label(halt);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        });

        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Null],
                vec![SqliteValue::Integer(7)],
                vec![SqliteValue::Integer(11)],
            ]
        );
    }

    // ── IdxRowid opcode test ─────────────────────────────────────

    #[test]
    fn test_idx_rowid_via_hot_path() {
        // Pins the hot-path arm's behaviour to the main-match arm.
        // IdxRowid shares the `cursor_rowid` helper with Rowid, so the
        // hot-path arm is byte-equivalent to the main-match arm; this
        // test exercises both input classes that helper handles:
        //   - storage cursor positioned on a row → emit Integer(rowid)
        //   - cursor id never opened            → emit Null
        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(13, vec![SqliteValue::Integer(130)]);
        table.insert(17, vec![SqliteValue::Integer(170)]);

        let (rows, _) = run_write_with_storage_cursors(db, |b| {
            let halt = b.emit_label();

            // Probe a never-opened cursor id first → expects Null.
            let r_unopened = b.alloc_reg();
            b.emit_op(Opcode::IdxRowid, 99, r_unopened, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_unopened, 1, 0, P4::None, 0);

            b.emit_op(Opcode::OpenWrite, 0, root, 0, P4::Int(1), 0);
            b.emit_jump_to_label(Opcode::Rewind, 0, 0, halt, P4::None, 0);

            let body = b.current_addr();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::IdxRowid, 0, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            let next_target =
                i32::try_from(body).expect("program counter should fit into i32 for tests");
            b.emit_op(Opcode::Next, 0, next_target, 0, P4::None, 0);

            b.resolve_label(halt);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        });

        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Null],
                vec![SqliteValue::Integer(13)],
                vec![SqliteValue::Integer(17)],
            ]
        );
    }

    // ── IfNotZero opcode test ────────────────────────────────────

    #[test]
    fn test_if_not_zero_decrements_and_jumps() {
        // Start with 2 in register, loop with IfNotZero until it reaches 0.
        let rows = run_program(|b| {
            let end = b.emit_label();
            let loop_start = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_counter = b.alloc_reg();
            let r_count = b.alloc_reg();

            b.emit_op(Opcode::Integer, 3, r_counter, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 0, r_count, 0, P4::None, 0);

            b.resolve_label(loop_start);
            // Count iterations
            b.emit_op(Opcode::AddImm, r_count, 1, 0, P4::None, 0);
            // Decrement and jump if not zero
            b.emit_jump_to_label(Opcode::IfNotZero, r_counter, 0, loop_start, P4::None, 0);

            // When counter reaches 0, output iteration count
            b.emit_op(Opcode::ResultRow, r_count, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        // Loop: AddImm then IfNotZero.
        // iter 1: count=1, 3→2 jump; iter 2: count=2, 2→1 jump;
        // iter 3: count=3, 1→0 jump; iter 4: count=4, 0→fall through.
        assert_eq!(rows, vec![vec![SqliteValue::Integer(4)]]);
    }

    // ── Pagecount / MaxPgcnt / JournalMode / IntegrityCk ─────────

    #[test]
    fn test_pagecount_returns_table_count() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Pagecount, 0, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        // No database set → 0 pages
        assert_eq!(rows, vec![vec![SqliteValue::Integer(0)]]);
    }

    #[test]
    fn test_max_pgcnt_returns_large_value() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_out = b.alloc_reg();
            b.emit_op(Opcode::MaxPgcnt, 0, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Integer(1_073_741_823)]]);
    }

    #[test]
    fn test_journal_mode_returns_wal() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_out = b.alloc_reg();
            b.emit_op(Opcode::JournalMode, 0, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Text("wal".into())]]);
    }

    #[test]
    fn test_integrity_ck_returns_ok() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_root = b.alloc_reg();
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Integer, 1, r_root, 0, P4::None, 0);
            b.emit_op(Opcode::IntegrityCk, r_root, r_out, 1, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Text("ok".into())]]);
    }

    // ── Vacuum and IncrVacuum ────────────────────────────────────

    #[test]
    fn test_vacuum_is_noop() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Vacuum, 0, 0, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
    }
    #[derive(Debug, Clone, PartialEq)]
    struct RecordedFilterCall {
        idx_num: i32,
        idx_str: Option<String>,
        args: Vec<SqliteValue>,
    }

    /// Mock virtual table cursor for testing VTable opcodes.
    #[derive(Clone)]
    struct MockVtabCursor {
        rows: Vec<Vec<SqliteValue>>,
        pos: usize,
        filtered: bool,
        filter_capture: Option<Arc<std::sync::Mutex<Option<RecordedFilterCall>>>>,
        cancel_on_filter: bool,
        interrupt_on_filter: bool,
        cancel_on_column: Option<Cx>,
    }

    impl MockVtabCursor {
        fn new(rows: Vec<Vec<SqliteValue>>) -> Self {
            Self {
                rows,
                pos: 0,
                filtered: false,
                filter_capture: None,
                cancel_on_filter: false,
                interrupt_on_filter: false,
                cancel_on_column: None,
            }
        }

        fn with_filter_capture(
            rows: Vec<Vec<SqliteValue>>,
            filter_capture: Arc<std::sync::Mutex<Option<RecordedFilterCall>>>,
        ) -> Self {
            Self {
                rows,
                pos: 0,
                filtered: false,
                filter_capture: Some(filter_capture),
                cancel_on_filter: false,
                interrupt_on_filter: false,
                cancel_on_column: None,
            }
        }

        fn with_filter_child_cancel(rows: Vec<Vec<SqliteValue>>) -> Self {
            Self {
                rows,
                pos: 0,
                filtered: false,
                filter_capture: None,
                cancel_on_filter: true,
                interrupt_on_filter: false,
                cancel_on_column: None,
            }
        }

        fn with_filter_interrupt(rows: Vec<Vec<SqliteValue>>) -> Self {
            Self {
                rows,
                pos: 0,
                filtered: false,
                filter_capture: None,
                cancel_on_filter: false,
                interrupt_on_filter: true,
                cancel_on_column: None,
            }
        }

        fn with_column_cancel(rows: Vec<Vec<SqliteValue>>, cancel_cx: Cx) -> Self {
            Self {
                rows,
                pos: 0,
                filtered: false,
                filter_capture: None,
                cancel_on_filter: false,
                interrupt_on_filter: false,
                cancel_on_column: Some(cancel_cx),
            }
        }
    }

    impl VirtualTableCursor for MockVtabCursor {
        fn filter(
            &mut self,
            cx: &Cx,
            idx_num: i32,
            idx_str: Option<&str>,
            args: &[SqliteValue],
        ) -> Result<()> {
            if self.interrupt_on_filter {
                return Err(FrankenError::Abort);
            }
            if self.cancel_on_filter {
                cx.cancel();
            }
            if let Some(filter_capture) = &self.filter_capture {
                *filter_capture.lock().expect("filter capture lock poisoned") =
                    Some(RecordedFilterCall {
                        idx_num,
                        idx_str: idx_str.map(str::to_owned),
                        args: args.to_vec(),
                    });
            }
            self.pos = 0;
            self.filtered = true;
            Ok(())
        }
        fn next(&mut self, _cx: &Cx) -> Result<()> {
            self.pos += 1;
            Ok(())
        }
        fn eof(&self) -> bool {
            self.pos >= self.rows.len()
        }
        fn column(&self, ctx: &mut ColumnContext, col: i32) -> Result<()> {
            if let Some(cancel_cx) = &self.cancel_on_column {
                cancel_cx.cancel();
            }
            #[allow(clippy::cast_sign_loss)]
            let val = self
                .rows
                .get(self.pos)
                .and_then(|row| row.get(col as usize))
                .cloned()
                .unwrap_or(SqliteValue::Null);
            ctx.set_value(val);
            Ok(())
        }
        fn rowid(&self) -> Result<i64> {
            #[allow(clippy::cast_possible_wrap)]
            Ok(self.pos as i64 + 1)
        }
    }

    struct MockVtab {
        cursor_template: MockVtabCursor,
        cancel_on_begin: bool,
        interrupt_on_begin: bool,
    }

    impl MockVtab {
        fn new(cursor_template: MockVtabCursor) -> Self {
            Self {
                cursor_template,
                cancel_on_begin: false,
                interrupt_on_begin: false,
            }
        }

        fn with_begin_child_cancel(cursor_template: MockVtabCursor) -> Self {
            Self {
                cursor_template,
                cancel_on_begin: true,
                interrupt_on_begin: false,
            }
        }

        fn with_begin_interrupt(cursor_template: MockVtabCursor) -> Self {
            Self {
                cursor_template,
                cancel_on_begin: false,
                interrupt_on_begin: true,
            }
        }
    }

    impl VirtualTable for MockVtab {
        type Cursor = MockVtabCursor;

        fn connect(_cx: &Cx, _args: &[&str]) -> Result<Self> {
            Ok(Self::new(MockVtabCursor::new(Vec::new())))
        }

        fn best_index(&self, _info: &mut IndexInfo) -> Result<()> {
            Ok(())
        }

        fn open(&self) -> Result<Self::Cursor> {
            Ok(self.cursor_template.clone())
        }

        fn begin(&mut self, cx: &Cx) -> Result<()> {
            if self.interrupt_on_begin {
                return Err(FrankenError::Abort);
            }
            if self.cancel_on_begin {
                cx.cancel();
            }
            Ok(())
        }
    }

    /// Helper: build a program, register a vtab instance, then execute.
    fn run_vtab_program(
        cursor_id: i32,
        cursor: MockVtabCursor,
        build: impl FnOnce(&mut ProgramBuilder),
    ) -> (Vec<Vec<SqliteValue>>, ExecOutcome) {
        let mut b = ProgramBuilder::new();
        build(&mut b);
        let prog = b.finish().expect("program should build");
        let mut engine = VdbeEngine::new(prog.register_count());
        engine.register_vtab_instance(cursor_id, Box::new(MockVtab::new(cursor)));
        let outcome = engine.execute(&prog).expect("execution should succeed");
        (
            engine
                .take_results()
                .into_iter()
                .map(|v| v.into_vec())
                .collect(),
            outcome,
        )
    }

    fn emit_vfilter_noargs(b: &mut ProgramBuilder, cursor_id: i32, jump_label: Label) {
        let r_idx_num = b.alloc_reg();
        let r_argc = b.alloc_reg();
        debug_assert_eq!(r_argc, r_idx_num + 1);
        b.emit_op(Opcode::Integer, 0, r_idx_num, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 0, r_argc, 0, P4::None, 0);
        b.emit_jump_to_label(
            Opcode::VFilter,
            cursor_id,
            r_idx_num,
            jump_label,
            P4::None,
            0,
        );
    }

    #[test]
    fn test_vopen_is_noop() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::VOpen, 0, 0, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 42, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows, vec![vec![SqliteValue::Integer(42)]]);
    }

    #[test]
    fn test_vcreate_is_noop() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_out = b.alloc_reg();
            b.emit_op(
                Opcode::VCreate,
                0,
                0,
                0,
                P4::Str("test_module".to_owned()),
                0,
            );
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
    }

    #[test]
    fn test_vdestroy_is_noop() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_out = b.alloc_reg();
            b.emit_op(
                Opcode::VDestroy,
                0,
                0,
                0,
                P4::Str("test_table".to_owned()),
                0,
            );
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
    }
    #[test]
    fn test_vcheck_stores_null_in_p2_without_clobbering_p3_argument() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_out = b.alloc_reg();
            let r_arg = b.alloc_reg();
            b.emit_op(Opcode::Integer, 99, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 123, r_arg, 0, P4::None, 0);
            b.emit_op(Opcode::VCheck, 0, r_out, r_arg, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 2, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(
            rows,
            vec![vec![SqliteValue::Null, SqliteValue::Integer(123)]]
        );
    }

    #[test]
    fn test_vupdate_stores_null_in_dest() {
        // VUpdate P1=cursor, P2=n_args, P3=first_arg_reg, P5=dest_reg.
        // When no vtab instance is registered for the cursor, VUpdate
        // writes Null into dest_reg (P5).
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_arg = b.alloc_reg();
            let r_dest = b.alloc_reg();
            b.emit_op(Opcode::Integer, 77, r_arg, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 99, r_dest, 0, P4::None, 0);
            // P1=cursor 0, P2=1 arg, P3=r_arg, P5=r_dest as dest
            #[allow(clippy::cast_possible_truncation)]
            b.emit_op(Opcode::VUpdate, 0, 1, r_arg, P4::None, r_dest as u16);
            b.emit_op(Opcode::ResultRow, r_dest, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows, vec![vec![SqliteValue::Null]]);
    }

    #[test]
    fn test_vinitin_copies_register() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_src = b.alloc_reg();
            let r_dst = b.alloc_reg();
            b.emit_op(Opcode::Integer, 123, r_src, 0, P4::None, 0);
            b.emit_op(Opcode::VInitIn, 0, r_src, r_dst, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_dst, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows, vec![vec![SqliteValue::Integer(123)]]);
    }

    #[test]
    fn test_vfilter_jumps_on_empty_cursor() {
        let empty_cursor = MockVtabCursor::new(vec![]);
        let (rows, outcome) = run_vtab_program(0, empty_cursor, |b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::VOpen, 0, 0, 0, P4::None, 0);
            let after_scan = b.emit_label();
            emit_vfilter_noargs(b, 0, after_scan);
            b.emit_op(Opcode::Integer, 999, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.resolve_label(after_scan);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(rows, vec![vec![SqliteValue::Integer(0)]]);
    }

    #[test]
    fn test_vfilter_jumps_when_cursor_is_missing() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_out = b.alloc_reg();
            let after_scan = b.emit_label();
            emit_vfilter_noargs(b, 99, after_scan);
            b.emit_op(Opcode::Integer, 999, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.resolve_label(after_scan);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows, vec![vec![SqliteValue::Integer(0)]]);
    }

    #[test]
    fn test_vfilter_reads_idx_num_idx_str_and_args_from_registers() {
        let capture = Arc::new(std::sync::Mutex::new(None));
        let cursor = MockVtabCursor::with_filter_capture(
            vec![vec![SqliteValue::Integer(1)]],
            Arc::clone(&capture),
        );

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        let r_idx_num = b.alloc_reg();
        let r_argc = b.alloc_reg();
        let r_arg0 = b.alloc_reg();
        let r_arg1 = b.alloc_reg();
        debug_assert_eq!(r_argc, r_idx_num + 1);
        debug_assert_eq!(r_arg0, r_idx_num + 2);
        debug_assert_eq!(r_arg1, r_idx_num + 3);
        b.emit_op(Opcode::VOpen, 0, 0, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 7, r_idx_num, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 2, r_argc, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 42, r_arg0, 0, P4::None, 0);
        b.emit_op(
            Opcode::String8,
            0,
            r_arg1,
            0,
            P4::Str("needle".to_owned()),
            0,
        );
        b.emit_jump_to_label(
            Opcode::VFilter,
            0,
            r_idx_num,
            done,
            P4::Str("range:eq".to_owned()),
            0,
        );
        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        let prog = b.finish().expect("program should build");

        let mut engine = VdbeEngine::new(prog.register_count());
        engine.register_vtab_instance(0, Box::new(MockVtab::new(cursor)));
        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(
            capture
                .lock()
                .expect("filter capture lock poisoned")
                .clone(),
            Some(RecordedFilterCall {
                idx_num: 7,
                idx_str: Some("range:eq".to_owned()),
                args: vec![SqliteValue::Integer(42), SqliteValue::Text("needle".into()),],
            })
        );
    }

    #[test]
    fn test_execute_observes_execution_cx_cancellation_immediately_after_vfilter_opcode() {
        let root_cx = Cx::new();

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        let r_out = b.alloc_reg();
        b.emit_op(Opcode::VOpen, 0, 0, 0, P4::None, 0);
        emit_vfilter_noargs(&mut b, 0, done);
        b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        let prog = b.finish().expect("program should build");

        let mut engine =
            VdbeEngine::new_with_execution_cx(prog.register_count(), &root_cx, PageSize::DEFAULT);
        let vtab = MockVtab::new(MockVtabCursor::with_filter_child_cancel(vec![vec![
            SqliteValue::Integer(7),
        ]]));
        engine.register_vtab_instance(0, Box::new(vtab));

        let err = engine
            .execute(&prog)
            .expect_err("cancellation should be observed before VFilter advances execution");
        assert!(matches!(err, FrankenError::Abort));
        assert!(engine.take_results().is_empty());
    }

    #[test]
    fn test_vfilter_propagates_interrupt_from_cursor() {
        let root_cx = Cx::new();

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        let r_out = b.alloc_reg();
        b.emit_op(Opcode::VOpen, 0, 0, 0, P4::None, 0);
        emit_vfilter_noargs(&mut b, 0, done);
        b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        let prog = b.finish().expect("program should build");

        let mut engine =
            VdbeEngine::new_with_execution_cx(prog.register_count(), &root_cx, PageSize::DEFAULT);
        let vtab = MockVtab::new(MockVtabCursor::with_filter_interrupt(vec![vec![
            SqliteValue::Integer(7),
        ]]));
        engine.register_vtab_instance(0, Box::new(vtab));

        let err = engine
            .execute(&prog)
            .expect_err("VFilter interrupt should propagate without being wrapped");
        assert!(matches!(err, FrankenError::Abort));
        assert!(engine.take_results().is_empty());
    }

    #[test]
    fn test_vfilter_vcolumn_vnext_scan_loop() {
        let cursor = MockVtabCursor::new(vec![
            vec![SqliteValue::Integer(10), SqliteValue::Text("a".into())],
            vec![SqliteValue::Integer(20), SqliteValue::Text("b".into())],
        ]);
        let (rows, outcome) = run_vtab_program(0, cursor, |b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r1 = b.alloc_reg();
            let r2 = b.alloc_reg();
            let done_label = b.emit_label();
            let loop_label = b.emit_label();
            b.emit_op(Opcode::VOpen, 0, 0, 0, P4::None, 0);
            emit_vfilter_noargs(b, 0, done_label);
            b.resolve_label(loop_label);
            b.emit_op(Opcode::VColumn, 0, 0, r1, P4::None, 0);
            b.emit_op(Opcode::VColumn, 0, 1, r2, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r1, 2, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::VNext, 0, 0, loop_label, P4::None, 0);
            b.resolve_label(done_label);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(
            rows,
            vec![
                vec![SqliteValue::Integer(10), SqliteValue::Text("a".into())],
                vec![SqliteValue::Integer(20), SqliteValue::Text("b".into())],
            ]
        );
    }

    #[test]
    fn test_vnext_no_cursor_falls_through() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_out = b.alloc_reg();
            let loop_label = b.emit_label();
            b.resolve_label(loop_label);
            b.emit_jump_to_label(Opcode::VNext, 99, 0, loop_label, P4::None, 0);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
    }

    #[test]
    fn test_vbegin_no_instance_is_noop() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::VBegin, 0, 0, 0, P4::None, 0);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
    }

    #[test]
    fn test_vbegin_observes_child_cx_cancellation_immediately() {
        let root_cx = Cx::new();

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        let r_out = b.alloc_reg();
        b.emit_op(Opcode::VBegin, 0, 0, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        let prog = b.finish().expect("program should build");

        let mut engine =
            VdbeEngine::new_with_execution_cx(prog.register_count(), &root_cx, PageSize::DEFAULT);
        let vtab = MockVtab::with_begin_child_cancel(MockVtabCursor::new(Vec::new()));
        engine.register_vtab_instance(0, Box::new(vtab));

        let err = engine
            .execute(&prog)
            .expect_err("VBegin child cancellation should abort execution immediately");
        assert!(matches!(err, FrankenError::Abort));
        assert!(engine.take_results().is_empty());
    }

    #[test]
    fn test_vbegin_propagates_interrupt_from_vtab() {
        let root_cx = Cx::new();

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        let r_out = b.alloc_reg();
        b.emit_op(Opcode::VBegin, 0, 0, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        let prog = b.finish().expect("program should build");

        let mut engine =
            VdbeEngine::new_with_execution_cx(prog.register_count(), &root_cx, PageSize::DEFAULT);
        let vtab = MockVtab::with_begin_interrupt(MockVtabCursor::new(Vec::new()));
        engine.register_vtab_instance(0, Box::new(vtab));

        let err = engine
            .execute(&prog)
            .expect_err("VBegin interrupt should propagate without being wrapped");
        assert!(matches!(err, FrankenError::Abort));
        assert!(engine.take_results().is_empty());
    }

    #[test]
    fn test_vrename_no_instance_is_noop() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::VRename, 0, 0, 0, P4::Str("new_name".to_owned()), 0);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
    }

    #[test]
    fn test_vcolumn_no_cursor_stores_null() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::Integer, 55, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::VColumn, 99, 0, r_out, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows, vec![vec![SqliteValue::Null]]);
    }

    #[test]
    fn test_rowid_reads_from_vtab_cursor() {
        let cursor = MockVtabCursor::new(vec![vec![SqliteValue::Integer(10)]]);
        let (rows, outcome) = run_vtab_program(0, cursor, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::VOpen, 0, 0, 0, P4::None, 0);
            emit_vfilter_noargs(b, 0, done);
            b.emit_op(Opcode::Rowid, 0, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
    }

    #[test]
    fn test_rowid_on_vtab_cursor_at_eof_stores_null() {
        let cursor = MockVtabCursor::new(vec![vec![SqliteValue::Integer(10)]]);
        let (rows, outcome) = run_vtab_program(0, cursor, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::VOpen, 0, 0, 0, P4::None, 0);
            emit_vfilter_noargs(b, 0, done);
            b.emit_jump_to_label(Opcode::VNext, 0, 0, done, P4::None, 0);
            b.emit_op(Opcode::Rowid, 0, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(rows, vec![vec![SqliteValue::Null]]);
    }

    #[test]
    fn test_nullrow_on_vtab_cursor_forces_vcolumn_and_rowid_to_null() {
        let cursor = MockVtabCursor::new(vec![vec![SqliteValue::Integer(10)]]);
        let (rows, outcome) = run_vtab_program(0, cursor, |b| {
            let end = b.emit_label();
            let done = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_col = b.alloc_reg();
            let r_rowid = b.alloc_reg();
            b.emit_op(Opcode::VOpen, 0, 0, 0, P4::None, 0);
            emit_vfilter_noargs(b, 0, done);
            b.emit_op(Opcode::NullRow, 0, 0, 0, P4::None, 0);
            b.emit_op(Opcode::VColumn, 0, 0, r_col, P4::None, 0);
            b.emit_op(Opcode::Rowid, 0, r_rowid, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_col, 2, 0, P4::None, 0);
            b.resolve_label(done);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(rows, vec![vec![SqliteValue::Null, SqliteValue::Null]]);
    }

    #[test]
    fn test_ifnullrow_jumps_for_vtab_nullrow() {
        let cursor = MockVtabCursor::new(vec![vec![SqliteValue::Integer(10)]]);
        let (rows, outcome) = run_vtab_program(0, cursor, |b| {
            let end = b.emit_label();
            let skip = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::VOpen, 0, 0, 0, P4::None, 0);
            emit_vfilter_noargs(b, 0, skip);
            b.emit_op(Opcode::NullRow, 0, 0, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::IfNullRow, 0, 0, skip, P4::None, 0);
            b.emit_op(Opcode::Integer, 999, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.resolve_label(skip);
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(rows, vec![vec![SqliteValue::Integer(0)]]);
    }

    #[test]
    fn test_vnext_on_vtab_nullrow_falls_through() {
        let cursor = MockVtabCursor::new(vec![
            vec![SqliteValue::Integer(10)],
            vec![SqliteValue::Integer(20)],
        ]);
        let (rows, outcome) = run_vtab_program(0, cursor, |b| {
            let end = b.emit_label();
            let loop_body = b.emit_label();
            let after_loop = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
            let r_out = b.alloc_reg();
            b.emit_op(Opcode::VOpen, 0, 0, 0, P4::None, 0);
            emit_vfilter_noargs(b, 0, after_loop);
            b.emit_op(Opcode::NullRow, 0, 0, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::VNext, 0, 0, loop_body, P4::None, 0);
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_jump_to_label(Opcode::Goto, 0, 0, after_loop, P4::None, 0);
            b.resolve_label(loop_body);
            b.emit_op(Opcode::Integer, 999, r_out, 0, P4::None, 0);
            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.resolve_label(after_loop);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(outcome, ExecOutcome::Done);
        assert_eq!(rows, vec![vec![SqliteValue::Integer(1)]]);
    }

    #[test]
    fn test_execute_observes_execution_cx_cancellation_immediately_after_vcolumn_opcode() {
        let root_cx = Cx::new();

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        let done = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        let r_out = b.alloc_reg();
        b.emit_op(Opcode::VOpen, 0, 0, 0, P4::None, 0);
        emit_vfilter_noargs(&mut b, 0, done);
        b.emit_op(Opcode::VColumn, 0, 0, r_out, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
        b.resolve_label(done);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        let prog = b.finish().expect("program should build");

        let mut engine =
            VdbeEngine::new_with_execution_cx(prog.register_count(), &root_cx, PageSize::DEFAULT);
        let vtab = MockVtab::new(MockVtabCursor::with_column_cancel(
            vec![vec![SqliteValue::Integer(7)]],
            root_cx.clone(),
        ));
        engine.register_vtab_instance(0, Box::new(vtab));

        let err = engine
            .execute(&prog)
            .expect_err("cancellation should be observed before VColumn publishes a value");
        assert!(matches!(err, FrankenError::Abort));
        assert!(engine.take_results().is_empty());
    }

    // ── Time-travel (SetSnapshot) tests ──────────────────────────────────

    #[test]
    fn test_set_snapshot_stores_time_travel_marker() {
        // Verify that SetSnapshot stores a TimeTravelMarker on the engine
        // AND upgrades the cursor when VersionStore, CommitLog, and GC
        // horizon are all provided (marker-only without infrastructure is
        // no longer supported after the empty-VersionStore hardening).
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(1, vec![SqliteValue::Integer(42)]);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);
        // SetSnapshot with commit sequence 5 on cursor 0.
        b.emit_op(Opcode::SetSnapshot, 0, 0, 0, P4::TimeTravelCommitSeq(5), 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        let prog = b.finish().expect("program should build");

        let vs = Arc::new(VersionStore::new(fsqlite_types::PageSize::DEFAULT));

        // Build a CommitLog with entries so SetSnapshot validation passes.
        let commit_log = {
            use fsqlite_types::TxnId;
            let mut log = CommitLog::new(CommitSeq::new(1));
            for seq in 1..=5 {
                log.append(fsqlite_mvcc::core_types::CommitRecord {
                    txn_id: TxnId::new(seq).unwrap(),
                    commit_seq: CommitSeq::new(seq),
                    pages: smallvec::smallvec![PageNumber::new(1).unwrap()],
                    timestamp_unix_ns: 1_700_000_000_000_000_000 + seq * 1_000_000_000,
                });
            }
            Arc::new(Mutex::new(log))
        };

        let mut engine = VdbeEngine::new(prog.register_count());
        engine.set_database(db);
        engine.set_transaction(txn);
        engine.set_version_store(Arc::clone(&vs));
        engine.set_time_travel_commit_log(Arc::clone(&commit_log));
        engine.set_time_travel_gc_horizon(CommitSeq::new(1));

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);

        // The marker should be recorded.
        let marker = engine.time_travel_marker(0);
        assert!(
            marker.is_some(),
            "time-travel marker should be set on cursor 0"
        );
        assert!(
            matches!(marker, Some(TimeTravelMarker::CommitSeq(seq)) if *seq == 5),
            "expected CommitSeq marker"
        );
    }

    #[test]
    fn test_set_snapshot_upgrades_txn_cursor_to_time_travel() {
        // Verify that when a VersionStore is available, SetSnapshot replaces
        // the cursor backend with TimeTravelPageIo.
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let mut db = MemDatabase::new();
        let root = db.create_table(1);
        let table = db.get_table_mut(root).unwrap();
        table.insert(1, vec![SqliteValue::Integer(99)]);

        let mut b = ProgramBuilder::new();
        let end = b.emit_label();
        b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);
        b.emit_op(Opcode::OpenRead, 0, root, 0, P4::Int(1), 0);
        b.emit_op(Opcode::SetSnapshot, 0, 0, 0, P4::TimeTravelCommitSeq(3), 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.resolve_label(end);
        let prog = b.finish().expect("program should build");

        let vs = Arc::new(VersionStore::new(fsqlite_types::PageSize::DEFAULT));

        // Build a CommitLog with entries so SetSnapshot validation passes.
        let commit_log = {
            use fsqlite_types::TxnId;
            let mut log = CommitLog::new(CommitSeq::new(1));
            for seq in 1..=5 {
                log.append(fsqlite_mvcc::core_types::CommitRecord {
                    txn_id: TxnId::new(seq).unwrap(),
                    commit_seq: CommitSeq::new(seq),
                    pages: smallvec::smallvec![PageNumber::new(1).unwrap()],
                    timestamp_unix_ns: 1_700_000_000_000_000_000 + seq * 1_000_000_000,
                });
            }
            Arc::new(Mutex::new(log))
        };

        let mut engine = VdbeEngine::new(prog.register_count());
        engine.set_database(db);
        engine.set_transaction(txn);
        engine.set_version_store(Arc::clone(&vs));
        engine.set_time_travel_commit_log(Arc::clone(&commit_log));
        engine.set_time_travel_gc_horizon(CommitSeq::new(1));

        let outcome = engine.execute(&prog).expect("execution should succeed");
        assert_eq!(outcome, ExecOutcome::Done);

        // Verify the cursor was upgraded to a TimeTravel backend.
        let sc = engine
            .storage_cursors
            .get(&0)
            .expect("cursor 0 should exist");
        assert!(
            sc.cursor.is_time_travel(),
            "cursor should be upgraded to TimeTravel backend"
        );
        // The cursor should be marked read-only.
        assert!(!sc.writable, "time-travel cursor should be read-only");
    }

    #[test]
    fn test_time_travel_page_io_empty_version_store_rejects_read() {
        // Unit test for TimeTravelPageIo: when the VersionStore is empty
        // (page_count == 0), read_page must return an explicit error rather
        // than silently falling through to current data.
        //
        // This tests the TimeTravelPageIo directly without going through
        // the full SetSnapshot opcode path, which has additional
        // requirements (CommitLog, GC horizon).
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();
        let inner_io = SharedTxnPageIo::new(txn);

        let empty_vs = Arc::new(VersionStore::new(fsqlite_types::PageSize::DEFAULT));

        let tt_snapshot =
            TimeTravelSnapshot::new_for_commit_seq(CommitSeq::new(5), SchemaEpoch::new(1));

        let tt_page_io = TimeTravelPageIo {
            inner: inner_io,
            version_store: empty_vs,
            snapshot: tt_snapshot,
        };

        // Attempt to read page 1. The VersionStore is empty, so this
        // should fail with an explicit error.
        let result = tt_page_io.read_page(&cx, PageNumber::new(1).unwrap());

        assert!(
            result.is_err(),
            "reading from TimeTravelPageIo with empty VersionStore \
             should return an error, not silently fall through"
        );

        let err_msg = format!("{}", result.unwrap_err());
        assert!(
            err_msg.contains("historical data not available")
                || err_msg.contains("time-travel")
                || err_msg.contains("version store"),
            "expected error about missing historical data, got: {err_msg}"
        );
    }

    #[test]
    fn test_time_travel_page_io_populated_store_falls_through_for_unchanged_page() {
        // When the VersionStore IS populated (has at least one page
        // version), but a specific page was never versioned, the
        // read_page should fall through to the underlying transaction
        // (the page hasn't changed since the target commit).
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};
        use fsqlite_types::{PageVersion, TxnEpoch, TxnId, TxnToken};

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();
        let inner_io = SharedTxnPageIo::new(txn);

        let vs = Arc::new(VersionStore::new(fsqlite_types::PageSize::DEFAULT));

        // Publish a version for page 1 at commit_seq=3 so the store is
        // not empty.
        let version = PageVersion {
            pgno: PageNumber::new(1).unwrap(),
            commit_seq: CommitSeq::new(3),
            created_by: TxnToken::new(TxnId::new(3).unwrap(), TxnEpoch::new(1)),
            data: PageData::from_vec(vec![0xAA; 4096]),
            prev: None,
        };
        vs.publish(version);

        // Snapshot at commit 5 -- page 1 is versioned, page 2 is not.
        let tt_snapshot =
            TimeTravelSnapshot::new_for_commit_seq(CommitSeq::new(5), SchemaEpoch::new(1));

        let tt_page_io = TimeTravelPageIo {
            inner: inner_io,
            version_store: vs,
            snapshot: tt_snapshot,
        };

        // Reading page 2 (not versioned) should fall through to the
        // underlying transaction, not error. The MockMvccPager's
        // transaction will likely return an error because it has no real
        // pages, but the important thing is it does NOT return the
        // "historical data not available" error -- it falls through.
        let result = tt_page_io.read_page(&cx, PageNumber::new(2).unwrap());

        // The result may be Ok (if the mock provides data) or Err (if
        // the mock doesn't), but it should NOT be the "historical data
        // not available" error.
        if let Err(e) = &result {
            let msg = format!("{e}");
            assert!(
                !msg.contains("historical data not available"),
                "populated VersionStore should fall through for unknown pages, \
                 not return 'historical data not available'"
            );
        }
    }

    #[test]
    fn test_shared_txn_page_io_zero_busy_timeout_preserves_losing_writer_state() {
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};
        use fsqlite_types::Snapshot;

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let snapshot = Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1));
        let contested_page = PageNumber::ONE;
        let page_bytes = vec![0xAB; PageSize::DEFAULT.as_usize()];

        let (holder_session, writer_session, writer_handle) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let holder_session = guard
                .begin_concurrent(snapshot)
                .expect("holder session should register");
            let writer_session = guard
                .begin_concurrent(snapshot)
                .expect("writer session should register");

            let mut holder = guard
                .get_mut(holder_session)
                .expect("holder session must be present");
            concurrent_write_page(
                &mut holder,
                &lock_table,
                holder_session,
                contested_page,
                PageData::from_vec(page_bytes.clone()),
            )
            .expect("holder should acquire the contested page lock");
            let writer_handle = guard
                .handle(writer_session)
                .expect("writer session handle must be present");
            (holder_session, writer_session, writer_handle)
        };

        let mut page_io = SharedTxnPageIo::with_concurrent(
            txn,
            writer_session,
            writer_handle,
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            0,
        );

        let err = page_io
            .write_page(&cx, contested_page, &page_bytes)
            .expect_err("losing writer should time out with SQLITE_BUSY");
        assert!(
            matches!(err, FrankenError::Busy),
            "expected SQLITE_BUSY on timed-out handoff, got {err}"
        );

        let guard = registry
            .lock()
            .unwrap_or_else(std::sync::PoisonError::into_inner);
        {
            let holder = guard
                .get(holder_session)
                .expect("holder session should remain registered");
            assert!(
                holder.write_set().contains_key(&contested_page),
                "winning writer must retain the contested page in its write set"
            );
        }
        {
            let writer = guard
                .get(writer_session)
                .expect("losing writer session should remain registered");
            assert!(
                writer.write_set().is_empty(),
                "timed-out writer must not leak page data into its write set"
            );
            assert!(
                writer.held_locks().is_empty(),
                "timed-out writer must not retain the contested page lock"
            );
        }
        drop(guard);

        assert_eq!(
            lock_table.total_lock_count(),
            1,
            "contested page lock must remain owned only by the winning writer"
        );
    }

    #[test]
    fn test_shared_txn_page_io_owned_lock_skips_busy_snapshot_after_savepoint_rollback() {
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};
        use fsqlite_types::Snapshot;

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Concurrent).unwrap();

        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let snapshot = Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1));
        let target_page = PageNumber::new(2).unwrap();
        let first_bytes = vec![0xAB; PageSize::DEFAULT.as_usize()];
        let second_bytes = vec![0xCD; PageSize::DEFAULT.as_usize()];

        let (session_id, handle) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let session_id = guard
                .begin_concurrent(snapshot)
                .expect("session should register");
            let handle = guard
                .handle(session_id)
                .expect("session handle must be present");
            (session_id, handle)
        };

        let savepoint = {
            let guard = handle.lock();
            fsqlite_mvcc::concurrent_savepoint(&guard, "sp1").unwrap()
        };

        let mut page_io = SharedTxnPageIo::with_concurrent(
            txn,
            session_id,
            Arc::clone(&handle),
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            0,
        );

        page_io
            .write_page(&cx, target_page, &first_bytes)
            .expect("initial concurrent write should succeed");

        {
            let mut guard = handle.lock();
            fsqlite_mvcc::concurrent_rollback_to_savepoint(
                &mut guard,
                &lock_table,
                session_id,
                &savepoint,
            )
            .unwrap();
            assert!(
                guard.holds_page_lock(target_page),
                "rollback-to-savepoint must preserve page ownership"
            );
            assert!(
                !guard.tracks_write_conflict_page(target_page),
                "rollback-to-savepoint should clear staged tracking for the rolled-back page"
            );
        }

        commit_index.update(target_page, CommitSeq::new(8));

        page_io
            .write_page(&cx, target_page, &second_bytes)
            .expect("already-owned page should bypass stale-snapshot rejection");

        let read_back = page_io
            .read_page_data(&cx, target_page)
            .expect("pager must keep marker-backed read-your-writes data");
        assert_eq!(
            read_back.as_bytes(),
            second_bytes.as_slice(),
            "rewrite after savepoint rollback should remain readable through the pager"
        );

        let guard = registry
            .lock()
            .unwrap_or_else(std::sync::PoisonError::into_inner);
        let writer = guard
            .get(session_id)
            .expect("writer session should remain registered");
        assert_eq!(
            concurrent_page_read_status(&writer, target_page),
            (false, true),
            "rewrite after savepoint rollback should restage the owned page in the MVCC surface"
        );
        assert!(
            concurrent_read_page(&writer, target_page).is_none(),
            "marker-backed VDBE writes keep payload bytes in the pager, not MVCC"
        );
    }

    #[test]
    fn test_shared_txn_page_io_waits_for_page_lock_release_and_succeeds() {
        use fsqlite_mvcc::concurrent_abort;
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};
        use fsqlite_types::Snapshot;

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let snapshot = Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1));
        let contested_page = PageNumber::ONE;
        let page_bytes = vec![0xCD; PageSize::DEFAULT.as_usize()];

        let (holder_session, writer_session, writer_handle) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let holder_session = guard
                .begin_concurrent(snapshot)
                .expect("holder session should register");
            let writer_session = guard
                .begin_concurrent(snapshot)
                .expect("writer session should register");

            let mut holder = guard
                .get_mut(holder_session)
                .expect("holder session must be present");
            concurrent_write_page(
                &mut holder,
                &lock_table,
                holder_session,
                contested_page,
                PageData::from_vec(page_bytes.clone()),
            )
            .expect("holder should acquire the contested page lock");
            let writer_handle = guard
                .handle(writer_session)
                .expect("writer session handle must be present");
            (holder_session, writer_session, writer_handle)
        };

        let release_registry = Arc::clone(&registry);
        let release_lock_table = Arc::clone(&lock_table);
        let releaser = std::thread::spawn(move || {
            std::thread::sleep(Duration::from_millis(20));
            let guard = release_registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let mut holder = guard
                .get_mut(holder_session)
                .expect("holder session must still be present before release");
            concurrent_abort(&mut holder, &release_lock_table, holder_session);
        });

        let mut page_io = SharedTxnPageIo::with_concurrent(
            txn,
            writer_session,
            writer_handle,
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            250,
        );

        page_io
            .write_page(&cx, contested_page, &page_bytes)
            .expect("writer should wake and acquire the page after holder release");
        releaser
            .join()
            .expect("holder release helper thread must complete cleanly");

        let guard = registry
            .lock()
            .unwrap_or_else(std::sync::PoisonError::into_inner);
        {
            let writer = guard
                .get(writer_session)
                .expect("writer session should remain registered");
            assert!(
                writer.write_set().contains_key(&contested_page),
                "woken writer must stage the contested page in its write set"
            );
            assert!(
                writer.held_locks().contains(&contested_page),
                "woken writer must own the contested page lock after retrying"
            );
        }
        drop(guard);

        assert_eq!(
            lock_table.total_lock_count(),
            1,
            "after wake/retry exactly one writer must hold the contested page lock"
        );
    }

    #[test]
    fn test_shared_txn_page_io_short_concurrent_write_preserves_page_size_on_read() {
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};
        use fsqlite_types::Snapshot;

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Concurrent).unwrap();

        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let snapshot = Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1));

        let (session_id, handle) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let session_id = guard
                .begin_concurrent(snapshot)
                .expect("session should register");
            let handle = guard
                .handle(session_id)
                .expect("session handle must be present");
            (session_id, handle)
        };

        let mut page_io = SharedTxnPageIo::with_concurrent(
            txn,
            session_id,
            handle,
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            0,
        );
        let page_no = PageNumber::new(2).expect("page number must be non-zero");
        let expected = vec![0xA5; 32];

        page_io
            .write_page(&cx, page_no, &expected)
            .expect("short concurrent write should succeed");

        let bytes = page_io
            .read_page(&cx, page_no)
            .expect("read-your-writes should return the normalized page image");
        assert_eq!(
            bytes.len(),
            PageSize::DEFAULT.as_usize(),
            "concurrent read-your-writes must preserve the pager page-size invariant"
        );
        assert_eq!(&bytes[..expected.len()], expected.as_slice());
        assert!(
            bytes[expected.len()..].iter().all(|byte| *byte == 0),
            "concurrent read-your-writes should zero-fill any unwritten tail bytes"
        );
    }

    #[test]
    fn test_shared_txn_page_io_short_concurrent_owned_write_preserves_page_size_on_read() {
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};
        use fsqlite_types::Snapshot;

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Concurrent).unwrap();

        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let snapshot = Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1));

        let (session_id, handle) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let session_id = guard
                .begin_concurrent(snapshot)
                .expect("session should register");
            let handle = guard
                .handle(session_id)
                .expect("session handle must be present");
            (session_id, handle)
        };

        let mut page_io = SharedTxnPageIo::with_concurrent(
            txn,
            session_id,
            handle,
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            0,
        );
        let page_no = PageNumber::new(2).expect("page number must be non-zero");
        let expected = vec![0x5A; 32];

        page_io
            .write_page_data(&cx, page_no, PageData::from_vec(expected.clone()))
            .expect("short concurrent owned write should succeed");

        let bytes = page_io
            .read_page(&cx, page_no)
            .expect("read-your-writes should return the normalized owned page image");
        assert_eq!(
            bytes.len(),
            PageSize::DEFAULT.as_usize(),
            "concurrent owned writes must preserve the pager page-size invariant"
        );
        assert_eq!(&bytes[..expected.len()], expected.as_slice());
        assert!(
            bytes[expected.len()..].iter().all(|byte| *byte == 0),
            "concurrent owned writes should zero-fill any unwritten tail bytes"
        );
    }

    #[test]
    fn test_shared_txn_page_io_try_mutate_staged_page_data_updates_read_your_writes() {
        use std::path::PathBuf;

        use fsqlite_pager::{MvccPager as _, SimplePager, TransactionMode};
        use fsqlite_types::Snapshot;
        use fsqlite_vfs::MemoryVfs;

        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let vfs = MemoryVfs::new();
        let path = PathBuf::from("/shared_txn_page_io_try_mutate_staged_page_data.db");
        let cx = Cx::new();
        let pager = SimplePager::open_with_cx(&cx, vfs, &path, PageSize::MIN).unwrap();
        let txn = pager.begin(&cx, TransactionMode::Concurrent).unwrap();

        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let snapshot = Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1));

        let (session_id, handle) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let session_id = guard
                .begin_concurrent(snapshot)
                .expect("session should register");
            let handle = guard
                .handle(session_id)
                .expect("session handle must be present");
            (session_id, handle)
        };

        let mut page_io = SharedTxnPageIo::with_concurrent(
            txn,
            session_id,
            handle,
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            0,
        );
        let page_no = PageNumber::new(2).expect("page number must be non-zero");
        let mut page = vec![0x11; PageSize::MIN.as_usize()];

        page_io
            .write_page_data(&cx, page_no, PageData::from_vec(page.clone()))
            .expect("initial concurrent owned write should succeed");

        let mut missing_page_closure_called = false;
        assert!(
            !page_io.try_mutate_staged_page_data(
                PageNumber::new(3).expect("page number must be non-zero"),
                &mut |data| {
                    missing_page_closure_called = true;
                    data.as_bytes_mut()[0] = 0xEE;
                },
            ),
            "unstaged pages should not report a successful mutation"
        );
        assert!(
            !missing_page_closure_called,
            "unstaged-page mutation must not invoke the caller closure"
        );

        assert!(
            page_io.try_mutate_staged_page_data(page_no, &mut |data| {
                let bytes = data.as_bytes_mut();
                bytes[0] = 0xA5;
                bytes[PageSize::MIN.as_usize() - 1] = 0x5A;
            }),
            "staged concurrent pages should support in-place pager mutation"
        );
        page[0] = 0xA5;
        page[PageSize::MIN.as_usize() - 1] = 0x5A;

        let read_back = page_io
            .read_page_data(&cx, page_no)
            .expect("read-your-writes should use the mutated pager image");
        assert_eq!(read_back.as_bytes(), page.as_slice());

        let guard = registry
            .lock()
            .unwrap_or_else(std::sync::PoisonError::into_inner);
        let writer = guard
            .get(session_id)
            .expect("writer session should remain registered");
        assert!(
            writer.write_set().contains_key(&page_no),
            "in-place mutation must preserve the concurrent write-set surface"
        );
        assert!(
            writer.held_locks().contains(&page_no),
            "in-place mutation must preserve the page lock"
        );
    }

    #[test]
    fn test_shared_txn_page_io_short_owned_write_tracks_in_place_zero_extend_metrics() {
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};

        let _guard = VDBE_OBSERVABILITY_LOCK
            .lock()
            .unwrap_or_else(|e| e.into_inner());
        let prev_metrics_enabled = vdbe_metrics_enabled();
        reset_vdbe_metrics();
        set_vdbe_metrics_enabled(true);

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();
        let mut page_io = SharedTxnPageIo::new(txn);
        let page_no = PageNumber::new(2).expect("page number must be non-zero");
        let expected = vec![0x6B; 32];

        let before = vdbe_metrics_snapshot();
        page_io
            .write_page_data(&cx, page_no, PageData::from_vec(expected.clone()))
            .expect("short owned write should succeed");
        let after = vdbe_metrics_snapshot();

        let bytes = page_io
            .read_page(&cx, page_no)
            .expect("write should remain readable through the pager");
        assert_eq!(bytes.len(), PageSize::DEFAULT.as_usize());
        assert_eq!(&bytes[..expected.len()], expected.as_slice());
        assert!(
            bytes[expected.len()..].iter().all(|byte| *byte == 0),
            "short owned writes should still zero-fill their unwritten tail"
        );
        assert_eq!(
            after.page_data_motion.owned_write_normalization_calls_total
                - before
                    .page_data_motion
                    .owned_write_normalization_calls_total,
            1
        );
        assert_eq!(
            after.page_data_motion.owned_in_place_zero_extends_total
                - before.page_data_motion.owned_in_place_zero_extends_total,
            1
        );
        assert_eq!(
            after.page_data_motion.owned_resized_copies_total
                - before.page_data_motion.owned_resized_copies_total,
            0
        );
        assert_eq!(
            after.page_data_motion.normalized_payload_bytes_total
                - before.page_data_motion.normalized_payload_bytes_total,
            0
        );
        assert_eq!(
            after.page_data_motion.normalized_zero_fill_bytes_total
                - before.page_data_motion.normalized_zero_fill_bytes_total,
            u64::try_from(PageSize::DEFAULT.as_usize() - expected.len()).unwrap()
        );

        set_vdbe_metrics_enabled(prev_metrics_enabled);
    }

    #[test]
    fn test_shared_txn_page_io_rejects_oversized_write_buffer() {
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();
        let mut page_io = SharedTxnPageIo::new(txn);
        let page_no = PageNumber::new(2).expect("page number must be non-zero");
        let oversized = vec![0xCC; PageSize::DEFAULT.as_usize() + 1];

        let err = page_io
            .write_page(&cx, page_no, &oversized)
            .expect_err("oversized page buffer should be rejected");

        assert!(
            matches!(err, FrankenError::Internal(ref message) if message.contains("page buffer exceeds page size invariant")),
            "unexpected error for oversized page buffer: {err}"
        );
    }

    #[test]
    fn test_shared_txn_page_io_rejects_oversized_owned_page_buffer() {
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();
        let mut page_io = SharedTxnPageIo::new(txn);
        let page_no = PageNumber::new(2).expect("page number must be non-zero");
        let oversized = PageData::from_vec(vec![0xDD; PageSize::DEFAULT.as_usize() + 1]);

        let err = page_io
            .write_page_data(&cx, page_no, oversized)
            .expect_err("oversized owned page buffer should be rejected");

        assert!(
            matches!(err, FrankenError::Internal(ref message) if message.contains("page buffer exceeds page size invariant")),
            "unexpected error for oversized owned page buffer: {err}"
        );
    }

    #[test]
    fn test_shared_txn_page_io_busy_snapshot_restores_page_one_tracking() {
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};
        use fsqlite_types::Snapshot;

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Immediate).unwrap();

        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let snapshot = Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1));
        let target_page = PageNumber::new(2).unwrap();
        let page_bytes = vec![0xEF; PageSize::DEFAULT.as_usize()];

        let (writer_session, writer_handle) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let writer_session = guard
                .begin_concurrent(snapshot)
                .expect("writer session should register");
            let writer_handle = guard
                .handle(writer_session)
                .expect("writer session handle must be present");
            (writer_session, writer_handle)
        };

        commit_index.update(target_page, CommitSeq::new(8));

        let mut page_io = SharedTxnPageIo::with_concurrent(
            txn,
            writer_session,
            writer_handle,
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            250,
        );

        let err = page_io
            .write_page(&cx, target_page, &page_bytes)
            .expect_err("stale snapshot should reject the write");
        assert!(
            matches!(err, FrankenError::BusySnapshot { .. }),
            "expected SQLITE_BUSY_SNAPSHOT on stale page write, got {err}"
        );

        let guard = registry
            .lock()
            .unwrap_or_else(std::sync::PoisonError::into_inner);
        let writer = guard
            .get(writer_session)
            .expect("writer session should remain registered");
        assert!(
            writer.write_set().is_empty(),
            "stale snapshot must not leak staged page bytes into the write set"
        );
        assert!(
            !writer.tracks_write_conflict_page(PageNumber::ONE),
            "failed write must restore the synthetic page-one conflict surface"
        );
        assert!(
            !writer.held_locks().contains(&PageNumber::ONE),
            "failed write must not retain the synthetic page-one lock"
        );
        assert_eq!(
            lock_table.total_lock_count(),
            0,
            "stale snapshot failure should leave the lock table unchanged"
        );
    }

    #[test]
    fn test_shared_txn_page_io_net_zero_growth_clears_synthetic_page_one_tracking() {
        use fsqlite_pager::{MemoryMockMvccPager, MvccPager as _, TransactionMode};
        use fsqlite_types::Snapshot;

        let pager = MemoryMockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Concurrent).unwrap();

        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let snapshot = Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1));

        let (session_id, handle) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let session_id = guard
                .begin_concurrent(snapshot)
                .expect("session should register");
            let handle = guard
                .handle(session_id)
                .expect("session handle must be present");
            (session_id, handle)
        };

        let mut page_io = SharedTxnPageIo::with_concurrent(
            txn,
            session_id,
            Arc::clone(&handle),
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            0,
        );

        let page_no = page_io
            .allocate_page(&cx)
            .expect("allocate_page should succeed for concurrent txn");
        let page_bytes = vec![0x5A; PageSize::DEFAULT.as_usize()];
        page_io
            .write_page(&cx, page_no, &page_bytes)
            .expect("write_page should succeed for allocated page");

        {
            let guard = handle.lock();
            assert!(
                !guard.tracks_write_conflict_page(PageNumber::ONE),
                "successful writes must reconcile synthetic page-one tracking when the pager does not expose page one in pending_commit_pages"
            );
            assert!(
                !guard.held_locks().contains(&PageNumber::ONE),
                "successful writes must release any synthetic page-one lock when reconciliation drops page one from the conflict surface"
            );
        }

        page_io
            .free_page(&cx, page_no)
            .expect("free_page should succeed for net-zero growth");

        let guard = handle.lock();
        assert!(
            !guard.tracks_write_conflict_page(PageNumber::ONE),
            "net-zero growth should drop the synthetic page-one conflict surface"
        );
        assert!(
            !guard.held_locks().contains(&PageNumber::ONE),
            "net-zero growth should release the synthetic page-one lock"
        );
    }

    #[test]
    fn test_shared_txn_page_io_concurrent_growth_write_does_not_block_on_page_one_pretracking() {
        use std::path::PathBuf;

        use fsqlite_pager::{MvccPager as _, SimplePager, TransactionMode};
        use fsqlite_types::Snapshot;
        use fsqlite_vfs::MemoryVfs;

        let vfs = MemoryVfs::new();
        let path = PathBuf::from("/leased_growth_write_skips_page_one_pretracking.db");
        let cx = Cx::new();
        let pager = SimplePager::open_with_cx(&cx, vfs, &path, PageSize::MIN).unwrap();

        let txn = pager.begin(&cx, TransactionMode::Concurrent).unwrap();
        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let snapshot = Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1));

        let (session_id, handle, blocker_session) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let session_id = guard
                .begin_concurrent(snapshot)
                .expect("session should register");
            let handle = guard
                .handle(session_id)
                .expect("session handle must be present");
            let blocker_session = guard
                .begin_concurrent(snapshot)
                .expect("blocker session should register");
            (session_id, handle, blocker_session)
        };

        {
            let guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let mut blocker = guard
                .get_mut(blocker_session)
                .expect("blocker handle should be present");
            concurrent_track_write_conflict_page(
                &mut blocker,
                &lock_table,
                blocker_session,
                PageNumber::ONE,
            )
            .expect("blocker must hold synthetic page-one tracking");
        }

        let mut page_io = SharedTxnPageIo::with_concurrent(
            txn,
            session_id,
            Arc::clone(&handle),
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            0,
        );

        let page_no = page_io
            .allocate_page(&cx)
            .expect("allocate_page should not need page one");
        page_io
            .write_page(&cx, page_no, &vec![0x5A; PageSize::MIN.as_usize()])
            .expect("leased growth write should not block on unrelated page-one tracking");

        let guard = handle.lock();
        assert!(
            guard.tracks_write_conflict_page(page_no),
            "the actual high page must still enter the write-conflict surface"
        );
        assert!(
            !guard.tracks_write_conflict_page(PageNumber::ONE),
            "tier-1 leased growth should not synthesize page-one conflict tracking before commit planning"
        );
    }

    #[test]
    fn test_shared_txn_page_io_high_page_write_skips_conservative_page_one_probe() {
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};
        use fsqlite_types::Snapshot;

        let pager = MockMvccPager;
        let cx = Cx::new();
        let txn = pager.begin(&cx, TransactionMode::Concurrent).unwrap();

        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let snapshot = Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1));
        let target_page = PageNumber::new(2).unwrap();

        let (session_id, handle, blocker_session) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let session_id = guard
                .begin_concurrent(snapshot)
                .expect("session should register");
            let handle = guard
                .handle(session_id)
                .expect("session handle must be present");
            let blocker_session = guard
                .begin_concurrent(snapshot)
                .expect("blocker session should register");
            (session_id, handle, blocker_session)
        };

        {
            let guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let mut blocker = guard
                .get_mut(blocker_session)
                .expect("blocker handle should be present");
            concurrent_track_write_conflict_page(
                &mut blocker,
                &lock_table,
                blocker_session,
                PageNumber::ONE,
            )
            .expect("blocker must hold page-one tracking");
        }

        let mut page_io = SharedTxnPageIo::with_concurrent(
            txn,
            session_id,
            Arc::clone(&handle),
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            0,
        );

        page_io
            .write_page(&cx, target_page, &vec![0x7B; PageSize::DEFAULT.as_usize()])
            .expect("ordinary high-page concurrent writes must not probe page-one tracking");

        let guard = handle.lock();
        assert!(
            guard.tracks_write_conflict_page(target_page),
            "the high page must still enter the write-conflict surface"
        );
        assert!(
            !guard.tracks_write_conflict_page(PageNumber::ONE),
            "high-page writes must not synthesize page-one conflict tracking"
        );
    }

    #[test]
    fn test_shared_txn_page_io_free_does_not_late_acquire_existing_freelist_trunk_pages() {
        use std::path::PathBuf;

        use fsqlite_pager::{MvccPager as _, SimplePager, TransactionMode};
        use fsqlite_types::Snapshot;
        use fsqlite_vfs::MemoryVfs;

        let vfs = MemoryVfs::new();
        let path = PathBuf::from("/late_pending_commit_freelist_trunk.db");
        let cx = Cx::new();
        let pager = SimplePager::open_with_cx(&cx, vfs, &path, PageSize::MIN).unwrap();
        let ps = PageSize::MIN.as_usize();

        let (p2, p3) = {
            let mut seed = pager.begin(&cx, TransactionMode::Immediate).unwrap();
            let p2 = seed.allocate_page(&cx).unwrap();
            let p3 = seed.allocate_page(&cx).unwrap();
            seed.write_page(&cx, p2, &vec![0x11; ps]).unwrap();
            seed.write_page(&cx, p3, &vec![0x22; ps]).unwrap();
            seed.commit(&cx).unwrap();
            (p2, p3)
        };

        {
            let mut establish_committed_freelist =
                pager.begin(&cx, TransactionMode::Immediate).unwrap();
            establish_committed_freelist.free_page(&cx, p2).unwrap();
            establish_committed_freelist.commit(&cx).unwrap();
        }

        {
            let mut preview = pager.begin(&cx, TransactionMode::Concurrent).unwrap();
            preview.free_page(&cx, p3).unwrap();
            let predicted = preview.pending_commit_pages().unwrap();
            assert!(
                predicted.contains(&p2),
                "freeing a durable page should require rewriting the existing freelist trunk page at commit"
            );
            assert!(
                !predicted.contains(&p3),
                "the commit surface can include an existing durable trunk without directly rewriting the newly freed page"
            );
            preview.rollback(&cx).unwrap();
        }

        let txn = pager.begin(&cx, TransactionMode::Concurrent).unwrap();
        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let snapshot = Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1));

        let (session_id, handle, blocker_session) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let session_id = guard
                .begin_concurrent(snapshot)
                .expect("session should register");
            let handle = guard
                .handle(session_id)
                .expect("session handle must be present");
            let blocker_session = guard
                .begin_concurrent(snapshot)
                .expect("blocker session should register");
            (session_id, handle, blocker_session)
        };

        {
            let guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let mut blocker = guard
                .get_mut(blocker_session)
                .expect("blocker handle should be present");
            concurrent_track_write_conflict_page(&mut blocker, &lock_table, blocker_session, p2)
                .expect("blocker must hold the existing freelist trunk page lock");
        }

        let mut page_io = SharedTxnPageIo::with_concurrent(
            txn,
            session_id,
            Arc::clone(&handle),
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            0,
        );

        page_io
            .free_page(&cx, p3)
            .expect("free_page must not fail just because commit-time trunk rewrites will later need an existing freelist page");

        let guard = handle.lock();
        assert!(
            !guard.tracks_write_conflict_page(p2),
            "the per-op path must not late-acquire committed freelist trunk pages after mutating the pager state"
        );
        assert!(
            !guard.held_locks().contains(&p2),
            "the per-op path must not steal the committed trunk page lock before commit planning"
        );
    }

    #[test]
    fn test_shared_txn_page_io_clears_preexisting_synthetic_page_one_tracking_when_unneeded() {
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};

        // Simulate the state after an earlier allocator operation has already
        // installed synthetic page-one tracking, but the current pager surface
        // no longer needs page one in `pending_commit_pages()`.
        let cx = Cx::new();
        let pager = MockMvccPager;
        let txn = pager
            .begin(&cx, TransactionMode::Immediate)
            .expect("transaction should start");

        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let snapshot = Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1));

        let (session_id, handle) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let session_id = guard
                .begin_concurrent(snapshot)
                .expect("session should register");
            let handle = guard
                .handle(session_id)
                .expect("session handle must be present");
            (session_id, handle)
        };

        {
            let mut guard = handle.lock();
            concurrent_track_write_conflict_page(
                &mut guard,
                &lock_table,
                session_id,
                PageNumber::ONE,
            )
            .expect("synthetic page-one tracking should be installed");
        }

        let mut page_io = SharedTxnPageIo::with_concurrent(
            txn,
            session_id,
            Arc::clone(&handle),
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            0,
        );

        {
            let guard = handle.lock();
            assert!(
                guard.tracks_write_conflict_page(PageNumber::ONE),
                "test precondition should start with synthetic page-one tracking already present"
            );
        }

        page_io
            .free_page(
                &cx,
                PageNumber::new(2).expect("page number must be non-zero"),
            )
            .expect("free_page should succeed while pending commit pages stay empty");

        let guard = handle.lock();
        assert!(
            !guard.tracks_write_conflict_page(PageNumber::ONE),
            "reconciliation must clear stale synthetic page-one tracking even when the current op did not introduce it"
        );
        assert!(
            !guard.held_locks().contains(&PageNumber::ONE),
            "reconciliation must release the stale synthetic page-one lock"
        );
    }

    #[test]
    fn test_shared_txn_page_io_wait_is_cancellation_responsive() {
        use fsqlite_pager::{MockMvccPager, MvccPager as _, TransactionMode};
        use fsqlite_types::Snapshot;

        let pager = MockMvccPager;
        let root_cx = Cx::new();
        let write_cx = root_cx.create_child();
        let txn = pager.begin(&root_cx, TransactionMode::Immediate).unwrap();

        let registry = Arc::new(Mutex::new(ConcurrentRegistry::new()));
        let lock_table = Arc::new(InProcessPageLockTable::new());
        let commit_index = Arc::new(CommitIndex::new());
        let snapshot = Snapshot::new(CommitSeq::new(7), SchemaEpoch::new(1));
        let contested_page = PageNumber::ONE;
        let page_bytes = vec![0xAA; PageSize::DEFAULT.as_usize()];

        let (holder_session, writer_session, writer_handle) = {
            let mut guard = registry
                .lock()
                .unwrap_or_else(std::sync::PoisonError::into_inner);
            let holder_session = guard
                .begin_concurrent(snapshot)
                .expect("holder session should register");
            let writer_session = guard
                .begin_concurrent(snapshot)
                .expect("writer session should register");

            let mut holder = guard
                .get_mut(holder_session)
                .expect("holder session must be present");
            concurrent_write_page(
                &mut holder,
                &lock_table,
                holder_session,
                contested_page,
                PageData::from_vec(page_bytes.clone()),
            )
            .expect("holder should acquire the contested page lock");
            let writer_handle = guard
                .handle(writer_session)
                .expect("writer session handle must be present");
            (holder_session, writer_session, writer_handle)
        };

        let cancel_cx = write_cx.clone();
        let cancel_helper = std::thread::spawn(move || {
            std::thread::sleep(Duration::from_millis(20));
            cancel_cx.cancel();
        });

        let mut page_io = SharedTxnPageIo::with_concurrent(
            txn,
            writer_session,
            writer_handle,
            Arc::clone(&lock_table),
            Arc::clone(&commit_index),
            500,
        );

        let started = std::time::Instant::now();
        let err = page_io
            .write_page(&write_cx, contested_page, &page_bytes)
            .expect_err("cancelled waiter should abort before busy timeout");
        cancel_helper
            .join()
            .expect("cancel helper thread must complete cleanly");

        assert!(matches!(err, FrankenError::Abort));
        assert!(
            started.elapsed() < Duration::from_millis(250),
            "cancelled wait should return promptly instead of sleeping until busy_timeout"
        );

        let guard = registry
            .lock()
            .unwrap_or_else(std::sync::PoisonError::into_inner);
        let holder = guard
            .get(holder_session)
            .expect("holder session should remain registered");
        assert!(
            holder.held_locks().contains(&contested_page),
            "cancellation must not disturb the winning writer's held lock"
        );
        let writer = guard
            .get(writer_session)
            .expect("writer session should remain registered");
        assert!(
            writer.write_set().is_empty(),
            "cancelled write must not stage page bytes"
        );
        assert!(
            writer.held_locks().is_empty(),
            "cancelled write must not retain the contested page lock"
        );
    }

    // ── Subtype opcode tests ─────────────────────────────────────────

    #[test]
    fn test_subtype_set_get_clr_roundtrip() {
        // SetSubtype P1=subtype_reg P2=target_reg,
        // GetSubtype P1=target_reg P2=result_reg,
        // ClrSubtype P1=target_reg.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_subtype = b.alloc_reg(); // holds subtype value (74 = 'J')
            let r_target = b.alloc_reg(); // register to tag
            let r_out1 = b.alloc_reg(); // result before SetSubtype
            let r_out2 = b.alloc_reg(); // result after SetSubtype
            let r_out3 = b.alloc_reg(); // result after ClrSubtype

            // Put a value into r_target.
            b.emit_op(
                Opcode::String8,
                0,
                r_target,
                0,
                P4::Str(r#"{"a":1}"#.to_owned()),
                0,
            );

            // GetSubtype before any SetSubtype → 0.
            b.emit_op(Opcode::GetSubtype, r_target, r_out1, 0, P4::None, 0);

            // SetSubtype: tag r_target with subtype 74 ('J' for JSON).
            b.emit_op(Opcode::Integer, 74, r_subtype, 0, P4::None, 0);
            b.emit_op(Opcode::SetSubtype, r_subtype, r_target, 0, P4::None, 0);

            // GetSubtype → should be 74.
            b.emit_op(Opcode::GetSubtype, r_target, r_out2, 0, P4::None, 0);

            // ClrSubtype → clear the tag.
            b.emit_op(Opcode::ClrSubtype, r_target, 0, 0, P4::None, 0);
            b.emit_op(Opcode::GetSubtype, r_target, r_out3, 0, P4::None, 0);

            b.emit_op(Opcode::ResultRow, r_out1, 3, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0][0], SqliteValue::Integer(0)); // before set
        assert_eq!(rows[0][1], SqliteValue::Integer(74)); // after set
        assert_eq!(rows[0][2], SqliteValue::Integer(0)); // after clear
    }

    #[test]
    fn test_subtype_set_zero_clears() {
        // Setting subtype to 0 should effectively clear it.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_st = b.alloc_reg();
            let r_target = b.alloc_reg();
            let r_out = b.alloc_reg();

            b.emit_op(Opcode::Integer, 42, r_target, 0, P4::None, 0);
            // Set subtype to 74.
            b.emit_op(Opcode::Integer, 74, r_st, 0, P4::None, 0);
            b.emit_op(Opcode::SetSubtype, r_st, r_target, 0, P4::None, 0);
            // Set subtype to 0 → should clear.
            b.emit_op(Opcode::Integer, 0, r_st, 0, P4::None, 0);
            b.emit_op(Opcode::SetSubtype, r_st, r_target, 0, P4::None, 0);
            b.emit_op(Opcode::GetSubtype, r_target, r_out, 0, P4::None, 0);

            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0][0], SqliteValue::Integer(0));
    }

    #[test]
    fn test_subtype_is_cleared_when_register_value_is_overwritten() {
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_st = b.alloc_reg();
            let r_target = b.alloc_reg();
            let r_out = b.alloc_reg();

            b.emit_op(
                Opcode::String8,
                0,
                r_target,
                0,
                P4::Str(r#"{"a":1}"#.to_owned()),
                0,
            );
            b.emit_op(Opcode::Integer, 74, r_st, 0, P4::None, 0);
            b.emit_op(Opcode::SetSubtype, r_st, r_target, 0, P4::None, 0);

            // Any subsequent register write replaces the logical value, so the
            // prior JSON subtype must not survive.
            b.emit_op(Opcode::Integer, 42, r_target, 0, P4::None, 0);
            b.emit_op(Opcode::GetSubtype, r_target, r_out, 0, P4::None, 0);

            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });

        assert_eq!(rows, vec![vec![SqliteValue::Integer(0)]]);
    }

    #[test]
    fn test_take_reg_clears_subtype_metadata() {
        let mut engine = VdbeEngine::new(4);
        engine.set_reg(1, SqliteValue::Text("payload".into()));
        engine.set_register_subtype(1, 74);

        assert_eq!(engine.take_reg(1), SqliteValue::Text("payload".into()));
        assert_eq!(engine.get_reg(1), &SqliteValue::Null);
        assert!(
            engine.register_subtype(1).is_none(),
            "moving a register value out must clear any stale subtype metadata"
        );
    }

    #[test]
    fn test_execute_reuse_clears_subtype_metadata() {
        let mut first_builder = ProgramBuilder::new();
        first_builder.emit_op(
            Opcode::String8,
            0,
            1,
            0,
            P4::Str(r#"{"a":1}"#.to_owned()),
            0,
        );
        first_builder.emit_op(Opcode::Integer, 74, 2, 0, P4::None, 0);
        first_builder.emit_op(Opcode::SetSubtype, 2, 1, 0, P4::None, 0);
        first_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let first_program = first_builder.finish().expect("first program should build");

        let mut second_builder = ProgramBuilder::new();
        second_builder.emit_op(Opcode::GetSubtype, 1, 3, 0, P4::None, 0);
        second_builder.emit_op(Opcode::ResultRow, 3, 1, 0, P4::None, 0);
        second_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let second_program = second_builder
            .finish()
            .expect("second program should build");

        let mut engine = VdbeEngine::new(
            first_program
                .register_count()
                .max(second_program.register_count()),
        );
        assert_eq!(
            engine.execute(&first_program).expect("first execution"),
            ExecOutcome::Done
        );
        assert_eq!(
            engine.execute(&second_program).expect("second execution"),
            ExecOutcome::Done
        );

        assert_eq!(
            engine
                .results()
                .iter()
                .map(|row| row.clone().into_vec())
                .collect::<Vec<_>>(),
            vec![vec![SqliteValue::Integer(0)]]
        );
    }

    #[test]
    fn test_simple_statement_does_not_allocate_cold_state_sidecar() {
        let mut builder = ProgramBuilder::new();
        builder.emit_op(Opcode::Integer, 42, 1, 0, P4::None, 0);
        builder.emit_op(Opcode::ResultRow, 1, 1, 0, P4::None, 0);
        builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let program = builder.finish().expect("program should build");

        let mut engine = VdbeEngine::new(program.register_count());
        assert_eq!(
            engine.execute(&program).expect("execution should succeed"),
            ExecOutcome::Done
        );

        assert!(
            !engine.has_cold_state_allocated(),
            "simple non-feature statements should not allocate the cold sidecar"
        );
        assert!(engine.statement_cold_state.is_empty());
    }

    #[test]
    fn test_aggregate_statement_allocates_cold_state_sidecar() {
        let mut builder = ProgramBuilder::new();
        builder.emit_op(Opcode::Integer, 7, 1, 0, P4::None, 0);
        builder.emit_op(Opcode::AggStep, 0, 1, 2, P4::FuncName("sum".to_owned()), 1);
        builder.emit_op(Opcode::AggFinal, 2, 1, 0, P4::FuncName("sum".to_owned()), 0);
        builder.emit_op(Opcode::ResultRow, 2, 1, 0, P4::None, 0);
        builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let program = builder.finish().expect("program should build");

        let mut engine = VdbeEngine::new(program.register_count());
        let mut registry = FunctionRegistry::new();
        register_builtins(&mut registry);
        engine.set_function_registry(Arc::new(registry));

        assert_eq!(
            engine
                .execute(&program)
                .expect("aggregate execution should succeed"),
            ExecOutcome::Done
        );
        assert!(engine.has_cold_state_allocated());
        assert!(
            engine
                .statement_cold_state
                .contains(StatementColdState::AGGREGATES)
        );
        assert_eq!(
            engine
                .results()
                .iter()
                .map(|row| row.clone().into_vec())
                .collect::<Vec<_>>(),
            vec![vec![SqliteValue::Integer(7)]]
        );
    }

    #[test]
    fn test_execute_reuse_drops_cold_state_sidecar() {
        let mut aggregate_builder = ProgramBuilder::new();
        aggregate_builder.emit_op(Opcode::Integer, 3, 1, 0, P4::None, 0);
        aggregate_builder.emit_op(Opcode::AggStep, 0, 1, 2, P4::FuncName("sum".to_owned()), 1);
        aggregate_builder.emit_op(Opcode::AggFinal, 2, 1, 0, P4::FuncName("sum".to_owned()), 0);
        aggregate_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let aggregate_program = aggregate_builder
            .finish()
            .expect("aggregate program should build");

        let mut simple_builder = ProgramBuilder::new();
        simple_builder.emit_op(Opcode::Integer, 1, 1, 0, P4::None, 0);
        simple_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let simple_program = simple_builder
            .finish()
            .expect("simple program should build");

        let mut engine = VdbeEngine::new(
            aggregate_program
                .register_count()
                .max(simple_program.register_count()),
        );
        let mut registry = FunctionRegistry::new();
        register_builtins(&mut registry);
        engine.set_function_registry(Arc::new(registry));

        assert_eq!(
            engine
                .execute(&aggregate_program)
                .expect("aggregate execution should succeed"),
            ExecOutcome::Done
        );
        assert!(engine.has_cold_state_allocated());

        assert_eq!(
            engine
                .execute(&simple_program)
                .expect("simple execution should succeed"),
            ExecOutcome::Done
        );
        assert!(
            !engine.has_cold_state_allocated(),
            "reuse should drop the cold sidecar before the next simple statement"
        );
        assert!(engine.statement_cold_state.is_empty());
    }

    #[test]
    fn test_vdbe_engine_inline_size_stays_bounded() {
        assert!(
            std::mem::size_of::<VdbeEngine>() <= VDBE_ENGINE_INLINE_SIZE_BUDGET_BYTES,
            "VdbeEngine inline size regressed to {} bytes",
            std::mem::size_of::<VdbeEngine>()
        );
    }

    // ── Bloom filter opcode tests ────────────────────────────────────

    #[test]
    fn test_bloom_filter_add_and_test() {
        // FilterAdd adds a hash; Filter should NOT jump (entry present).
        // Filter: jump to P2 if NOT found, fall through if possibly found.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_filter = b.alloc_reg();
            let r_key = b.alloc_reg();
            let r_out = b.alloc_reg();

            // Add key "hello" to filter.
            b.emit_op(Opcode::String8, 0, r_key, 0, P4::Str("hello".to_owned()), 0);
            b.emit_op(Opcode::FilterAdd, r_filter, 0, r_key, P4::None, 0);

            // Test "hello" — should be found (falls through past Filter).
            let not_found = b.emit_label();
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0); // default: not found
            b.emit_jump_to_label(Opcode::Filter, r_filter, r_key, not_found, P4::None, 0);
            // Fell through → found. Overwrite with 1.
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(not_found);

            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0][0], SqliteValue::Integer(1)); // found
    }

    #[test]
    fn test_bloom_filter_miss_jumps() {
        // Test a key NOT in the filter → Filter should jump to P2.
        // Filter: jump to P2 if NOT found, fall through if found.
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_filter = b.alloc_reg();
            let r_key1 = b.alloc_reg();
            let r_key2 = b.alloc_reg();
            let r_out = b.alloc_reg();

            // Add "hello" to filter.
            b.emit_op(
                Opcode::String8,
                0,
                r_key1,
                0,
                P4::Str("hello".to_owned()),
                0,
            );
            b.emit_op(Opcode::FilterAdd, r_filter, 0, r_key1, P4::None, 0);

            // Test "world" — likely NOT found → jumps to not_found.
            b.emit_op(
                Opcode::String8,
                0,
                r_key2,
                0,
                P4::Str("world".to_owned()),
                0,
            );
            let not_found = b.emit_label();
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0); // default: not found
            b.emit_jump_to_label(Opcode::Filter, r_filter, r_key2, not_found, P4::None, 0);
            // Fell through → found (false positive).
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(not_found);

            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        // Due to Bloom filter false positives, we can't assert the exact value.
        assert!(matches!(rows[0][0], SqliteValue::Integer(0 | 1)));
    }

    #[test]
    fn test_bloom_filter_no_filter_falls_through() {
        // Filter with no FilterAdd → no filter exists → conservatively
        // falls through (never skips).
        let rows = run_program(|b| {
            let end = b.emit_label();
            b.emit_jump_to_label(Opcode::Init, 0, 0, end, P4::None, 0);

            let r_filter = b.alloc_reg();
            let r_key = b.alloc_reg();
            let r_out = b.alloc_reg();

            b.emit_op(Opcode::Integer, 42, r_key, 0, P4::None, 0);
            let not_found = b.emit_label();
            b.emit_op(Opcode::Integer, 0, r_out, 0, P4::None, 0); // default: not found
            b.emit_jump_to_label(Opcode::Filter, r_filter, r_key, not_found, P4::None, 0);
            // Fell through → found (or no filter). Set to 1.
            b.emit_op(Opcode::Integer, 1, r_out, 0, P4::None, 0);
            b.resolve_label(not_found);

            b.emit_op(Opcode::ResultRow, r_out, 1, 0, P4::None, 0);
            b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
            b.resolve_label(end);
        });
        assert_eq!(rows[0][0], SqliteValue::Integer(1)); // fell through
    }

    #[test]
    fn test_execute_reuse_clears_bloom_filters() {
        let added_value = SqliteValue::Text("hello".into());
        let missing_value = SqliteValue::Text("world".into());
        let bloom_bits = (BLOOM_FILTER_WORDS * 64) as u64;
        assert_ne!(
            bloom_hash(&added_value) % bloom_bits,
            bloom_hash(&missing_value) % bloom_bits,
            "test fixture must exercise a genuinely missing bloom-filter bit"
        );

        let mut first_builder = ProgramBuilder::new();
        first_builder.emit_op(Opcode::String8, 0, 2, 0, P4::Str("hello".to_owned()), 0);
        first_builder.emit_op(Opcode::FilterAdd, 1, 0, 2, P4::None, 0);
        first_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let first_program = first_builder.finish().expect("first program should build");

        let mut second_builder = ProgramBuilder::new();
        let not_present = second_builder.emit_label();
        let end = second_builder.emit_label();
        second_builder.emit_op(Opcode::String8, 0, 2, 0, P4::Str("world".to_owned()), 0);
        second_builder.emit_jump_to_label(Opcode::Filter, 1, 0, not_present, P4::None, 2);
        second_builder.emit_op(Opcode::Integer, 1, 3, 0, P4::None, 0);
        second_builder.emit_op(Opcode::ResultRow, 3, 1, 0, P4::None, 0);
        second_builder.emit_jump_to_label(Opcode::Goto, 0, 0, end, P4::None, 0);
        second_builder.resolve_label(not_present);
        second_builder.emit_op(Opcode::Integer, 0, 3, 0, P4::None, 0);
        second_builder.emit_op(Opcode::ResultRow, 3, 1, 0, P4::None, 0);
        second_builder.resolve_label(end);
        second_builder.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let second_program = second_builder
            .finish()
            .expect("second program should build");

        let mut engine = VdbeEngine::new(
            first_program
                .register_count()
                .max(second_program.register_count()),
        );
        assert_eq!(
            engine.execute(&first_program).expect("first execution"),
            ExecOutcome::Done
        );
        assert_eq!(
            engine.execute(&second_program).expect("second execution"),
            ExecOutcome::Done
        );

        assert_eq!(
            engine
                .results()
                .iter()
                .map(|row| row.clone().into_vec())
                .collect::<Vec<_>>(),
            vec![vec![SqliteValue::Integer(1)]],
            "second execution should observe no inherited bloom filter"
        );
    }

    // ── IMPL-13: Fused Integer + ResultRow ──────────────────────────────
    //
    // Verifies the `FusedLiteralResultRow` opcode and the peephole pass
    // that rewrites `Integer(lit, reg) + ResultRow(reg, 1)` pairs into the
    // fused form. The test compiles the unfused program, executes it, then
    // compiles+peephole-fuses the same program and byte-compares the
    // emitted result rows.

    fn opcode_fusion_build_unfused_program(literal: i32) -> crate::VdbeProgram {
        let mut b = ProgramBuilder::new();
        let r = b.alloc_reg();
        b.emit_op(Opcode::Integer, literal, r, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        b.finish().expect("unfused program should build")
    }

    fn opcode_fusion_build_fused_program(literal: i32) -> (crate::VdbeProgram, usize) {
        let mut b = ProgramBuilder::new();
        let r = b.alloc_reg();
        b.emit_op(Opcode::Integer, literal, r, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);
        let fused_count = b.apply_fuse_literal_result_row();
        (b.finish().expect("fused program should build"), fused_count)
    }

    #[test]
    fn opcode_fusion_literal_result_row_peephole_rewrites_pair() {
        let (program, fused_count) = opcode_fusion_build_fused_program(42);
        assert_eq!(fused_count, 1, "exactly one fusion site should match");
        let ops = program.ops();
        assert_eq!(
            ops[0].opcode,
            Opcode::FusedLiteralResultRow,
            "first op should be the fused opcode"
        );
        assert_eq!(ops[0].p1, 42);
        assert_eq!(
            ops[1].opcode,
            Opcode::Noop,
            "second op should be Noop to preserve program length"
        );
        assert_eq!(ops[2].opcode, Opcode::Halt);
    }

    #[test]
    fn opcode_fusion_literal_result_row_byte_equivalent_to_unfused() {
        let literal = 42;

        let unfused = opcode_fusion_build_unfused_program(literal);
        let mut eng_unfused = VdbeEngine::new(unfused.register_count());
        assert_eq!(
            eng_unfused.execute(&unfused).expect("unfused exec"),
            ExecOutcome::Done
        );
        let unfused_rows: Vec<Vec<SqliteValue>> = eng_unfused
            .take_results()
            .into_iter()
            .map(|row| row.into_vec())
            .collect();

        let (fused, fused_count) = opcode_fusion_build_fused_program(literal);
        assert_eq!(fused_count, 1);
        let mut eng_fused = VdbeEngine::new(fused.register_count());
        assert_eq!(
            eng_fused.execute(&fused).expect("fused exec"),
            ExecOutcome::Done
        );
        let fused_rows: Vec<Vec<SqliteValue>> = eng_fused
            .take_results()
            .into_iter()
            .map(|row| row.into_vec())
            .collect();

        assert_eq!(
            fused_rows, unfused_rows,
            "fused program must emit byte-equivalent rows"
        );
        assert_eq!(
            fused_rows,
            vec![vec![SqliteValue::Integer(i64::from(literal))]]
        );
    }

    #[test]
    fn opcode_fusion_peephole_skips_multi_column_result_row() {
        // `ResultRow(reg, 2)` emits two columns — the single-column fast
        // path must not fuse here because the following register value
        // participates in the row.
        let mut b = ProgramBuilder::new();
        let r = b.alloc_reg();
        let _r2 = b.alloc_reg();
        b.emit_op(Opcode::Integer, 7, r, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r, 2, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);

        let count = b.apply_fuse_literal_result_row();
        assert_eq!(count, 0, "multi-column ResultRow must not fuse");

        let program = b.finish().expect("program should build");
        assert_eq!(program.ops()[0].opcode, Opcode::Integer);
        assert_eq!(program.ops()[1].opcode, Opcode::ResultRow);
    }

    #[test]
    fn opcode_fusion_peephole_skips_register_mismatch() {
        // Integer writes reg A, ResultRow reads reg B — not a fusion.
        let mut b = ProgramBuilder::new();
        let r_a = b.alloc_reg();
        let r_b = b.alloc_reg();
        b.emit_op(Opcode::Integer, 7, r_a, 0, P4::None, 0);
        b.emit_op(Opcode::Integer, 9, r_b, 0, P4::None, 0);
        b.emit_op(Opcode::ResultRow, r_b, 1, 0, P4::None, 0);
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);

        let count = b.apply_fuse_literal_result_row();
        // The second Integer + ResultRow pair has matching registers and
        // can fuse; the first Integer (writes r_a) cannot.
        assert_eq!(count, 1);

        let program = b.finish().expect("program should build");
        assert_eq!(program.ops()[0].opcode, Opcode::Integer);
        assert_eq!(program.ops()[1].opcode, Opcode::FusedLiteralResultRow);
        assert_eq!(program.ops()[2].opcode, Opcode::Noop);
    }

    #[test]
    fn opcode_fusion_peephole_skips_jump_target_on_result_row() {
        // Goto lands on the ResultRow mid-pair — fusion would change
        // behavior (Noop can't do the ResultRow work), so the peephole
        // must refuse this site.
        let mut b = ProgramBuilder::new();
        let r = b.alloc_reg();
        // 0: Goto -> 2 (the ResultRow address)
        b.emit_op(Opcode::Goto, 0, 2, 0, P4::None, 0);
        // 1: Integer 5 -> r
        b.emit_op(Opcode::Integer, 5, r, 0, P4::None, 0);
        // 2: ResultRow(r, 1)  <-- jump target
        b.emit_op(Opcode::ResultRow, r, 1, 0, P4::None, 0);
        // 3: Halt
        b.emit_op(Opcode::Halt, 0, 0, 0, P4::None, 0);

        let count = b.apply_fuse_literal_result_row();
        assert_eq!(count, 0, "ResultRow that is a jump target must not fuse");
    }
}
