icydb-core 0.264.2

IcyDB — A schema-first typed query engine and persistence runtime for Internet Computer canisters
Documentation
use crate::{
    db::{
        QueryError, RequestExecutionRoot,
        executor::budget::{HardExecutionBudget, HardExecutionFailureHeadroom},
        query::{
            builder::scalar_projection::{
                render_scalar_projection_expr_plan_label, write_scalar_projection_expr_plan_label,
            },
            plan::expr::Expr,
            preparation::PreparationWork,
        },
    },
    value::{Value, ValueEnum},
};
use icydb_diagnostic_code::{
    DiagnosticExecutionBudgetResource as Resource, DiagnosticExecutionLane as Lane,
    DiagnosticFactTag,
};
use std::fmt;

fn root(limit: u64) -> RequestExecutionRoot {
    RequestExecutionRoot::new_for_tests(
        HardExecutionBudget::uniform_for_tests(
            16_000_000,
            HardExecutionFailureHeadroom::new(500_000_000, 64 * 1024),
        )
        .with_limit_for_tests(Resource::TemporaryBytes, limit),
    )
}

#[test]
fn shared_sink_preserves_label_escaping_and_charges_output_bytes() {
    let expr = Expr::Literal(Value::Text("quote's λ".into()));
    let expected = render_scalar_projection_expr_plan_label(&expr);
    let request = root(16_000_000);
    let actual = PreparationWork::run(&request.scope(), Lane::Diagnostic, |work| {
        work.render_text(|out| write_scalar_projection_expr_plan_label(&expr, out))
    })
    .unwrap();
    assert_eq!(actual, expected);
    assert_eq!(
        request.observed(Resource::PredicateExpressionSteps),
        expected.len() as u64
    );
}

#[test]
fn swallowed_sink_errors_are_sticky_and_cannot_return_partial_text() {
    let request = root(0);
    for expected in [4, 8] {
        let error = PreparationWork::run(&request.scope(), Lane::Diagnostic, |work| {
            work.render_text(|out| {
                let _ = out.write_str("head");
                let _ = out.write_str("ignored tail");
                Ok(())
            })
        })
        .unwrap_err();
        assert!(error.diagnostic_facts().contains(&(
            DiagnosticFactTag::BudgetResource,
            Resource::TemporaryBytes.raw()
        )));
        assert_eq!(request.observed(Resource::TemporaryBytes), expected);
        assert_eq!(
            request.observed(Resource::PredicateExpressionSteps),
            expected
        );
    }
}

#[test]
fn formatter_failure_is_not_a_successful_partial_result() {
    let request = root(16_000_000);
    let error = PreparationWork::run(&request.scope(), Lane::Diagnostic, |work| {
        work.render_text(|out| {
            out.write_str("partial")?;
            Err(fmt::Error)
        })
    })
    .unwrap_err();
    assert_eq!(
        error.diagnostic_code(),
        QueryError::invariant().diagnostic_code()
    );
    assert_eq!(request.observed(Resource::PredicateExpressionSteps), 7);
}

#[test]
fn decimal_label_scratch_uses_current_authority_and_preserves_successful_text() {
    for value in [
        Value::NatBig("18446744073709551616".parse().unwrap()),
        Value::IntBig("-18446744073709551616".parse().unwrap()),
        Value::Enum(ValueEnum::test_payload(
            2,
            3,
            Value::Map(vec![(
                Value::Text("key".into()),
                Value::List(vec![
                    Value::NatBig("18446744073709551616".parse().unwrap()),
                    Value::IntBig("-18446744073709551616".parse().unwrap()),
                ]),
            )]),
        )),
    ] {
        let expr = Expr::Literal(value);
        let expected = render_scalar_projection_expr_plan_label(&expr);
        for lane in [Lane::PublicRead, Lane::TrustedRead, Lane::Diagnostic] {
            let render = |request: &RequestExecutionRoot| {
                PreparationWork::run(&request.scope(), lane, |work| {
                    work.render_text(|out| write_scalar_projection_expr_plan_label(&expr, out))
                })
            };
            let generous = root(16_000_000);
            assert_eq!(render(&generous).unwrap(), expected);
            for resource in [Resource::TemporaryBytes, Resource::PredicateExpressionSteps] {
                let used = generous.observed(resource);
                assert!(used > expected.len() as u64);
                for allowance in [used - 1, used, 2 * used] {
                    let request = RequestExecutionRoot::new_for_tests(
                        HardExecutionBudget::uniform_for_tests(
                            16_000_000,
                            HardExecutionFailureHeadroom::new(500_000_000, 64 * 1024),
                        )
                        .with_limit_for_tests(resource, allowance),
                    );
                    if allowance < used {
                        assert!(render(&request).is_err());
                        continue;
                    }
                    for _ in 0..allowance / used {
                        assert_eq!(render(&request).unwrap(), expected);
                    }
                    assert_eq!(request.observed(resource), allowance);
                    let error = render(&request).unwrap_err();
                    assert!(
                        error
                            .diagnostic_facts()
                            .contains(&(DiagnosticFactTag::BudgetResource, resource.raw(),))
                    );
                    assert_eq!(request.observed(Resource::RowsVisited), 0);
                }
            }
        }
    }
}

#[test]
fn decimal_scratch_rejection_precedes_output_and_cannot_be_swallowed() {
    let expr = Expr::Literal(Value::NatBig("18446744073709551616".parse().unwrap()));
    let request = root(27);
    let error = PreparationWork::run(&request.scope(), Lane::Diagnostic, |work| {
        work.render_text(|out| {
            let _ = write_scalar_projection_expr_plan_label(&expr, out);
            let _ = out.write_str("must not continue");
            Ok(())
        })
    })
    .unwrap_err();
    assert!(error.diagnostic_facts().contains(&(
        DiagnosticFactTag::BudgetResource,
        Resource::TemporaryBytes.raw(),
    )));
    assert_eq!(request.observed(Resource::TemporaryBytes), 28);
    assert_eq!(request.observed(Resource::PredicateExpressionSteps), 0);
}

#[test]
fn nested_decimal_conversion_rejection_is_sticky_and_cumulative() {
    let value = crate::types::NatBig::from_biguint(num_bigint::BigUint::from(1_u32) << 524_288);
    let expr = Expr::Literal(Value::List(vec![Value::NatBig(value)]));
    let request = RequestExecutionRoot::new_for_tests(
        HardExecutionBudget::uniform_for_tests(
            16_000_000,
            HardExecutionFailureHeadroom::new(500_000_000, 64 * 1024),
        )
        .with_limit_for_tests(Resource::PredicateExpressionSteps, 1024),
    );
    for _ in 0..2 {
        let before = request.observed(Resource::PredicateExpressionSteps);
        let error = PreparationWork::run(&request.scope(), Lane::Diagnostic, |work| {
            work.render_text(|out| {
                let _ = write_scalar_projection_expr_plan_label(&expr, out);
                let _ = out.write_str("must not continue");
                Ok(())
            })
        })
        .unwrap_err();
        assert!(error.diagnostic_facts().contains(&(
            DiagnosticFactTag::BudgetResource,
            Resource::PredicateExpressionSteps.raw(),
        )));
        assert!(request.observed(Resource::PredicateExpressionSteps) > before);
        assert_eq!(request.observed(Resource::RowsVisited), 0);
    }
}