reifydb-rql 0.4.13

ReifyDB Query Language (RQL) parser and AST
Documentation
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2025 ReifyDB

use std::{error::Error, fmt::Write, path::Path};

use reifydb_catalog::catalog::{Catalog, namespace::NamespaceToCreate, table::TableToCreate};
use reifydb_core::interface::catalog::{id::NamespaceId, namespace::Namespace};
use reifydb_engine::test_harness::create_test_admin_transaction;
use reifydb_rql::explain::{
	ast::explain_ast, logical::explain_logical_plan, physical::explain_physical_plan, tokenize::explain_tokenize,
};
use reifydb_testing::testscript::{
	command::Command,
	runner::{Runner, run_path},
};
use reifydb_transaction::transaction::Transaction;
use reifydb_type::fragment::Fragment;
use test_each_file::test_each_path;

test_each_path! { in "crates/rql/tests/scripts/tokenize" as tokenize => run_test }
test_each_path! { in "crates/rql/tests/scripts/ast" as ast => run_test }
test_each_path! { in "crates/rql/tests/scripts/logical_plan" as logical_plan => run_test }
test_each_path! { in "crates/rql/tests/scripts/physical_plan" as physical_plan => run_test }

fn run_test(path: &Path) {
	run_path(&mut TestRunner {}, path).expect("test failed")
}

pub struct TestRunner {}

impl Runner for TestRunner {
	fn run(&mut self, command: &Command) -> Result<String, Box<dyn Error>> {
		let mut output = String::new();
		match command.name.as_str() {
			// token QUERY
			"tokenize" => {
				let mut args = command.consume_args();
				let query = args.next_pos().ok_or("args not given")?.value.as_str();
				args.reject_rest()?;
				let result = explain_tokenize(query).unwrap();
				writeln!(output, "{}", result).unwrap();
			}
			// ast QUERY
			"ast" => {
				let mut args = command.consume_args();
				let query = args.next_pos().ok_or("args not given")?.value.as_str();
				args.reject_rest()?;
				let result = explain_ast(query).unwrap();
				writeln!(output, "{}", result).unwrap();
			}
			// logical QUERY
			"logical" => {
				let mut args = command.consume_args();
				let query = args.next_pos().ok_or("args not given")?.value.as_str();
				args.reject_rest()?;

				let rt = tokio::runtime::Runtime::new().unwrap();
				let result = rt.block_on(async {
					let mut dummy_tx = create_test_admin_transaction();
					let catalog = Catalog::testing();

					let default_namespace = Namespace::default_namespace();

					catalog.create_table(
						&mut dummy_tx,
						TableToCreate {
							name: Fragment::internal("users"),
							namespace: default_namespace.id(),
							columns: vec![],
							retention_strategy: None,
							primary_key_columns: None,
							underlying: false,
						},
					)
					.unwrap();

					catalog.create_table(
						&mut dummy_tx,
						TableToCreate {
							name: Fragment::internal("orders"),
							namespace: default_namespace.id(),
							columns: vec![],
							retention_strategy: None,
							primary_key_columns: None,
							underlying: false,
						},
					)
					.unwrap();

					// Also create test namespace for tests that
					// explicitly use test::users
					let test_ns = catalog
						.create_namespace(
							&mut dummy_tx,
							NamespaceToCreate {
								namespace_fragment: None,
								name: "test".to_string(),
								local_name: "test".to_string(),
								parent_id: NamespaceId::ROOT,
								grpc: None,
								token: None,
							},
						)
						.unwrap();

					catalog.create_table(
						&mut dummy_tx,
						TableToCreate {
							name: Fragment::internal("users"),
							namespace: test_ns.id(),
							columns: vec![],
							retention_strategy: None,
							primary_key_columns: None,
							underlying: false,
						},
					)
					.unwrap();

					explain_logical_plan(&catalog, &mut (&mut dummy_tx).into(), query).unwrap()
				});
				writeln!(output, "{}", result).unwrap();
			}
			// physical QUERY
			"physical" => {
				let mut args = command.consume_args();
				let query = args.next_pos().ok_or("args not given")?.value.as_str();
				args.reject_rest()?;

				let rt = tokio::runtime::Runtime::new().unwrap();
				let result = rt.block_on(async {
					let mut dummy_tx = create_test_admin_transaction();
					let catalog = Catalog::testing();

					let namespace = Namespace::default_namespace();

					catalog.create_table(
						&mut dummy_tx,
						TableToCreate {
							name: Fragment::internal("users"),
							namespace: namespace.id(),
							columns: vec![],
							retention_strategy: None,
							primary_key_columns: None,
							underlying: false,
						},
					)
					.unwrap();

					catalog.create_table(
						&mut dummy_tx,
						TableToCreate {
							name: Fragment::internal("orders"),
							namespace: namespace.id(),
							columns: vec![],
							retention_strategy: None,
							primary_key_columns: None,
							underlying: false,
						},
					)
					.unwrap();

					explain_physical_plan(&catalog, &mut Transaction::Admin(&mut dummy_tx), query)
						.unwrap()
				});
				writeln!(output, "{}", result).unwrap();
			}
			_ => unimplemented!(),
		}
		Ok(output)
	}
}