reifydb-engine 0.9.0

Query execution and processing engine for ReifyDB
Documentation
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2026 ReifyDB

use reifydb_test_harness::engine::TestEngine;
use reifydb_value::value::{Value, frame::frame::Frame};

fn setup() -> TestEngine {
	let t = TestEngine::new();
	t.admin("CREATE NAMESPACE test");
	t.admin("CREATE TABLE test::nums { id: int4, v: int4 }");
	t.command(
		r#"INSERT test::nums [
			{ id: 1, v: 0 },
			{ id: 2, v: 3 },
			{ id: 3, v: 5 },
			{ id: 4, v: 7 },
			{ id: 5, v: 10 }
		]"#,
	);
	t
}

fn bools(frames: &[Frame]) -> Vec<Option<bool>> {
	let frame = &frames[0];
	let out_col = frame.columns.iter().rev().next().unwrap();
	(0..out_col.data.len())
		.map(|i| match out_col.data.get_value(i) {
			Value::Boolean(b) => Some(b),
			Value::None {
				..
			} => None,
			other => panic!("expected Boolean, got {:?}", other),
		})
		.collect()
}

fn strings(frames: &[Frame]) -> Vec<String> {
	let frame = &frames[0];
	let out_col = frame.columns.iter().rev().next().unwrap();
	(0..out_col.data.len())
		.map(|i| match out_col.data.get_value(i) {
			Value::Utf8(s) => s,
			other => panic!("expected Utf8, got {:?}", other),
		})
		.collect()
}

fn ints(frames: &[Frame]) -> Vec<i64> {
	let frame = &frames[0];
	let out_col = frame.columns.iter().rev().next().unwrap();
	(0..out_col.data.len())
		.map(|i| match out_col.data.get_value(i) {
			Value::Int1(v) => v as i64,
			Value::Int2(v) => v as i64,
			Value::Int4(v) => v as i64,
			Value::Int8(v) => v,
			Value::Int16(v) => v as i64,
			other => panic!("expected integer, got {:?}", other),
		})
		.collect()
}

#[test]
fn test_batch_udf_logic_and() {
	let t = setup();
	let frames = t.query(r#"
			UDF in_range ($x: int) { RETURN $x > 2 AND $x < 8 };
			FROM test::nums MAP { id, r: in_range(v) } SORT { id: ASC }
		"#);
	assert_eq!(bools(&frames), vec![Some(false), Some(true), Some(true), Some(true), Some(false)]);
}

#[test]
fn test_batch_udf_logic_or_xor() {
	let t = setup();
	let frames = t.query(r#"
			UDF or_check ($x: int) { RETURN $x == 0 OR $x == 10 };
			FROM test::nums MAP { id, r: or_check(v) } SORT { id: ASC }
		"#);
	assert_eq!(bools(&frames), vec![Some(true), Some(false), Some(false), Some(false), Some(true)]);

	let frames = t.query(r#"
			UDF xor_check ($x: int) { RETURN ($x > 2) XOR ($x > 5) };
			FROM test::nums MAP { id, r: xor_check(v) } SORT { id: ASC }
		"#);
	assert_eq!(bools(&frames), vec![Some(false), Some(true), Some(true), Some(false), Some(false)]);
}

#[test]
fn test_batch_udf_between() {
	let t = setup();
	let frames = t.query(r#"
			UDF in_range ($x: int) { RETURN $x BETWEEN 3 AND 7 };
			FROM test::nums MAP { id, r: in_range(v) } SORT { id: ASC }
		"#);
	assert_eq!(bools(&frames), vec![Some(false), Some(true), Some(true), Some(true), Some(false)]);
}

#[test]
fn test_batch_udf_in_list() {
	let t = setup();
	let frames = t.query(r#"
			UDF is_one_of ($x: int) { RETURN $x IN [0, 5, 10] };
			FROM test::nums MAP { id, r: is_one_of(v) } SORT { id: ASC }
		"#);
	assert_eq!(bools(&frames), vec![Some(true), Some(false), Some(true), Some(false), Some(true)]);

	let frames = t.query(r#"
			UDF not_in ($x: int) { RETURN $x NOT IN [0, 10] };
			FROM test::nums MAP { id, r: not_in(v) } SORT { id: ASC }
		"#);
	assert_eq!(bools(&frames), vec![Some(false), Some(true), Some(true), Some(true), Some(false)]);
}

#[test]
fn test_batch_udf_cast() {
	let t = setup();
	// utf8 is the only target every integer round-trips through; CAST to boolean accepts
	// literal 0 or 1 only.
	let frames = t.query(r#"
			UDF as_utf8 ($x: int) { RETURN CAST($x, utf8) };
			FROM test::nums MAP { id, r: as_utf8(v) } SORT { id: ASC }
		"#);
	assert_eq!(strings(&frames), vec!["0", "3", "5", "7", "10"]);
}

#[test]
fn test_batch_udf_calls_vectorizable_udf() {
	let t = setup();
	// The inner body is arithmetic only, so the outer's batch path must dispatch Call into the
	// columnar user-function path rather than falling back.
	let frames = t.query(r#"
			UDF helper ($y: int) { RETURN $y * 2 };
			UDF outer ($x: int) { RETURN helper($x) + 1 };
			FROM test::nums MAP { id, r: outer(v) } SORT { id: ASC }
		"#);
	assert_eq!(ints(&frames), vec![1, 7, 11, 15, 21]);
}

#[test]
fn test_batch_udf_calls_non_vectorizable_udf() {
	let t = setup();
	// BREAK is not vectorizable, so the columnar Call path must fall back to per-row scalar
	// execution instead of producing a wrong batch answer.
	let frames = t.query(r#"
			UDF helper ($x: int) : int2 {
				LET $i = 0;
				WHILE $i < 100 {
					IF $i >= $x { BREAK };
					$i = $i + 1
				};
				RETURN $i
			};
			UDF outer ($x: int) : int2 { RETURN helper($x) };
			FROM test::nums MAP { id, r: outer(v) } SORT { id: ASC }
		"#);
	assert_eq!(ints(&frames), vec![0, 3, 5, 7, 10]);
}

#[test]
fn test_batch_udf_if_branches() {
	let t = setup();
	let frames = t.query(r#"
			UDF classify ($x: int) {
				IF $x > 2 AND $x < 8 {
					RETURN TRUE
				}
				RETURN FALSE
			};
			FROM test::nums MAP { id, r: classify(v) } SORT { id: ASC }
		"#);
	assert_eq!(bools(&frames), vec![Some(false), Some(true), Some(true), Some(true), Some(false)]);
}

#[test]
fn test_batch_udf_if_else_if_else_chain() {
	let t = setup();
	let frames = t.query(r#"
			UDF classify ($x: int) : int2 {
				IF $x < 3 {
					RETURN 1
				}
				ELSE IF $x < 8 {
					RETURN 2
				}
				ELSE {
					RETURN 3
				}
			};
			FROM test::nums MAP { id, r: classify(v) } SORT { id: ASC }
		"#);
	assert_eq!(ints(&frames), vec![1, 2, 2, 2, 3]);
}

#[test]
fn test_batch_udf_match_returns_without_else() {
	let t = setup();
	let frames = t.query(r#"
			UDF classify ($x: int) : int2 {
				MATCH {
					$x < 3 => { RETURN 1 },
					$x < 8 => { RETURN 2 }
				}
				RETURN 3
			};
			FROM test::nums MAP { id, r: classify(v) } SORT { id: ASC }
		"#);
	// A row matching no arm must fall through to the trailing RETURN, not fault.
	assert_eq!(ints(&frames), vec![1, 2, 2, 2, 3]);
}

#[test]
fn test_batch_udf_match_value_without_else_is_none() {
	let t = setup();
	let frames = t.query(r#"
			UDF flag ($x: int) {
				RETURN MATCH {
					$x < 3 => TRUE,
					$x < 8 => FALSE
				}
			};
			FROM test::nums MAP { id, r: flag(v) } SORT { id: ASC }
		"#);
	// A MATCH expression with no matching arm yields none, not a fault or a default.
	assert_eq!(bools(&frames), vec![Some(true), Some(false), Some(false), Some(false), None]);
}

#[test]
fn test_batch_udf_nested_if_returns() {
	let t = setup();
	let frames = t.query(r#"
			UDF classify ($x: int) : int2 {
				IF $x < 3 {
					RETURN 1
				}
				ELSE IF $x < 8 {
					IF $x == 5 {
						RETURN 50
					}
					ELSE {
						RETURN 2
					}
				}
				ELSE {
					RETURN 3
				}
			};
			FROM test::nums MAP { id, r: classify(v) } SORT { id: ASC }
		"#);
	assert_eq!(ints(&frames), vec![1, 2, 50, 2, 3]);
}

#[test]
fn test_batch_udf_return_inside_while() {
	let t = setup();
	let frames = t.query(r#"
			UDF first_at_least ($x: int) : int2 {
				LET $i = 0;
				WHILE $i < 10 {
					IF $i >= $x {
						RETURN $i
					}
					ELSE {
						$i = $i + 1
					}
				};
				RETURN 0 - 1
			};
			FROM test::nums MAP { id, r: first_at_least(v) } SORT { id: ASC }
		"#);
	// 10 never satisfies the guard inside the bounded loop, so it must reach the fallthrough.
	assert_eq!(ints(&frames), vec![0, 3, 5, 7, -1]);
}

#[test]
fn test_batch_udf_if_else_chain_single_line() {
	let t = setup();
	let frames = t.query(r#"
			UDF classify ($x: int) : int2 {
				IF $x < 3 { RETURN 1 } ELSE IF $x < 8 { RETURN 2 } ELSE { RETURN 3 }
			};
			FROM test::nums MAP { id, r: classify(v) } SORT { id: ASC }
		"#);
	assert_eq!(ints(&frames), vec![1, 2, 2, 2, 3]);
}