nodedb_sql/planner/
sort.rs1use sqlparser::ast::{OrderByOptions, OrderBySort};
10
11use crate::error::{Result, SqlError};
12use crate::resolver::ColumnScope;
13use crate::resolver::columns::TableScope;
14use crate::resolver::expr::convert_expr;
15use crate::types::SortKey;
16
17pub fn convert_sort_keys(
19 exprs: &[sqlparser::ast::OrderByExpr],
20 scope: &TableScope,
21) -> Result<Vec<SortKey>> {
22 let scope = ColumnScope::Relations(scope);
23 exprs
24 .iter()
25 .map(|o| {
26 Ok(SortKey {
27 expr: convert_expr(&o.expr, &scope)?,
28 ascending: sort_ascending(&o.options)?,
29 nulls_first: sort_nulls_first(&o.options)?,
30 })
31 })
32 .collect()
33}
34
35pub fn sort_ascending(options: &OrderByOptions) -> Result<bool> {
38 match &options.sort {
39 None | Some(OrderBySort::Asc) => Ok(true),
40 Some(OrderBySort::Desc) => Ok(false),
41 Some(OrderBySort::Using(operator)) => Err(SqlError::Unsupported {
42 detail: format!("ORDER BY ... USING {operator} is not supported"),
43 }),
44 }
45}
46
47pub fn sort_nulls_first(options: &OrderByOptions) -> Result<bool> {
50 match options.nulls_first {
51 Some(first) => Ok(first),
52 None => Ok(!sort_ascending(options)?),
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use sqlparser::ast::{OrderByKind, Statement};
59
60 use super::*;
61 use crate::parser::statement::parse_sql;
62
63 fn options(key: &str) -> OrderByOptions {
65 let statements = parse_sql(&format!("SELECT a FROM t ORDER BY {key}")).expect("parses");
66 let Statement::Query(query) = &statements[0] else {
67 panic!("expected a query");
68 };
69 let Some(OrderByKind::Expressions(keys)) = query.order_by.as_ref().map(|o| &o.kind) else {
70 panic!("expected ORDER BY expressions");
71 };
72 keys[0].options.clone()
73 }
74
75 #[test]
76 fn nulls_follow_the_direction_unless_stated() {
77 let asc = options("a");
78 assert!(sort_ascending(&asc).expect("ascending"));
79 assert!(!sort_nulls_first(&asc).expect("nulls last"));
80
81 let desc = options("a DESC");
82 assert!(!sort_ascending(&desc).expect("descending"));
83 assert!(sort_nulls_first(&desc).expect("nulls first"));
84
85 let stated = options("a DESC NULLS LAST");
86 assert!(!sort_nulls_first(&stated).expect("nulls last"));
87 }
88
89 #[test]
90 fn using_an_operator_is_refused() {
91 let using = options("a USING <");
92 let err = sort_ascending(&using).expect_err("USING is refused");
93 assert!(matches!(err, SqlError::Unsupported { .. }), "{err}");
94 assert!(sort_nulls_first(&using).is_err());
95 }
96}