icydb-core 0.264.7

IcyDB — A schema-first typed query engine and persistence runtime for Internet Computer canisters
Documentation
use super::*;
use crate::db::{
    cursor::{CursorBoundary, CursorBoundarySlot},
    executor::terminal::page::post_access::apply_load_cursor_and_pagination_window,
    query::plan::{OrderDirection, ResolvedOrder, ResolvedOrderField, ResolvedOrderValueSource},
};

fn kernel_row_u64(value: u64) -> KernelRow {
    let layout = RetainedSlotLayout::compile(1, vec![0]);
    KernelRow::new_slot_only(RetainedSlotRow::from_indexed_values(
        &layout,
        vec![Some(Value::Nat64(value))],
    ))
}

fn direct_field_order(slot: usize) -> ResolvedOrder {
    ResolvedOrder::new(vec![ResolvedOrderField::new(
        ResolvedOrderValueSource::direct_field(slot),
        OrderDirection::Asc,
    )])
}

#[test]
fn retained_slot_layout_preserves_only_nondefault_decode_modes() {
    use RetainedSlotValueMode::{Normal, ScalarOctetLength};

    for modes in [vec![], vec![Normal, Normal]] {
        let layout = RetainedSlotLayout::compile_with_value_modes(4, vec![3, 1], modes);
        assert!(!layout.has_value_mode_overrides());
        assert_eq!(layout.override_value_modes(), None);
        assert_eq!(layout.required_slots(), &[3, 1]);
        assert_eq!(layout.value_index_for_slot(3), Some(0));
        assert_eq!(layout.value_index_for_slot(1), Some(1));
        assert_eq!(layout.value_index_for_slot(0), None);
    }

    let modes = vec![Normal, ScalarOctetLength];
    let layout = RetainedSlotLayout::compile_with_value_modes(4, vec![3, 1], modes.clone());
    assert!(layout.has_value_mode_overrides());
    assert_eq!(layout.override_value_modes(), Some(modes.as_slice()));
    assert_eq!(layout.value_index_for_slot(3), Some(0));
    assert_eq!(layout.value_index_for_slot(1), Some(1));

    let empty = RetainedSlotLayout::compile(0, vec![]);
    assert!(!empty.has_value_mode_overrides());
    assert_eq!(empty.override_value_modes(), None);
}

#[test]
fn retained_slot_row_indexed_layout_uses_shared_slot_lookup() {
    let layout = RetainedSlotLayout::compile(8, vec![1, 3, 5]);
    let mut row = RetainedSlotRow::from_indexed_values(
        &layout,
        vec![
            Some(Value::Text("alpha".to_string())),
            Some(Value::Bool(true)),
            Some(Value::Nat64(7)),
        ],
    );

    assert_eq!(row.slot_ref(5), Some(&Value::Nat64(7)));
    assert_eq!(row.take_slot(1), Some(Value::Text("alpha".to_string())));
    assert_eq!(row.slot_ref(1), None);
    assert_eq!(row.slot_ref(3), Some(&Value::Bool(true)));
    assert_eq!(row.take_slot(5), Some(Value::Nat64(7)));
    assert_eq!(row.take_slot(5), None);
    assert_eq!(row.slot_ref(5), None);
    assert_eq!(row.slot_ref(3), Some(&Value::Bool(true)));
    assert_eq!(row.slot_ref(0), None);
    assert_eq!(row.slot_ref(8), None);
    assert_eq!(row.take_slot(8), None);
}

#[test]
fn load_cursor_and_pagination_window_compacts_in_one_pass() {
    let resolved_order = direct_field_order(0);
    let boundary = CursorBoundary {
        slots: vec![CursorBoundarySlot::Present(Value::Nat64(2))],
    };
    let mut rows = vec![
        kernel_row_u64(1),
        kernel_row_u64(2),
        kernel_row_u64(3),
        kernel_row_u64(4),
        kernel_row_u64(5),
    ];

    let rows_after_cursor = apply_load_cursor_and_pagination_window(
        &mut rows,
        Some((&resolved_order, &boundary)),
        1,
        Some(2),
    )
    .expect("valid cursor boundary should apply");

    assert_eq!(rows_after_cursor, 3);
    assert_eq!(
        rows.into_iter().map(|row| row.slot(0)).collect::<Vec<_>>(),
        vec![Some(Value::Nat64(4)), Some(Value::Nat64(5))]
    );
}

#[test]
fn load_pagination_window_without_cursor_skips_offset_then_limits() {
    let mut rows = vec![
        kernel_row_u64(10),
        kernel_row_u64(20),
        kernel_row_u64(30),
        kernel_row_u64(40),
    ];

    let rows_after_cursor = apply_load_cursor_and_pagination_window(&mut rows, None, 2, Some(1))
        .expect("pagination without cursor should apply");

    assert_eq!(rows_after_cursor, 4);
    assert_eq!(
        rows.into_iter().map(|row| row.slot(0)).collect::<Vec<_>>(),
        vec![Some(Value::Nat64(30))]
    );
}