surrealdb-sql 1.1.0

Full type definitions for the SurrealQL query language
Documentation
use super::super::{
	comment::shouldbespace,
	ending,
	error::{expect_tag_no_case, expected},
	omit::omit,
	part::{
		cond, explain, fetch, fields, group, limit, order, split, start, timeout, version, with,
	},
	special::{check_group_by_fields, check_order_by_fields, check_split_on_fields},
	value::selects,
	IResult,
};
use crate::statements::SelectStatement;
use nom::{
	bytes::complete::tag_no_case,
	combinator::{cut, opt, peek},
	sequence::preceded,
};

pub fn select(i: &str) -> IResult<&str, SelectStatement> {
	let (i, _) = tag_no_case("SELECT")(i)?;
	let (i, _) = shouldbespace(i)?;
	let (i, expr) = fields(i)?;
	let (i, omit) = opt(preceded(shouldbespace, omit))(i)?;
	let (i, _) = cut(shouldbespace)(i)?;
	let (i, _) = expect_tag_no_case("FROM")(i)?;
	let (i, only) = opt(preceded(shouldbespace, tag_no_case("ONLY")))(i)?;
	let (i, _) = cut(shouldbespace)(i)?;
	let (i, what) = cut(selects)(i)?;
	let (i, with) = opt(preceded(shouldbespace, with))(i)?;
	let (i, cond) = opt(preceded(shouldbespace, cond))(i)?;
	let (i, split) = opt(preceded(shouldbespace, split))(i)?;
	check_split_on_fields(i, &expr, &split)?;
	let (i, group) = opt(preceded(shouldbespace, group))(i)?;
	check_group_by_fields(i, &expr, &group)?;
	let (i, order) = opt(preceded(shouldbespace, order))(i)?;
	check_order_by_fields(i, &expr, &order)?;

	let (i, (limit, start)) = if let Ok((i, limit)) = preceded(shouldbespace, limit)(i) {
		let (i, start) = opt(preceded(shouldbespace, start))(i)?;
		(i, (Some(limit), start))
	} else if let Ok((i, start)) = preceded(shouldbespace, start)(i) {
		let (i, limit) = opt(preceded(shouldbespace, limit))(i)?;
		(i, (limit, Some(start)))
	} else {
		(i, (None, None))
	};

	let (i, fetch) = opt(preceded(shouldbespace, fetch))(i)?;
	let (i, version) = opt(preceded(shouldbespace, version))(i)?;
	let (i, timeout) = opt(preceded(shouldbespace, timeout))(i)?;
	let (i, parallel) = opt(preceded(shouldbespace, tag_no_case("PARALLEL")))(i)?;
	let (i, explain) = opt(preceded(shouldbespace, explain))(i)?;
	let (i, _) = expected(
		"one of WITH, WHERE, SPLIT, GROUP, ORDER, LIMIT, START, FETCH, VERSION, TIMEOUT, PARALLEL, or EXPLAIN",
		cut(peek(ending::query))
	)(i)?;

	Ok((
		i,
		SelectStatement {
			expr,
			omit,
			only: only.is_some(),
			what,
			with,
			cond,
			split,
			group,
			order,
			limit,
			start,
			fetch,
			version,
			timeout,
			parallel: parallel.is_some(),
			explain,
		},
	))
}

#[cfg(test)]
mod tests {

	use super::*;

	fn assert_parsable(sql: &str) {
		let res = select(sql);
		assert!(res.is_ok());
		let (_, out) = res.unwrap();
		assert_eq!(sql, format!("{}", out))
	}

	#[test]
	fn select_statement_param() {
		assert_parsable("SELECT * FROM $test");
	}

	#[test]
	fn select_statement_table() {
		assert_parsable("SELECT * FROM test");
	}

	#[test]
	fn select_statement_omit() {
		assert_parsable("SELECT * OMIT password FROM test");
	}

	#[test]
	fn select_statement_thing() {
		assert_parsable("SELECT * FROM test:thingy ORDER BY name");
	}

	#[test]
	fn select_statement_clash() {
		assert_parsable("SELECT * FROM order ORDER BY order");
	}

	#[test]
	fn select_statement_limit_select() {
		assert_parsable("SELECT * FROM table LIMIT 3 START 2");
	}

	#[test]
	fn select_statement_limit_select_unordered() {
		let res = select("SELECT * FROM table START 2 LIMIT 1");
		assert!(res.is_ok());
		let (_, out) = res.unwrap();
		assert_eq!("SELECT * FROM table LIMIT 1 START 2", format!("{}", out))
	}

	#[test]
	fn select_statement_table_thing() {
		assert_parsable("SELECT *, ((1 + 3) / 4), 1.3999f AS tester FROM test, test:thingy");
	}

	#[test]
	fn select_with_function() {}
}