• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gluesql / gluesql / 27895806754

21 Jun 2026 06:20AM UTC coverage: 98.637% (-0.07%) from 98.711%
27895806754

push

github

web-flow
Convert core and native storages to sync execution (#1928)

GlueSQL's async support has mostly been driven by browser-facing storage constraints such as IndexedDB, rather than by the core execution model itself.

That async boundary ended up spreading through the planner, executor, storage traits, CLI, examples, tests, and native storage implementations. For Rust users and native storages, this added futures/stream plumbing and async dependencies even when the underlying work was synchronous or backed by synchronous APIs.

With the JavaScript and Python packages being split out of this workspace, this PR makes the Rust core synchronous again and keeps browser/package-specific async concerns out of the core storage contract.

2100 of 2147 new or added lines in 141 files covered. (97.81%)

18 existing lines in 9 files now uncovered.

44000 of 44608 relevant lines covered (98.64%)

77908.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.23
/core/src/executor/sort.rs
1
use {
2
    super::{
3
        context::{AggregateValues, RowContext},
4
        evaluate::evaluate,
5
    },
6
    crate::{
7
        ast::{Literal, UnaryOperator},
8
        data::{Key, Row, Value},
9
        plan::{ExprPlan, OrderByExprPlan},
10
        result::{Error, Result},
11
        store::GStore,
12
    },
13
    bigdecimal::ToPrimitive,
14
    serde::Serialize,
15
    std::{borrow::Cow, cmp::Ordering, fmt::Debug, rc::Rc},
16
    thiserror::Error as ThisError,
17
    utils::Vector,
18
};
19

20
#[derive(ThisError, Serialize, Debug, PartialEq, Eq)]
21
pub enum SortError {
22
    #[error("ORDER BY COLUMN_INDEX must be within SELECT-list but: {0}")]
23
    ColumnIndexOutOfRange(usize),
24
    #[error("Unreachable ORDER BY Clause")]
25
    Unreachable,
26
}
27

28
pub struct Sort<'a, T: GStore> {
29
    storage: &'a T,
30
    context: Option<Rc<RowContext<'a>>>,
31
    order_by: &'a [OrderByExprPlan],
32
}
33

34
impl<'a, T: GStore> Sort<'a, T> {
35
    pub fn new(
23,704✔
36
        storage: &'a T,
23,704✔
37
        context: Option<Rc<RowContext<'a>>>,
23,704✔
38
        order_by: &'a [OrderByExprPlan],
23,704✔
39
    ) -> Self {
23,704✔
40
        Self {
23,704✔
41
            storage,
23,704✔
42
            context,
23,704✔
43
            order_by,
23,704✔
44
        }
23,704✔
45
    }
23,704✔
46

47
    pub fn apply(
23,676✔
48
        &self,
23,676✔
49
        rows: impl Iterator<
23,676✔
50
            Item = Result<(Option<Rc<AggregateValues>>, Option<Rc<RowContext<'a>>>, Row)>,
23,676✔
51
        > + 'a,
23,676✔
52
        table_alias: &'a str,
23,676✔
53
    ) -> Result<Box<dyn Iterator<Item = Result<Row>> + 'a>> {
23,676✔
54
        if self.order_by.is_empty() {
23,676✔
55
            return Ok(Box::new(rows.map(|row| row.map(|(.., row)| row))));
43,977✔
56
        }
456✔
57

58
        let rows = rows.collect::<Result<Vec<_>>>()?;
456✔
59
        let mut keyed_rows = Vec::with_capacity(rows.len());
456✔
60
        for (aggregated, next, row) in rows {
1,867✔
61
            enum SortType<'a> {
62
                Value(Value),
63
                Expr(&'a ExprPlan),
64
            }
65

66
            let order_by = self
1,867✔
67
                .order_by
1,867✔
68
                .iter()
1,867✔
69
                .map(|OrderByExprPlan { expr, asc }| -> Result<_> {
2,379✔
70
                    let big_decimal = match expr {
2,379✔
71
                        ExprPlan::Literal(Literal::Number(n)) => Some(n),
84✔
72
                        ExprPlan::UnaryOp {
73
                            op: UnaryOperator::Plus,
74
                            expr,
56✔
75
                        } => match expr.as_ref() {
56✔
76
                            ExprPlan::Literal(Literal::Number(n)) => Some(n),
56✔
UNCOV
77
                            _ => None,
×
78
                        },
79
                        _ => None,
2,239✔
80
                    };
81

82
                    match big_decimal {
2,379✔
83
                        Some(n) => {
140✔
84
                            let index = n
140✔
85
                                .to_usize()
140✔
86
                                .ok_or_else(|| -> Error { SortError::Unreachable.into() })?;
140✔
87
                            let zero_based = index.checked_sub(1).ok_or_else(|| -> Error {
140✔
88
                                SortError::ColumnIndexOutOfRange(index).into()
14✔
89
                            })?;
14✔
90
                            let value = row.values.get(zero_based).ok_or_else(|| -> Error {
126✔
91
                                SortError::ColumnIndexOutOfRange(index).into()
14✔
92
                            })?;
14✔
93

94
                            Ok((SortType::Value(value.clone()), *asc))
112✔
95
                        }
96
                        _ => Ok((SortType::Expr(expr), *asc)),
2,239✔
97
                    }
98
                })
2,379✔
99
                .collect::<Result<Vec<_>>>()?;
1,867✔
100

101
            let filter_context = match (&next, &self.context) {
1,839✔
102
                (Some(next), Some(context)) => Some(Rc::new(RowContext::concat(
84✔
103
                    Rc::clone(next),
84✔
104
                    Rc::clone(context),
84✔
105
                ))),
84✔
106
                (Some(next), None) => Some(Rc::clone(next)),
1,755✔
NEW
107
                (None, Some(context)) => Some(Rc::clone(context)),
×
NEW
108
                (None, None) => None,
×
109
            };
110

111
            let context = RowContext::new(table_alias, Cow::Borrowed(&row), None);
1,839✔
112
            let label_context = Rc::new(context);
1,839✔
113
            let filter_context = match filter_context {
1,839✔
114
                Some(filter_context) => Some(Rc::new(RowContext::concat(
1,839✔
115
                    filter_context,
1,839✔
116
                    Rc::clone(&label_context),
1,839✔
117
                ))),
1,839✔
NEW
118
                None => Some(Rc::clone(&label_context)),
×
119
            };
120

121
            let keys = order_by
1,839✔
122
                .into_iter()
1,839✔
123
                .map(|(sort_type, asc)| {
2,351✔
124
                    match sort_type {
2,351✔
125
                        SortType::Value(value) => Ok(value),
112✔
126
                        SortType::Expr(expr) => evaluate(
2,239✔
127
                            self.storage,
2,239✔
128
                            filter_context.as_ref(),
2,239✔
129
                            aggregated.as_ref(),
2,239✔
130
                            expr,
2,239✔
NEW
131
                        )?
×
132
                        .try_into(),
2,239✔
NEW
133
                    }?
×
134
                    .try_into()
2,351✔
135
                    .map(|key| (key, asc))
2,351✔
136
                })
2,351✔
137
                .collect::<Result<Vec<_>>>()?;
1,839✔
138

139
            keyed_rows.push((keys, row));
1,839✔
140
        }
141

142
        let rows = Vector::from(keyed_rows)
428✔
143
            .sort_by(|(keys_a, ..), (keys_b, ..)| sort_by(keys_a, keys_b))
2,522✔
144
            .into_iter()
428✔
145
            .map(|(.., row)| row)
428✔
146
            .map(Ok);
428✔
147

148
        Ok(Box::new(rows))
428✔
149
    }
23,676✔
150
}
151

152
pub fn sort_by(keys_a: &[(Key, Option<bool>)], keys_b: &[(Key, Option<bool>)]) -> Ordering {
291,928✔
153
    let pairs = keys_a
291,928✔
154
        .iter()
291,928✔
155
        .map(|(a, _)| a)
291,928✔
156
        .zip(keys_b.iter())
291,928✔
157
        .map(|(a, (b, asc))| (a, b, asc.unwrap_or(true)));
291,928✔
158

159
    for (key_a, key_b, asc) in pairs {
291,928✔
160
        match (key_a.cmp(key_b), asc) {
253,071✔
161
            (Ordering::Equal, _) => {}
39,039✔
162
            (ord, true) => return ord,
107,926✔
163
            (ord, false) => return ord.reverse(),
106,106✔
164
        }
165
    }
166

167
    Ordering::Equal
77,896✔
168
}
291,928✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc