use sqlparser::ast;
use crate::error::{Result, SqlError};
use crate::resolver::columns::TableScope;
use crate::types::SortKey;
pub(in crate::planner::select) struct QueryTail<'a> {
pub order_by: Option<&'a ast::OrderBy>,
pub limit_clause: &'a Option<ast::LimitClause>,
pub fetch: Option<&'a ast::Fetch>,
}
impl QueryTail<'_> {
pub(in crate::planner::select) fn sort_keys(&self, scope: &TableScope) -> Result<Vec<SortKey>> {
match self.order_by.map(|o| &o.kind) {
Some(ast::OrderByKind::Expressions(exprs)) => {
crate::planner::sort::convert_sort_keys(exprs, scope)
}
Some(ast::OrderByKind::All(_)) | None => Ok(Vec::new()),
}
}
pub(in crate::planner::select) fn limit_offset(&self) -> Result<(Option<usize>, usize)> {
let (limit_from_clause, offset) = match self.limit_clause {
None => (None, 0),
Some(ast::LimitClause::LimitOffset { limit, offset, .. }) => {
let lv = match limit {
Some(e) => crate::coerce::checked_row_bound("LIMIT", e)?.limit(),
None => None,
};
let ov = match offset {
Some(o) => crate::coerce::checked_row_bound("OFFSET", &o.value)?.offset(),
None => 0,
};
(lv, ov)
}
Some(ast::LimitClause::OffsetCommaLimit { offset, limit }) => {
let lv = crate::coerce::checked_row_bound("LIMIT", limit)?.limit();
let ov = crate::coerce::checked_row_bound("OFFSET", offset)?.offset();
(lv, ov)
}
};
let Some(fetch) = self.fetch else {
return Ok((limit_from_clause, offset));
};
if limit_from_clause.is_some() {
return Err(SqlError::Unsupported {
detail: "a query cannot combine LIMIT with FETCH FIRST/NEXT".into(),
});
}
if fetch.with_ties {
return Err(SqlError::Unsupported {
detail: "FETCH ... WITH TIES is not supported".into(),
});
}
if fetch.percent {
return Err(SqlError::Unsupported {
detail: "FETCH ... PERCENT is not supported".into(),
});
}
let fetch_limit = match &fetch.quantity {
Some(expr) => crate::coerce::checked_row_bound("FETCH FIRST", expr)?.limit(),
None => Some(1),
};
Ok((fetch_limit, offset))
}
}